From f7b7f1ca4d645fcefcca589ca8edd8f8daec2d25 Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Sun, 4 Jul 2021 17:55:52 +0300
Subject: [PATCH 01/12] Add Exact body field for EntityConfig Add Alignment
 field to EntityConfig

+ Migrate to specifying alignment and body in entity assets
+ Make Body required field (with Uninit if you want to specify it later)
---
 .../common/entity/dungeon/fallback/boss.ron   |   3 +-
 .../common/entity/dungeon/fallback/enemy.ron  |   3 +-
 .../entity/dungeon/fallback/miniboss.ron      |   3 +-
 assets/common/entity/dungeon/tier-0/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-0/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-0/miniboss.ron |   3 +-
 assets/common/entity/dungeon/tier-0/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-0/staff.ron |   3 +-
 assets/common/entity/dungeon/tier-1/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-1/bow.ron   |   3 +-
 assets/common/entity/dungeon/tier-1/rat.ron   |   3 +-
 assets/common/entity/dungeon/tier-1/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-1/staff.ron |   3 +-
 assets/common/entity/dungeon/tier-2/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-2/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-2/hakulaq.ron  |   3 +-
 assets/common/entity/dungeon/tier-2/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-2/staff.ron |   3 +-
 .../entity/dungeon/tier-3/bonerattler.ron     |   3 +-
 assets/common/entity/dungeon/tier-3/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-3/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-3/sentry.ron   |   3 +-
 assets/common/entity/dungeon/tier-3/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-3/staff.ron |   3 +-
 assets/common/entity/dungeon/tier-4/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-4/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-4/miniboss.ron |   3 +-
 assets/common/entity/dungeon/tier-4/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-4/staff.ron |   3 +-
 .../entity/dungeon/tier-5/beastmaster.ron     |   3 +-
 assets/common/entity/dungeon/tier-5/boss.ron  |   3 +-
 .../common/entity/dungeon/tier-5/cultist.ron  |   3 +-
 assets/common/entity/dungeon/tier-5/hound.ron |   3 +-
 assets/common/entity/dungeon/tier-5/husk.ron  |   3 +-
 .../entity/dungeon/tier-5/husk_brute.ron      |   3 +-
 .../common/entity/dungeon/tier-5/turret.ron   |   3 +-
 .../common/entity/dungeon/tier-5/warlock.ron  |   3 +-
 .../common/entity/dungeon/tier-5/warlord.ron  |   3 +-
 assets/common/entity/test.ron                 |   6 +-
 assets/common/entity/village/dummy.ron        |  13 ++
 assets/common/entity/village/guard.ron        |   4 +-
 assets/common/entity/village/merchant.ron     |   4 +-
 assets/common/entity/village/villager.ron     |   5 +-
 common/src/comp/agent.rs                      |   3 +-
 common/src/generation.rs                      |  64 +++++----
 world/src/site/settlement/mod.rs              | 136 +++++++++---------
 world/src/site2/plot/dungeon.rs               |  84 +++++------
 47 files changed, 252 insertions(+), 181 deletions(-)
 create mode 100644 assets/common/entity/village/dummy.ron

diff --git a/assets/common/entity/dungeon/fallback/boss.ron b/assets/common/entity/dungeon/fallback/boss.ron
index caf7fc2256..002479e311 100644
--- a/assets/common/entity/dungeon/fallback/boss.ron
+++ b/assets/common/entity/dungeon/fallback/boss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Crazy Sheep"),
-    body: Some(RandomWith("sheep")),
+    body: RandomWith("sheep"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.fallback")),
 
diff --git a/assets/common/entity/dungeon/fallback/enemy.ron b/assets/common/entity/dungeon/fallback/enemy.ron
index 63e9172578..71d2483b36 100644
--- a/assets/common/entity/dungeon/fallback/enemy.ron
+++ b/assets/common/entity/dungeon/fallback/enemy.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Yan Hus"),
-    body: Some(RandomWith("humanoid")),
+    body: RandomWith("humanoid"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.fallback")),
 
diff --git a/assets/common/entity/dungeon/fallback/miniboss.ron b/assets/common/entity/dungeon/fallback/miniboss.ron
index 6f7ba8171e..dfeb34cb24 100644
--- a/assets/common/entity/dungeon/fallback/miniboss.ron
+++ b/assets/common/entity/dungeon/fallback/miniboss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Big Goose"),
-    body: Some(RandomWith("goose")),
+    body: RandomWith("goose"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.fallback")),
 
diff --git a/assets/common/entity/dungeon/tier-0/boss.ron b/assets/common/entity/dungeon/tier-0/boss.ron
index 98483efc4d..04f32f3935 100644
--- a/assets/common/entity/dungeon/tier-0/boss.ron
+++ b/assets/common/entity/dungeon/tier-0/boss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Harvester"),
-    body: Some(RandomWith("harvester")),
+    body: RandomWith("harvester"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-0.boss")),
 
diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron
index be2948eb55..a26c225b74 100644
--- a/assets/common/entity/dungeon/tier-0/bow.ron
+++ b/assets/common/entity/dungeon/tier-0/bow.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Gnarling Stalker"),
-    body: Some(RandomWith("gnarling")),
+    body: RandomWith("gnarling"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-0/miniboss.ron b/assets/common/entity/dungeon/tier-0/miniboss.ron
index 411ef1a7b1..8f091f4065 100644
--- a/assets/common/entity/dungeon/tier-0/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-0/miniboss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Deadwood"),
-    body: Some(RandomWith("deadwood")),
+    body: RandomWith("deadwood"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-0.miniboss")),
 
diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron
index e5a4d9e2ad..df95fc9982 100644
--- a/assets/common/entity/dungeon/tier-0/spear.ron
+++ b/assets/common/entity/dungeon/tier-0/spear.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Gnarling Mugger"),
-    body: Some(RandomWith("gnarling")),
+    body: RandomWith("gnarling"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron
index 7202904324..593e7a38f6 100644
--- a/assets/common/entity/dungeon/tier-0/staff.ron
+++ b/assets/common/entity/dungeon/tier-0/staff.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Gnarling Shaman"),
-    body: Some(RandomWith("gnarling")),
+    body: RandomWith("gnarling"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/tier-1/boss.ron
index 30035d7c3f..3dfcbfca13 100644
--- a/assets/common/entity/dungeon/tier-1/boss.ron
+++ b/assets/common/entity/dungeon/tier-1/boss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Yeti"),
-    body: Some(RandomWith("yeti")),
+    body: RandomWith("yeti"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-1.boss")),
 
diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron
index 013fd7c04c..cf8a1e7d82 100644
--- a/assets/common/entity/dungeon/tier-1/bow.ron
+++ b/assets/common/entity/dungeon/tier-1/bow.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Adlet Tracker"),
-    body: Some(RandomWith("adlet")),
+    body: RandomWith("adlet"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron
index 565057282d..0cfae2fa49 100644
--- a/assets/common/entity/dungeon/tier-1/rat.ron
+++ b/assets/common/entity/dungeon/tier-1/rat.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Rat"),
-    body: Some(RandomWith("rat")),
+    body: RandomWith("rat"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.creature.quad_small.generic")),
 
diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron
index 0ce2a1b28c..7bfd55d539 100644
--- a/assets/common/entity/dungeon/tier-1/spear.ron
+++ b/assets/common/entity/dungeon/tier-1/spear.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Adlet Hunter"),
-    body: Some(RandomWith("adlet")),
+    body: RandomWith("adlet"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron
index d9dc6d2a3e..c0bcc443a2 100644
--- a/assets/common/entity/dungeon/tier-1/staff.ron
+++ b/assets/common/entity/dungeon/tier-1/staff.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Adlet Shaman"),
-    body: Some(RandomWith("adlet")),
+    body: RandomWith("adlet"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-2/boss.ron b/assets/common/entity/dungeon/tier-2/boss.ron
index 4db8d8d5d2..661c35dbd2 100644
--- a/assets/common/entity/dungeon/tier-2/boss.ron
+++ b/assets/common/entity/dungeon/tier-2/boss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Tidal Warrior"),
-    body: Some(RandomWith("tidalwarrior")),
+    body: RandomWith("tidalwarrior"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-2.boss")),
 
diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron
index abd5927b14..5a2803862e 100644
--- a/assets/common/entity/dungeon/tier-2/bow.ron
+++ b/assets/common/entity/dungeon/tier-2/bow.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Sahagin Sniper"),
-    body: Some(RandomWith("sahagin")),
+    body: RandomWith("sahagin"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-2/hakulaq.ron b/assets/common/entity/dungeon/tier-2/hakulaq.ron
index 8f279e3943..0b42e2968b 100644
--- a/assets/common/entity/dungeon/tier-2/hakulaq.ron
+++ b/assets/common/entity/dungeon/tier-2/hakulaq.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Hakulaq"),
-    body: Some(RandomWith("hakulaq")),
+    body: RandomWith("hakulaq"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.creature.quad_low.fanged")),
 
diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron
index bce3699599..43c4d19381 100644
--- a/assets/common/entity/dungeon/tier-2/spear.ron
+++ b/assets/common/entity/dungeon/tier-2/spear.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Sahagin Spearman"),
-    body: Some(RandomWith("sahagin")),
+    body: RandomWith("sahagin"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron
index e41cddb070..889d656483 100644
--- a/assets/common/entity/dungeon/tier-2/staff.ron
+++ b/assets/common/entity/dungeon/tier-2/staff.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Sahagin Sorcerer"),
-    body: Some(RandomWith("sahagin")),
+    body: RandomWith("sahagin"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-3/bonerattler.ron b/assets/common/entity/dungeon/tier-3/bonerattler.ron
index 1fbf9329eb..6d53d539fb 100644
--- a/assets/common/entity/dungeon/tier-3/bonerattler.ron
+++ b/assets/common/entity/dungeon/tier-3/bonerattler.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Bonerattler"),
-    body: Some(RandomWith("bonerattler")),
+    body: RandomWith("bonerattler"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.creature.quad_medium.carapace")),
 
diff --git a/assets/common/entity/dungeon/tier-3/boss.ron b/assets/common/entity/dungeon/tier-3/boss.ron
index cf6ddc11ed..6b86a0ff60 100644
--- a/assets/common/entity/dungeon/tier-3/boss.ron
+++ b/assets/common/entity/dungeon/tier-3/boss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Clay Golem"),
-    body: Some(RandomWith("claygolem")),
+    body: RandomWith("claygolem"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-3.boss")),
 
diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron
index f693a38d03..b5550656ac 100644
--- a/assets/common/entity/dungeon/tier-3/bow.ron
+++ b/assets/common/entity/dungeon/tier-3/bow.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Haniwa Archer"),
-    body: Some(RandomWith("haniwa")),
+    body: RandomWith("haniwa"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-3/sentry.ron b/assets/common/entity/dungeon/tier-3/sentry.ron
index 724f259ee4..90dd422e20 100644
--- a/assets/common/entity/dungeon/tier-3/sentry.ron
+++ b/assets/common/entity/dungeon/tier-3/sentry.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Haniwa Sentry"),
-    body: None,
+    body: Exact(Object(HaniwaSentry)),
+    alignment: Alignment(Enemy),
 
     loot: Some(Item("common.items.crafting_ing.stones")),
 
diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron
index 376d2d67e6..0fa6541dc0 100644
--- a/assets/common/entity/dungeon/tier-3/spear.ron
+++ b/assets/common/entity/dungeon/tier-3/spear.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Haniwa Guard"),
-    body: Some(RandomWith("haniwa")),
+    body: RandomWith("haniwa"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron
index f8d051f2b7..c2450d26d6 100644
--- a/assets/common/entity/dungeon/tier-3/staff.ron
+++ b/assets/common/entity/dungeon/tier-3/staff.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Haniwa Sorcerer"),
-    body: Some(RandomWith("haniwa")),
+    body: RandomWith("haniwa"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-4/boss.ron b/assets/common/entity/dungeon/tier-4/boss.ron
index 5f8883c1dd..756eb9c71e 100644
--- a/assets/common/entity/dungeon/tier-4/boss.ron
+++ b/assets/common/entity/dungeon/tier-4/boss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Minotaur"),
-    body: Some(RandomWith("minotaur")),
+    body: RandomWith("minotaur"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-4.boss")),
 
diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron
index 04ec56dc59..14b7719c2b 100644
--- a/assets/common/entity/dungeon/tier-4/bow.ron
+++ b/assets/common/entity/dungeon/tier-4/bow.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Myrmidon Marksman"),
-    body: Some(RandomWith("myrmidon")),
+    body: RandomWith("myrmidon"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-4/miniboss.ron b/assets/common/entity/dungeon/tier-4/miniboss.ron
index 46ba728ead..30446ac213 100644
--- a/assets/common/entity/dungeon/tier-4/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-4/miniboss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Dullahan"),
-    body: Some(RandomWith("dullahan")),
+    body: RandomWith("dullahan"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-4.miniboss")),
 
diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron
index dffc6440dd..5cb98458c3 100644
--- a/assets/common/entity/dungeon/tier-4/spear.ron
+++ b/assets/common/entity/dungeon/tier-4/spear.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Myrmidon Hoplite"),
-    body: Some(RandomWith("myrmidon")),
+    body: RandomWith("myrmidon"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron
index 8d432875d2..1f26828f6b 100644
--- a/assets/common/entity/dungeon/tier-4/staff.ron
+++ b/assets/common/entity/dungeon/tier-4/staff.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Myrmidon Wizard"),
-    body: Some(RandomWith("myrmidon")),
+    body: RandomWith("myrmidon"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron
index 29987a13e2..4c1abf5318 100644
--- a/assets/common/entity/dungeon/tier-5/beastmaster.ron
+++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Beastmaster"),
-    body: Some(RandomWith("humanoid")),
+    body: RandomWith("humanoid"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.miniboss")),
 
diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron
index 9957ec5482..18557c26a3 100644
--- a/assets/common/entity/dungeon/tier-5/boss.ron
+++ b/assets/common/entity/dungeon/tier-5/boss.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Mindflayer"),
-    body: Some(RandomWith("mindflayer")),
+    body: RandomWith("mindflayer"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.boss")),
 
diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron
index b8e7996861..3b8097244e 100644
--- a/assets/common/entity/dungeon/tier-5/cultist.ron
+++ b/assets/common/entity/dungeon/tier-5/cultist.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Cultist"),
-    body: Some(RandomWith("humanoid")),
+    body: RandomWith("humanoid"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-5/hound.ron b/assets/common/entity/dungeon/tier-5/hound.ron
index 0b78c67746..f8116062fe 100644
--- a/assets/common/entity/dungeon/tier-5/hound.ron
+++ b/assets/common/entity/dungeon/tier-5/hound.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Tamed Darkhound"),
-    body: Some(RandomWith("darkhound")),
+    body: RandomWith("darkhound"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")),
 
diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron
index a28e46db0e..54aba3f90c 100644
--- a/assets/common/entity/dungeon/tier-5/husk.ron
+++ b/assets/common/entity/dungeon/tier-5/husk.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Cultist Husk"),
-    body: Some(RandomWith("husk")),
+    body: RandomWith("husk"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")),
 
diff --git a/assets/common/entity/dungeon/tier-5/husk_brute.ron b/assets/common/entity/dungeon/tier-5/husk_brute.ron
index 40f26815b6..ec39247f96 100644
--- a/assets/common/entity/dungeon/tier-5/husk_brute.ron
+++ b/assets/common/entity/dungeon/tier-5/husk_brute.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Husk Brute"),
-    body: Some(RandomWith("husk_brute")),
+    body: RandomWith("husk_brute"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.miniboss")),
 
diff --git a/assets/common/entity/dungeon/tier-5/turret.ron b/assets/common/entity/dungeon/tier-5/turret.ron
index bb84413d6a..7834953f58 100644
--- a/assets/common/entity/dungeon/tier-5/turret.ron
+++ b/assets/common/entity/dungeon/tier-5/turret.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Possessed Turret"),
-    body: None,
+    body: Exact(Object(Crossbow)),
+    alignment: Alignment(Enemy),
 
     loot: Some(Item("common.items.crafting_ing.twigs")),
 
diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron
index 369bd091cb..53f2ef16bd 100644
--- a/assets/common/entity/dungeon/tier-5/warlock.ron
+++ b/assets/common/entity/dungeon/tier-5/warlock.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Cultist Warlock"),
-    body: Some(RandomWith("cultist_warlock")),
+    body: RandomWith("cultist_warlock"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")),
 
diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron
index fe2152e6e4..be50ee1aa8 100644
--- a/assets/common/entity/dungeon/tier-5/warlord.ron
+++ b/assets/common/entity/dungeon/tier-5/warlord.ron
@@ -1,6 +1,7 @@
 EntityConfig (
     name: Some("Cultist Warlord"),
-    body: Some(RandomWith("cultist_warlord")),
+    body: RandomWith("cultist_warlord"),
+    alignment: Alignment(Enemy),
 
     loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")),
 
diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron
index 20c8ff8fb6..b24d5e713d 100644
--- a/assets/common/entity/test.ron
+++ b/assets/common/entity/test.ron
@@ -5,7 +5,11 @@ EntityConfig (
     /// Body
     /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such)
     /// or RandomWith (will generate random body or species)
-    body: Some(RandomWith("humanoid")),
+    /// or Uninit (means it should be specified somewhere in code)
+    body: RandomWith("humanoid"),
+
+    /// Alignment, can be Uninit
+    alignment: Alignment(Enemy),
 
     /// Main and second tools
     /// Can be Option<Item> (with asset_specifier for item)
diff --git a/assets/common/entity/village/dummy.ron b/assets/common/entity/village/dummy.ron
new file mode 100644
index 0000000000..ca958ede04
--- /dev/null
+++ b/assets/common/entity/village/dummy.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Training Dummy"),
+    body: Exact(Object(TrainingDummy)),
+    alignment: Alignment(Passive),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron
index a80314380c..dc642b2a81 100644
--- a/assets/common/entity/village/guard.ron
+++ b/assets/common/entity/village/guard.ron
@@ -1,7 +1,7 @@
 EntityConfig (
     name: Some("Guard"),
-    // body is specified outsite
-    body: None,
+    body: RandomWith("humanoid"),
+    alignment: Alignment(Npc),
 
     loot: None,
 
diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron
index 7fb1de003d..74491b729e 100644
--- a/assets/common/entity/village/merchant.ron
+++ b/assets/common/entity/village/merchant.ron
@@ -1,7 +1,7 @@
 EntityConfig (
     name: Some("Merchant"),
-    // body is specified outsite
-    body: None,
+    body: RandomWith("humanoid"),
+    alignment: Alignment(Npc),
 
     // considering giving some gold/gems/materials?
     loot: None,
diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron
index 37d6528807..b1bac72a82 100644
--- a/assets/common/entity/village/villager.ron
+++ b/assets/common/entity/village/villager.ron
@@ -1,7 +1,8 @@
 EntityConfig (
+    // name is specified outsite
     name: None,
-    // body is specified outsite
-    body: None,
+    body: RandomWith("humanoid"),
+    alignment: Alignment(Npc),
 
     loot: None,
 
diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs
index 0cb2bb22cb..ba42292175 100644
--- a/common/src/comp/agent.rs
+++ b/common/src/comp/agent.rs
@@ -5,6 +5,7 @@ use crate::{
     trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
     uid::Uid,
 };
+use serde::Deserialize;
 use specs::{Component, Entity as EcsEntity};
 use specs_idvs::IdvStorage;
 use std::{collections::VecDeque, fmt};
@@ -16,7 +17,7 @@ pub const DEFAULT_INTERACTION_TIME: f32 = 1.0;
 pub const TRADE_INTERACTION_TIME: f32 = 300.0;
 pub const MAX_LISTEN_DIST: f32 = 100.0;
 
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq, Deserialize)]
 pub enum Alignment {
     /// Wild animals and gentle giants
     Wild,
diff --git a/common/src/generation.rs b/common/src/generation.rs
index e5a46562c0..8f856553d4 100644
--- a/common/src/generation.rs
+++ b/common/src/generation.rs
@@ -14,8 +14,10 @@ use serde::Deserialize;
 use vek::*;
 
 #[derive(Debug, Deserialize, Clone)]
-enum BodyKind {
+enum BodyBuilder {
     RandomWith(String),
+    Exact(Body),
+    Uninit,
 }
 
 #[derive(Debug, Deserialize, Clone)]
@@ -24,10 +26,17 @@ enum LootKind {
     LootTable(String),
 }
 
+#[derive(Debug, Deserialize, Clone)]
+enum AlignmentMark {
+    Alignment(Alignment),
+    Uninit,
+}
+
 #[derive(Debug, Deserialize, Clone)]
 struct EntityConfig {
     name: Option<String>,
-    body: Option<BodyKind>,
+    body: BodyBuilder,
+    alignment: AlignmentMark,
     loot: Option<LootKind>,
     main_tool: Option<ItemSpec>,
     second_tool: Option<ItemSpec>,
@@ -101,6 +110,7 @@ impl EntityInfo {
         let EntityConfig {
             name,
             body,
+            alignment,
             loot,
             main_tool,
             second_tool,
@@ -112,17 +122,23 @@ impl EntityInfo {
             self = self.with_name(name);
         }
 
-        if let Some(body) = body {
-            match body {
-                BodyKind::RandomWith(string) => {
-                    let npc::NpcBody(_body_kind, mut body_creator) =
-                        string.parse::<npc::NpcBody>().unwrap_or_else(|err| {
-                            panic!("failed to parse body {:?}. Err: {:?}", &string, err)
-                        });
-                    let body = body_creator();
-                    self = self.with_body(body);
-                },
-            }
+        match body {
+            BodyBuilder::RandomWith(string) => {
+                let npc::NpcBody(_body_kind, mut body_creator) =
+                    string.parse::<npc::NpcBody>().unwrap_or_else(|err| {
+                        panic!("failed to parse body {:?}. Err: {:?}", &string, err)
+                    });
+                let body = body_creator();
+                self = self.with_body(body);
+            },
+            BodyBuilder::Exact(body) => {
+                self = self.with_body(body);
+            },
+            BodyBuilder::Uninit => {},
+        }
+
+        if let AlignmentMark::Alignment(alignment) = alignment {
+            self = self.with_alignment(alignment);
         }
 
         if let Some(loot) = loot {
@@ -319,9 +335,10 @@ mod tests {
                 second_tool,
                 loadout_asset,
                 skillset_asset,
-                name: _name,
                 body,
                 loot,
+                name: _name,           // can't fail if serialized, it's a boring String
+                alignment: _alignment, // can't fail if serialized, it's a boring enum
             } = config.clone();
 
             if let Some(main_tool) = main_tool {
@@ -332,16 +349,15 @@ mod tests {
                 second_tool.validate(EquipSlot::ActiveOffhand);
             }
 
-            if let Some(body) = body {
-                match body {
-                    BodyKind::RandomWith(string) => {
-                        let npc::NpcBody(_body_kind, mut body_creator) =
-                            string.parse::<npc::NpcBody>().unwrap_or_else(|err| {
-                                panic!("failed to parse body {:?}. Err: {:?}", &string, err)
-                            });
-                        let _ = body_creator();
-                    },
-                }
+            match body {
+                BodyBuilder::RandomWith(string) => {
+                    let npc::NpcBody(_body_kind, mut body_creator) =
+                        string.parse::<npc::NpcBody>().unwrap_or_else(|err| {
+                            panic!("failed to parse body {:?}. Err: {:?}", &string, err)
+                        });
+                    let _ = body_creator();
+                },
+                BodyBuilder::Exact { .. } | BodyBuilder::Uninit => {},
             }
 
             if let Some(loot) = loot {
diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs
index aba6c5d984..2f18b78b0a 100644
--- a/world/src/site/settlement/mod.rs
+++ b/world/src/site/settlement/mod.rs
@@ -16,13 +16,13 @@ use crate::{
 use common::{
     astar::Astar,
     comp::{
-        self, agent, bird_medium, humanoid,
+        self, agent, bird_medium,
         inventory::{
             loadout_builder::{make_potion_bag, LoadoutBuilder},
             slot::ArmorSlot,
             trade_pricing::TradePricing,
         },
-        object, quadruped_small, Item,
+        quadruped_small, Item,
     },
     generation::{ChunkSupplement, EntityInfo},
     path::Path,
@@ -860,7 +860,6 @@ impl Settlement {
         }
     }
 
-    #[allow(clippy::eval_order_dependence)] // TODO: Pending review in #587
     pub fn apply_supplement<'a>(
         &'a self,
         // NOTE: Used only for dynamic elements like chests and entities!
@@ -904,75 +903,19 @@ impl Settlement {
                 if matches!(sample.plot, Some(Plot::Town { .. }))
                     && RandomField::new(self.seed).chance(Vec3::from(wpos2d), 1.0 / (50.0 * 40.0))
                 {
-                    let is_human: bool;
                     let is_dummy =
                         RandomField::new(self.seed + 1).chance(Vec3::from(wpos2d), 1.0 / 15.0);
-                    let entity = EntityInfo::at(entity_wpos)
-                        .with_body(match dynamic_rng.gen_range(0..5) {
-                            _ if is_dummy => {
-                                is_human = false;
-                                object::Body::TrainingDummy.into()
-                            },
-                            0 => {
-                                let species = match dynamic_rng.gen_range(0..5) {
-                                    0 => quadruped_small::Species::Pig,
-                                    1 => quadruped_small::Species::Sheep,
-                                    2 => quadruped_small::Species::Goat,
-                                    3 => quadruped_small::Species::Dog,
-                                    _ => quadruped_small::Species::Cat,
-                                };
-                                is_human = false;
-                                comp::Body::QuadrupedSmall(quadruped_small::Body::random_with(
-                                    dynamic_rng,
-                                    &species,
-                                ))
-                            },
-                            1 => {
-                                let species = match dynamic_rng.gen_range(0..4) {
-                                    0 => bird_medium::Species::Duck,
-                                    1 => bird_medium::Species::Chicken,
-                                    2 => bird_medium::Species::Goose,
-                                    _ => bird_medium::Species::Peacock,
-                                };
-                                is_human = false;
-                                comp::Body::BirdMedium(bird_medium::Body::random_with(
-                                    dynamic_rng,
-                                    &species,
-                                ))
-                            },
-                            _ => {
-                                is_human = true;
-                                comp::Body::Humanoid(humanoid::Body::random())
-                            },
-                        })
-                        .with_agency(!is_dummy)
-                        .with_alignment(if is_dummy {
-                            comp::Alignment::Passive
-                        } else if is_human {
-                            comp::Alignment::Npc
-                        } else {
-                            comp::Alignment::Tame
-                        })
-                        .do_if(!is_dummy, |e| e.with_automatic_name())
-                        .do_if(is_dummy, |e| e.with_name("Training Dummy"))
-                        .do_if(is_human && dynamic_rng.gen(), |entity| {
-                            match dynamic_rng.gen_range(0..6) {
-                                0 => entity
-                                    .with_agent_mark(agent::Mark::Guard)
-                                    .with_lazy_loadout(guard_loadout)
-                                    .with_level(dynamic_rng.gen_range(10..15))
-                                    .with_asset_expect("common.entity.village.guard"),
-                                1 | 2 => entity
-                                    .with_agent_mark(agent::Mark::Merchant)
-                                    .with_economy(&economy)
-                                    .with_lazy_loadout(merchant_loadout)
-                                    .with_level(dynamic_rng.gen_range(10..15))
-                                    .with_asset_expect("common.entity.village.merchant"),
-                                _ => entity
-                                    .with_lazy_loadout(villager_loadout)
-                                    .with_asset_expect("common.entity.village.villager"),
-                            }
-                        });
+                    let entity = if is_dummy {
+                        EntityInfo::at(entity_wpos)
+                            .with_agency(false)
+                            .with_asset_expect("common.entity.village.dummy")
+                    } else {
+                        match dynamic_rng.gen_range(0..=4) {
+                            0 => barnyard(entity_wpos, dynamic_rng),
+                            1 => bird(entity_wpos, dynamic_rng),
+                            _ => human(entity_wpos, &economy, dynamic_rng),
+                        }
+                    };
 
                     supplement.add_entity(entity);
                 }
@@ -1031,6 +974,59 @@ impl Settlement {
     }
 }
 
+fn barnyard(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
+    let species = match dynamic_rng.gen_range(0..5) {
+        0 => quadruped_small::Species::Pig,
+        1 => quadruped_small::Species::Sheep,
+        2 => quadruped_small::Species::Goat,
+        3 => quadruped_small::Species::Dog,
+        _ => quadruped_small::Species::Cat,
+    };
+    EntityInfo::at(pos)
+        .with_body(comp::Body::QuadrupedSmall(
+            quadruped_small::Body::random_with(dynamic_rng, &species),
+        ))
+        .with_alignment(comp::Alignment::Tame)
+        .with_automatic_name()
+}
+
+fn bird(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
+    let species = match dynamic_rng.gen_range(0..4) {
+        0 => bird_medium::Species::Duck,
+        1 => bird_medium::Species::Chicken,
+        2 => bird_medium::Species::Goose,
+        _ => bird_medium::Species::Peacock,
+    };
+    EntityInfo::at(pos)
+        .with_body(comp::Body::BirdMedium(bird_medium::Body::random_with(
+            dynamic_rng,
+            &species,
+        )))
+        .with_alignment(comp::Alignment::Tame)
+        .with_automatic_name()
+}
+
+fn human(pos: Vec3<f32>, economy: &SiteInformation, dynamic_rng: &mut impl Rng) -> EntityInfo {
+    let entity = EntityInfo::at(pos);
+    match dynamic_rng.gen_range(0..12) {
+        0 => entity
+            .with_agent_mark(agent::Mark::Guard)
+            .with_lazy_loadout(guard_loadout)
+            .with_level(dynamic_rng.gen_range(10..15))
+            .with_asset_expect("common.entity.village.guard"),
+        1 | 2 => entity
+            .with_agent_mark(agent::Mark::Merchant)
+            .with_economy(&economy)
+            .with_lazy_loadout(merchant_loadout)
+            .with_level(dynamic_rng.gen_range(10..15))
+            .with_asset_expect("common.entity.village.merchant"),
+        _ => entity
+            .with_lazy_loadout(villager_loadout)
+            .with_asset_expect("common.entity.village.villager")
+            .with_automatic_name(),
+    }
+}
+
 fn merchant_loadout(
     loadout_builder: LoadoutBuilder,
     economy: Option<&trade::SiteInformation>,
diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs
index 6ee1804e08..3266ef4182 100644
--- a/world/src/site2/plot/dungeon.rs
+++ b/world/src/site2/plot/dungeon.rs
@@ -9,7 +9,6 @@ use crate::{
 use common::{
     assets::{self, AssetExt, AssetHandle},
     astar::Astar,
-    comp::{self},
     generation::{ChunkSupplement, EntityInfo},
     store::{Id, Store},
     terrain::{
@@ -235,16 +234,14 @@ impl Room {
 
             for entity in entities {
                 supplement.add_entity(
-                    entity
-                        .with_level(
-                            dynamic_rng
-                                .gen_range(
-                                    (self.difficulty as f32).powf(1.25) + 3.0
-                                        ..(self.difficulty as f32).powf(1.5) + 4.0,
-                                )
-                                .round() as u16,
-                        )
-                        .with_alignment(comp::Alignment::Enemy),
+                    entity.with_level(
+                        dynamic_rng
+                            .gen_range(
+                                (self.difficulty as f32).powf(1.25) + 3.0
+                                    ..(self.difficulty as f32).powf(1.5) + 4.0,
+                            )
+                            .round() as u16,
+                    ),
                 );
             }
         } else {
@@ -260,19 +257,13 @@ impl Room {
                                 - 16
                         })
                         .map(|e| e as f32 / 16.0);
-                let turret =
-                    EntityInfo::at(pos.map(|e| e as f32)).with_alignment(comp::Alignment::Enemy);
                 match self.difficulty {
                     3 => {
-                        let turret = turret
-                            .with_body(comp::Body::Object(comp::object::Body::HaniwaSentry))
-                            .with_asset_expect("common.entity.dungeon.tier-3.sentry");
+                        let turret = turret_3(pos);
                         supplement.add_entity(turret);
                     },
                     5 => {
-                        let turret = turret
-                            .with_body(comp::Body::Object(comp::object::Body::Crossbow))
-                            .with_asset_expect("common.entity.dungeon.tier-5.turret");
+                        let turret = turret_5(pos);
                         supplement.add_entity(turret);
                     },
                     _ => {},
@@ -311,17 +302,15 @@ impl Room {
 
             for entity in entities {
                 supplement.add_entity(
-                    entity
-                        .with_level(
-                            dynamic_rng
-                                .gen_range(
-                                    (self.difficulty as f32).powf(1.25) + 3.0
-                                        ..(self.difficulty as f32).powf(1.5) + 4.0,
-                                )
-                                .round() as u16
-                                * 5,
-                        )
-                        .with_alignment(comp::Alignment::Enemy),
+                    entity.with_level(
+                        dynamic_rng
+                            .gen_range(
+                                (self.difficulty as f32).powf(1.25) + 3.0
+                                    ..(self.difficulty as f32).powf(1.5) + 4.0,
+                            )
+                            .round() as u16
+                            * 5,
+                    ),
                 );
             }
         }
@@ -357,17 +346,15 @@ impl Room {
 
             for entity in entities {
                 supplement.add_entity(
-                    entity
-                        .with_level(
-                            dynamic_rng
-                                .gen_range(
-                                    (self.difficulty as f32).powf(1.25) + 3.0
-                                        ..(self.difficulty as f32).powf(1.5) + 4.0,
-                                )
-                                .round() as u16
-                                * 5,
-                        )
-                        .with_alignment(comp::Alignment::Enemy),
+                    entity.with_level(
+                        dynamic_rng
+                            .gen_range(
+                                (self.difficulty as f32).powf(1.25) + 3.0
+                                    ..(self.difficulty as f32).powf(1.5) + 4.0,
+                            )
+                            .round() as u16
+                            * 5,
+                    ),
                 );
             }
         }
@@ -789,6 +776,14 @@ fn enemy_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<En
     entities
 }
 
+fn turret_3(pos: Vec3<f32>) -> EntityInfo {
+    EntityInfo::at(pos).with_asset_expect("common.entity.dungeon.tier-3.sentry")
+}
+
+fn turret_5(pos: Vec3<f32>) -> EntityInfo {
+    EntityInfo::at(pos).with_asset_expect("common.entity.dungeon.tier-5.turret")
+}
+
 fn boss_0(tile_wcenter: Vec3<i32>) -> Vec<EntityInfo> {
     vec![
         EntityInfo::at(tile_wcenter.map(|e| e as f32))
@@ -1441,4 +1436,11 @@ mod tests {
         mini_boss_5(&mut dynamic_rng, tile_wcenter);
         mini_boss_fallback(tile_wcenter);
     }
+
+    #[test]
+    fn test_creating_turrets() {
+        let pos = Vec3::new(0.0, 0.0, 0.0);
+        turret_3(pos);
+        turret_5(pos);
+    }
 }

From 78b780e8bdd26af196193779546d283376da2f0c Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Mon, 5 Jul 2021 01:08:26 +0300
Subject: [PATCH 02/12] Fix bug with ignoring second_tool in EntityInfo

---
 server/src/sys/terrain.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs
index 8514cfbb31..25ce94cd7f 100644
--- a/server/src/sys/terrain.rs
+++ b/server/src/sys/terrain.rs
@@ -208,6 +208,7 @@ impl<'a> System<'a> for Sys {
                 let EntityInfo {
                     skillset_asset,
                     main_tool,
+                    second_tool,
                     loadout_asset,
                     make_loadout,
                     trading_information: economy,
@@ -234,6 +235,11 @@ impl<'a> System<'a> for Sys {
                         loadout_builder = loadout_builder.with_default_maintool(&body);
                     }
 
+                    // If second tool is passed, use it as well
+                    if let Some(second_tool) = second_tool {
+                        loadout_builder = loadout_builder.active_offhand(Some(second_tool));
+                    }
+
                     // If there is config, apply it.
                     // If not, use default equipement for this body.
                     if let Some(asset) = loadout_asset {

From 19c7355c4e4e77d2be4be2db56c6673cf50f436c Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Tue, 6 Jul 2021 01:46:57 +0300
Subject: [PATCH 03/12] Add wildlife assetization support

+ add asset tests
+ migrate tundra animals
+ migrate taiga animals
+ declare temperate zone (animals yet to be created)
+ declare jungle zone (animals yet to be created)
+ declare tropical zone (animals yet to be created)
+ declare desert zone (animals yet to be created)
+ declare water (river inhabitants) zones (animals yet to be created)
---
 .../entity/wild/aggressive/dreadhorn.ron      |   13 +
 .../entity/wild/aggressive/frostfang.ron      |   13 +
 .../common/entity/wild/aggressive/grolgar.ron |   13 +
 .../common/entity/wild/aggressive/mammoth.ron |   13 +
 .../entity/wild/aggressive/mountain_troll.ron |   13 +
 .../entity/wild/aggressive/rocksnapper.ron    |   13 +
 .../entity/wild/aggressive/roshwalr.ron       |   16 +
 .../entity/wild/aggressive/snow_leopard.ron   |   13 +
 .../entity/wild/aggressive/snow_raptor.ron    |   13 +
 .../common/entity/wild/aggressive/wendigo.ron |   13 +
 assets/common/entity/wild/aggressive/wolf.ron |   13 +
 assets/common/entity/wild/aggressive/yale.ron |   13 +
 assets/common/entity/wild/peaceful/alpaca.ron |   13 +
 .../entity/wild/peaceful/arctic_fox.ron       |   13 +
 .../entity/wild/peaceful/arctic_hare.ron      |   13 +
 assets/common/entity/wild/peaceful/eagle.ron  |   13 +
 .../common/entity/wild/peaceful/highland.ron  |   13 +
 assets/common/entity/wild/peaceful/llama.ron  |   13 +
 assets/common/entity/wild/peaceful/moose.ron  |   13 +
 .../common/entity/wild/peaceful/mouflon.ron   |   13 +
 assets/common/entity/wild/peaceful/owl.ron    |   13 +
 .../common/entity/wild/peaceful/tuskram.ron   |   13 +
 assets/common/entity/wild/peaceful/yak.ron    |   13 +
 assets/world/wildlife/spawn/desert/area.ron   |   14 +
 assets/world/wildlife/spawn/desert/hot.ron    |   25 +
 assets/world/wildlife/spawn/desert/river.ron  |   13 +
 .../world/wildlife/spawn/desert/wasteland.ron |   23 +
 .../wildlife/spawn/jungle/rainforest.ron      |   30 +
 .../wildlife/spawn/jungle/rainforest_area.ron |   26 +
 assets/world/wildlife/spawn/taiga/area.ron    |   18 +
 assets/world/wildlife/spawn/taiga/core.ron    |   17 +
 .../wildlife/spawn/taiga/core_forest.ron      |   14 +
 assets/world/wildlife/spawn/taiga/forest.ron  |   13 +
 assets/world/wildlife/spawn/taiga/water.ron   |   13 +
 .../wildlife/spawn/temperate/rainforest.ron   |   51 +
 .../world/wildlife/spawn/temperate/rare.ron   |   15 +
 .../world/wildlife/spawn/temperate/river.ron  |   17 +
 .../world/wildlife/spawn/temperate/water.ron  |   15 +
 .../world/wildlife/spawn/temperate/wood.ron   |   17 +
 .../wildlife/spawn/tropical/rainforest.ron    |   14 +
 .../world/wildlife/spawn/tropical/river.ron   |   15 +
 .../wildlife/spawn/tropical/river_rare.ron    |   13 +
 assets/world/wildlife/spawn/tropical/rock.ron |   13 +
 assets/world/wildlife/spawn/tundra/core.ron   |   16 +
 assets/world/wildlife/spawn/tundra/forest.ron |   16 +
 assets/world/wildlife/spawn/tundra/rock.ron   |   13 +
 assets/world/wildlife/spawn/tundra/snow.ron   |   15 +
 common/src/time.rs                            |    4 +-
 world/src/config.rs                           |    3 +
 world/src/layer/wildlife.rs                   | 1381 ++++-------------
 50 files changed, 1077 insertions(+), 1049 deletions(-)
 create mode 100644 assets/common/entity/wild/aggressive/dreadhorn.ron
 create mode 100644 assets/common/entity/wild/aggressive/frostfang.ron
 create mode 100644 assets/common/entity/wild/aggressive/grolgar.ron
 create mode 100644 assets/common/entity/wild/aggressive/mammoth.ron
 create mode 100644 assets/common/entity/wild/aggressive/mountain_troll.ron
 create mode 100644 assets/common/entity/wild/aggressive/rocksnapper.ron
 create mode 100644 assets/common/entity/wild/aggressive/roshwalr.ron
 create mode 100644 assets/common/entity/wild/aggressive/snow_leopard.ron
 create mode 100644 assets/common/entity/wild/aggressive/snow_raptor.ron
 create mode 100644 assets/common/entity/wild/aggressive/wendigo.ron
 create mode 100644 assets/common/entity/wild/aggressive/wolf.ron
 create mode 100644 assets/common/entity/wild/aggressive/yale.ron
 create mode 100644 assets/common/entity/wild/peaceful/alpaca.ron
 create mode 100644 assets/common/entity/wild/peaceful/arctic_fox.ron
 create mode 100644 assets/common/entity/wild/peaceful/arctic_hare.ron
 create mode 100644 assets/common/entity/wild/peaceful/eagle.ron
 create mode 100644 assets/common/entity/wild/peaceful/highland.ron
 create mode 100644 assets/common/entity/wild/peaceful/llama.ron
 create mode 100644 assets/common/entity/wild/peaceful/moose.ron
 create mode 100644 assets/common/entity/wild/peaceful/mouflon.ron
 create mode 100644 assets/common/entity/wild/peaceful/owl.ron
 create mode 100644 assets/common/entity/wild/peaceful/tuskram.ron
 create mode 100644 assets/common/entity/wild/peaceful/yak.ron
 create mode 100644 assets/world/wildlife/spawn/desert/area.ron
 create mode 100644 assets/world/wildlife/spawn/desert/hot.ron
 create mode 100644 assets/world/wildlife/spawn/desert/river.ron
 create mode 100644 assets/world/wildlife/spawn/desert/wasteland.ron
 create mode 100644 assets/world/wildlife/spawn/jungle/rainforest.ron
 create mode 100644 assets/world/wildlife/spawn/jungle/rainforest_area.ron
 create mode 100644 assets/world/wildlife/spawn/taiga/area.ron
 create mode 100644 assets/world/wildlife/spawn/taiga/core.ron
 create mode 100644 assets/world/wildlife/spawn/taiga/core_forest.ron
 create mode 100644 assets/world/wildlife/spawn/taiga/forest.ron
 create mode 100644 assets/world/wildlife/spawn/taiga/water.ron
 create mode 100644 assets/world/wildlife/spawn/temperate/rainforest.ron
 create mode 100644 assets/world/wildlife/spawn/temperate/rare.ron
 create mode 100644 assets/world/wildlife/spawn/temperate/river.ron
 create mode 100644 assets/world/wildlife/spawn/temperate/water.ron
 create mode 100644 assets/world/wildlife/spawn/temperate/wood.ron
 create mode 100644 assets/world/wildlife/spawn/tropical/rainforest.ron
 create mode 100644 assets/world/wildlife/spawn/tropical/river.ron
 create mode 100644 assets/world/wildlife/spawn/tropical/river_rare.ron
 create mode 100644 assets/world/wildlife/spawn/tropical/rock.ron
 create mode 100644 assets/world/wildlife/spawn/tundra/core.ron
 create mode 100644 assets/world/wildlife/spawn/tundra/forest.ron
 create mode 100644 assets/world/wildlife/spawn/tundra/rock.ron
 create mode 100644 assets/world/wildlife/spawn/tundra/snow.ron

diff --git a/assets/common/entity/wild/aggressive/dreadhorn.ron b/assets/common/entity/wild/aggressive/dreadhorn.ron
new file mode 100644
index 0000000000..bf65e87d08
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/dreadhorn.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Dreadhorn"),
+    body: RandomWith("dreadhorn"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/frostfang.ron b/assets/common/entity/wild/aggressive/frostfang.ron
new file mode 100644
index 0000000000..3335604852
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/frostfang.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Frostfang"),
+    body: RandomWith("frostfang"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/grolgar.ron b/assets/common/entity/wild/aggressive/grolgar.ron
new file mode 100644
index 0000000000..07912bb204
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/grolgar.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Grolgar"),
+    body: RandomWith("grolgar"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/mammoth.ron b/assets/common/entity/wild/aggressive/mammoth.ron
new file mode 100644
index 0000000000..24a8321cc9
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/mammoth.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Mammoth"),
+    body: RandomWith("mammoth"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/mountain_troll.ron b/assets/common/entity/wild/aggressive/mountain_troll.ron
new file mode 100644
index 0000000000..ca127f45e9
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/mountain_troll.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Mountain Troll"),
+    body: RandomWith("troll_mountain"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/rocksnapper.ron b/assets/common/entity/wild/aggressive/rocksnapper.ron
new file mode 100644
index 0000000000..43f6fbfcfa
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/rocksnapper.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Rock Snapper"),
+    body: RandomWith("rocksnapper"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/roshwalr.ron b/assets/common/entity/wild/aggressive/roshwalr.ron
new file mode 100644
index 0000000000..de322d8653
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/roshwalr.ron
@@ -0,0 +1,16 @@
+EntityConfig (
+    name: Some("Roshwalr"),
+    body: Exact(QuadrupedMedium(Body(
+        species: Roshwalr,
+        body_type: Male,
+    ))),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/snow_leopard.ron b/assets/common/entity/wild/aggressive/snow_leopard.ron
new file mode 100644
index 0000000000..2d6e376a12
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/snow_leopard.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Snow Leopard"),
+    body: RandomWith("snowleopard"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/snow_raptor.ron b/assets/common/entity/wild/aggressive/snow_raptor.ron
new file mode 100644
index 0000000000..8c07519312
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/snow_raptor.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Snow Raptor"),
+    body: RandomWith("raptor_snow"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/wendigo.ron b/assets/common/entity/wild/aggressive/wendigo.ron
new file mode 100644
index 0000000000..a655f63a9e
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/wendigo.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Wendigo"),
+    body: RandomWith("wendigo"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/wolf.ron b/assets/common/entity/wild/aggressive/wolf.ron
new file mode 100644
index 0000000000..4d0d3de3dd
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/wolf.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Wolf"),
+    body: RandomWith("wolf"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/aggressive/yale.ron b/assets/common/entity/wild/aggressive/yale.ron
new file mode 100644
index 0000000000..f0e64cb936
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/yale.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Yale"),
+    body: RandomWith("yale"),
+    alignment: Alignment(Enemy),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/alpaca.ron b/assets/common/entity/wild/peaceful/alpaca.ron
new file mode 100644
index 0000000000..aca63d07d7
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/alpaca.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Alpaca"),
+    body: RandomWith("alpaca"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/arctic_fox.ron b/assets/common/entity/wild/peaceful/arctic_fox.ron
new file mode 100644
index 0000000000..986c771aea
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/arctic_fox.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Fox"),
+    body: Exact(QuadrupedSmall(Body(species: Fox, body_type: Female))),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/arctic_hare.ron b/assets/common/entity/wild/peaceful/arctic_hare.ron
new file mode 100644
index 0000000000..b839a7c4de
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/arctic_hare.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Hare"),
+    body: Exact(QuadrupedSmall(Body(species: Hare, body_type: Female))),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/eagle.ron b/assets/common/entity/wild/peaceful/eagle.ron
new file mode 100644
index 0000000000..debf5ce6f5
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/eagle.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Eagle"),
+    body: RandomWith("eagle"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/highland.ron b/assets/common/entity/wild/peaceful/highland.ron
new file mode 100644
index 0000000000..36baa5539a
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/highland.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Highland"),
+    body: RandomWith("highland"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/llama.ron b/assets/common/entity/wild/peaceful/llama.ron
new file mode 100644
index 0000000000..1a92cced90
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/llama.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Llama"),
+    body: RandomWith("llama"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/moose.ron b/assets/common/entity/wild/peaceful/moose.ron
new file mode 100644
index 0000000000..1475c586aa
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/moose.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Moose"),
+    body: RandomWith("moose"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/mouflon.ron b/assets/common/entity/wild/peaceful/mouflon.ron
new file mode 100644
index 0000000000..1788f8f9de
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/mouflon.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Mouflon"),
+    body: RandomWith("mouflon"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/owl.ron b/assets/common/entity/wild/peaceful/owl.ron
new file mode 100644
index 0000000000..bcc12c8e64
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/owl.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Owl"),
+    body: RandomWith("owl"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/tuskram.ron b/assets/common/entity/wild/peaceful/tuskram.ron
new file mode 100644
index 0000000000..ffcadff2a3
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/tuskram.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Tuskram"),
+    body: RandomWith("tuskram"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/common/entity/wild/peaceful/yak.ron b/assets/common/entity/wild/peaceful/yak.ron
new file mode 100644
index 0000000000..58988f2ae8
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/yak.ron
@@ -0,0 +1,13 @@
+EntityConfig (
+    name: Some("Yak"),
+    body: RandomWith("yak"),
+    alignment: Alignment(Wild),
+
+    loot: None,
+
+    main_tool: None,
+    second_tool: None,
+
+    loadout_asset: None,
+    skillset_asset: None,
+)
diff --git a/assets/world/wildlife/spawn/desert/area.ron b/assets/world/wildlife/spawn/desert/area.ron
new file mode 100644
index 0000000000..2c3aca95e5
--- /dev/null
+++ b/assets/world/wildlife/spawn/desert/area.ron
@@ -0,0 +1,14 @@
+SpawnEntry (
+    name: "Desert area wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (3, 6, "common.entity.wild.peaceful.zebra")),
+                (1, (3, 6, "common.entity.wild.peaceful.antelope")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/desert/hot.ron b/assets/world/wildlife/spawn/desert/hot.ron
new file mode 100644
index 0000000000..1f56298d9e
--- /dev/null
+++ b/assets/world/wildlife/spawn/desert/hot.ron
@@ -0,0 +1,25 @@
+SpawnEntry (
+    name: "Desert hot area wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.peaceful.camel")),
+                (1, (1, 1, "common.entity.wild.peaceful.sand_hare")),
+                (1, (1, 1, "common.entity.wild.peaceful.sand_salamander")),
+                (1, (1, 1, "common.entity.wild.peaceful.gecko")),
+            ],
+            is_underwater: false,
+            day_period: [Morning, Noon, Evening],
+        ),
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.peaceful.holladon")),
+                (1, (1, 1, "common.entity.wild.peaceful.porcupine")),
+                (1, (1, 1, "common.entity.wild.peaceful.pangolin")),
+            ],
+            is_underwater: false,
+            day_period: [Night],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/desert/river.ron b/assets/world/wildlife/spawn/desert/river.ron
new file mode 100644
index 0000000000..8b60349da6
--- /dev/null
+++ b/assets/world/wildlife/spawn/desert/river.ron
@@ -0,0 +1,13 @@
+SpawnEntry (
+    name: "Desert river wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.agressive.crocodile")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/desert/wasteland.ron b/assets/world/wildlife/spawn/desert/wasteland.ron
new file mode 100644
index 0000000000..f42867d2ed
--- /dev/null
+++ b/assets/world/wildlife/spawn/desert/wasteland.ron
@@ -0,0 +1,23 @@
+SpawnEntry (
+    name: "Desert wasteland wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                // Casual
+                (100, (1, 1, "common.entity.wild.aggressive.bonerattler")),
+                (100, (1, 1, "common.entity.wild.aggressive.sand_raptor")),
+                (100, (1, 1, "common.entity.wild.aggressive.ngoubou")),
+                (100, (1, 1, "common.entity.wild.aggressive.sandshark")),
+                // Rare
+                (20, (1, 1, "common.entity.wild.aggressive.lavadrake")),
+                (20, (1, 1, "common.entity.wild.aggressive.ntouka")),
+                (20, (1, 1, "common.entity.wild.aggressive.archaeos")),
+                // Ultra_rare
+                (1, (1, 1, "common.entity.wild.agressive.roshwalr_boss")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/jungle/rainforest.ron b/assets/world/wildlife/spawn/jungle/rainforest.ron
new file mode 100644
index 0000000000..14b85d0145
--- /dev/null
+++ b/assets/world/wildlife/spawn/jungle/rainforest.ron
@@ -0,0 +1,30 @@
+SpawnEntry (
+    name: "Jungle rainforest core wildlife.",
+    note: "Concentrated in the core of biome.",
+    rules: [
+        Pack(
+            groups: [
+                // Casual
+                (5, (1, 1, "common.entity.wild.aggressive.asp")),
+                (5, (1, 1, "common.entity.wild.aggressive.tiger")),
+                // Rare
+                (1, (1, 1, "common.entity.wild.aggressive.sunlizard")),
+                (1, (1, 1, "common.entity.wild.aggressive.odonto")),
+                (1, (1, 1, "common.entity.wild.aggressive.mighty_saurok")),
+                (1, (1, 1, "common.entity.wild.aggressive.occult_saurok")),
+                (1, (1, 1, "common.entity.wild.aggressive.sly_saurok")),
+            ],
+            is_underwater: false,
+            day_period: [Morning, Noon, Evening],
+        ),
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.aggressive.tiger")),
+                (1, (1, 1, "common.entity.wild.aggressive.maneater")),
+                (1, (1, 1, "common.entity.wild.aggressive.cockatrice")),
+            ],
+            is_underwater: false,
+            day_period: [Night],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/jungle/rainforest_area.ron b/assets/world/wildlife/spawn/jungle/rainforest_area.ron
new file mode 100644
index 0000000000..d25f2a6109
--- /dev/null
+++ b/assets/world/wildlife/spawn/jungle/rainforest_area.ron
@@ -0,0 +1,26 @@
+SpawnEntry (
+    name: "Jungle rainforest rare wildlife.",
+    note: "Spread through the area.",
+    rules: [
+        Pack(
+            groups: [
+                // Casual
+                (5, (1, 1, "common.entity.wild.peaceful.parrot")),
+                (5, (1, 1, "common.entity.wild.peaceful.quokka")),
+                // Rare
+                (1, (1, 1, "common.entity.wild.peaceful.tortoise")),
+                (1, (1, 1, "common.entity.wild.aggressive.monitor")),
+            ],
+            is_underwater: false,
+            day_period: [Morning, Noon, Evening],
+        ),
+        Pack(
+            groups: [
+                (5, (1, 1, "common.entity.wild.peaceful.quokka")),
+                (1, (1, 1, "common.entity.wild.peaceful.tortoise")),
+            ],
+            is_underwater: false,
+            day_period: [Night],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/taiga/area.ron b/assets/world/wildlife/spawn/taiga/area.ron
new file mode 100644
index 0000000000..e9400a0988
--- /dev/null
+++ b/assets/world/wildlife/spawn/taiga/area.ron
@@ -0,0 +1,18 @@
+SpawnEntry (
+    name: "Taiga animals.",
+    note: "Spread through the area",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.peaceful.eagle")),
+                (1, (1, 1, "common.entity.wild.peaceful.owl")),
+                (1, (1, 1, "common.entity.wild.peaceful.arctic_fox")),
+                (1, (1, 1, "common.entity.wild.peaceful.moose")),
+                (1, (1, 1, "common.entity.wild.peaceful.arctic_hare")),
+                (1, (1, 1, "common.entity.wild.peaceful.tuskram")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/taiga/core.ron b/assets/world/wildlife/spawn/taiga/core.ron
new file mode 100644
index 0000000000..1604fa4171
--- /dev/null
+++ b/assets/world/wildlife/spawn/taiga/core.ron
@@ -0,0 +1,17 @@
+SpawnEntry (
+    name: "Taiga pack animals.",
+    note: "Search for them in the heart of taiga.",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 3, "common.entity.wild.peaceful.mouflon")),
+                (1, (1, 3, "common.entity.wild.peaceful.yak")),
+                (1, (1, 3, "common.entity.wild.peaceful.llama")),
+                (1, (1, 3, "common.entity.wild.peaceful.alpaca")),
+                (1, (1, 3, "common.entity.wild.peaceful.highland")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/taiga/core_forest.ron b/assets/world/wildlife/spawn/taiga/core_forest.ron
new file mode 100644
index 0000000000..673d1579dc
--- /dev/null
+++ b/assets/world/wildlife/spawn/taiga/core_forest.ron
@@ -0,0 +1,14 @@
+SpawnEntry (
+    name: "Taiga rare forest wildlife.",
+    note: "Search for them in the heart of the taiga, if you are feeling lucky.",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.aggressive.wendigo")),
+                (1, (1, 1, "common.entity.wild.aggressive.dreadhorn")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/taiga/forest.ron b/assets/world/wildlife/spawn/taiga/forest.ron
new file mode 100644
index 0000000000..a6731694e2
--- /dev/null
+++ b/assets/world/wildlife/spawn/taiga/forest.ron
@@ -0,0 +1,13 @@
+SpawnEntry (
+    name: "Taiga forest wildlife.",
+    note: "Search for them in taiga forests.",
+    rules: [
+        Pack(
+            groups: [
+                (1, (3, 7, "common.entity.wild.aggressive.wolf")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/taiga/water.ron b/assets/world/wildlife/spawn/taiga/water.ron
new file mode 100644
index 0000000000..8ad83b0baa
--- /dev/null
+++ b/assets/world/wildlife/spawn/taiga/water.ron
@@ -0,0 +1,13 @@
+SpawnEntry (
+    name: "Taiga water wildlife.",
+    note: "River inhabitants",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 2, "common.entity.wild.aggressive.icepike")),
+            ],
+            is_underwater: true,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/temperate/rainforest.ron b/assets/world/wildlife/spawn/temperate/rainforest.ron
new file mode 100644
index 0000000000..317870f474
--- /dev/null
+++ b/assets/world/wildlife/spawn/temperate/rainforest.ron
@@ -0,0 +1,51 @@
+SpawnEntry (
+    name: "Temperate rainforest wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                // Pack animals
+                (1, (1, 7, "common.entity.wild.peaceful.deer")),
+                (1, (1, 3, "common.entity.wild.peaceful.rat")),
+                (1, (1, 7, "common.entity.wild.peaceful.rabbit")),
+                (1, (1, 7, "common.entity.wild.peaceful.jackalope")),
+                (1, (1, 7, "common.entity.wild.peaceful.boar")),
+                (1, (1, 7, "common.entity.wild.peaceful.sheep")),
+                (1, (1, 7, "common.entity.wild.peaceful.pig")),
+                (1, (1, 7, "common.entity.wild.peaceful.squirrel")),
+                (1, (1, 7, "common.entity.wild.peaceful.horse")),
+                (1, (1, 7, "common.entity.wild.peaceful.cattle")),
+                (1, (1, 7, "common.entity.wild.peaceful.goat")),
+                (1, (1, 7, "common.entity.wild.peaceful.llama")),
+                (1, (1, 7, "common.entity.wild.peaceful.alpaca")),
+                (1, (1, 7, "common.entity.wild.peaceful.chicken")),
+                // Solitary
+                (2, (1, 1, "common.entity.wild.peaceful.forest_fox")),
+                (2, (1, 1, "common.entity.wild.peaceful.donkey")),
+                (2, (1, 1, "common.entity.wild.peaceful.goose")),
+                (2, (1, 1, "common.entity.wild.peaceful.peacock")),
+                (2, (1, 1, "common.entity.wild.peaceful.skunk")),
+                (2, (1, 1, "common.entity.wild.peaceful.raccon")),
+                (2, (1, 1, "common.entity.wild.peaceful.catoblepas")),
+                (2, (1, 1, "common.entity.wild.peaceful.turtle")),
+                (2, (1, 1, "common.entity.wild.peaceful.hidrasil")),
+                (2, (1, 1, "common.entity.wild.peaceful.truffler")),
+            ],
+            is_underwater: false,
+            day_period: [Morning, Noon, Evening],
+        ),
+        Pack(
+            groups: [
+                // Solitary
+                (1, (1, 1, "common.entity.wild.aggressive.batfox")),
+                (5, (1, 1, "common.entity.wild.peaceful.forest_fox")),
+                (5, (1, 1, "common.entity.wild.peaceful.raccon")),
+                // Pack
+                (5, (1, 3, "common.entity.wild.peaceful.rat")),
+                (5, (1, 3, "common.entity.wild.peaceful.squirrel")),
+            ],
+            is_underwater: false,
+            day_period: [Night],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/temperate/rare.ron b/assets/world/wildlife/spawn/temperate/rare.ron
new file mode 100644
index 0000000000..790ff2b2db
--- /dev/null
+++ b/assets/world/wildlife/spawn/temperate/rare.ron
@@ -0,0 +1,15 @@
+SpawnEntry (
+    name: "Temperate rare wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.aggressive.ogre")),
+                (1, (1, 1, "common.entity.wild.aggressive.swamp_troll")),
+                (1, (1, 1, "common.entity.wild.aggressive.cyclope")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/temperate/river.ron b/assets/world/wildlife/spawn/temperate/river.ron
new file mode 100644
index 0000000000..e8a7a4a146
--- /dev/null
+++ b/assets/world/wildlife/spawn/temperate/river.ron
@@ -0,0 +1,17 @@
+SpawnEntry (
+    name: "Temperate river wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (5, (1, 1, "common.entity.wild.peaceful.beaver")),
+                (5, (1, 1, "common.entity.wild.peaceful.river_salamander")),
+                (5, (1, 1, "common.entity.wild.peaceful.duck")),
+                (1, (1, 1, "common.entity.wild.peaceful.kelpie")),
+                (1, (1, 1, "common.entity.wild.aggressive.hakulaq")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/temperate/water.ron b/assets/world/wildlife/spawn/temperate/water.ron
new file mode 100644
index 0000000000..55be4600d2
--- /dev/null
+++ b/assets/world/wildlife/spawn/temperate/water.ron
@@ -0,0 +1,15 @@
+SpawnEntry (
+    name: "Temperate water wildlife.",
+    note: "River inhabitants",
+    rules: [
+        Pack(
+            groups: [
+                (1, (3, 4, "common.entity.wild.peaceful.marlin")),
+                (1, (3, 4, "common.entity.wild.peaceful.pirahna")),
+                (1, (3, 4, "common.entity.wild.peaceful.clownfish")),
+            ],
+            is_underwater: true,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/temperate/wood.ron b/assets/world/wildlife/spawn/temperate/wood.ron
new file mode 100644
index 0000000000..510ab327a3
--- /dev/null
+++ b/assets/world/wildlife/spawn/temperate/wood.ron
@@ -0,0 +1,17 @@
+SpawnEntry (
+    name: "Temperate wood wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.aggressive.bear")),
+                (1, (1, 1, "common.entity.wild.aggressive.tarasque")),
+                (1, (1, 1, "common.entity.wild.aggressive.wood_raptor")),
+                (1, (1, 1, "common.entity.wild.aggressive.deadwood")),
+                (1, (1, 1, "common.entity.wild.aggressive.saber")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tropical/rainforest.ron b/assets/world/wildlife/spawn/tropical/rainforest.ron
new file mode 100644
index 0000000000..ed7375c42a
--- /dev/null
+++ b/assets/world/wildlife/spawn/tropical/rainforest.ron
@@ -0,0 +1,14 @@
+SpawnEntry (
+    name: "Tropical rainforest wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 2, "common.entity.wild.aggressive.lion")),
+                (1, (1, 3, "common.entity.wild.aggressive.hyena")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tropical/river.ron b/assets/world/wildlife/spawn/tropical/river.ron
new file mode 100644
index 0000000000..c26932d6ac
--- /dev/null
+++ b/assets/world/wildlife/spawn/tropical/river.ron
@@ -0,0 +1,15 @@
+SpawnEntry (
+    name: "Tropical river wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 2, "common.entity.wild.peaceful.frog")),
+                (1, (1, 2, "common.entity.wild.peaceful.axolotl")),
+                (1, (1, 2, "common.entity.wild.peaceful.fungome")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tropical/river_rare.ron b/assets/world/wildlife/spawn/tropical/river_rare.ron
new file mode 100644
index 0000000000..efdc77a4f7
--- /dev/null
+++ b/assets/world/wildlife/spawn/tropical/river_rare.ron
@@ -0,0 +1,13 @@
+SpawnEntry (
+    name: "Tropical rare river wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 2, "common.entity.wild.aggressive.alligator")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tropical/rock.ron b/assets/world/wildlife/spawn/tropical/rock.ron
new file mode 100644
index 0000000000..6a54b8b5a3
--- /dev/null
+++ b/assets/world/wildlife/spawn/tropical/rock.ron
@@ -0,0 +1,13 @@
+SpawnEntry (
+    name: "Tropical rocks wildlife.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.aggressive.dodarock")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tundra/core.ron b/assets/world/wildlife/spawn/tundra/core.ron
new file mode 100644
index 0000000000..12315b9318
--- /dev/null
+++ b/assets/world/wildlife/spawn/tundra/core.ron
@@ -0,0 +1,16 @@
+SpawnEntry (
+    name: "Tundra rare animals.",
+    note: "Search for them in the heart of tundra.",
+    rules: [
+        Pack(
+            groups: [
+                (15, (1, 1, "common.entity.wild.aggressive.snow_raptor")),
+                (1, (1, 1, "common.entity.wild.aggressive.wendigo")),
+                (1, (1, 1, "common.entity.wild.aggressive.mammoth")),
+                (1, (1, 1, "common.entity.wild.aggressive.mountain_troll")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tundra/forest.ron b/assets/world/wildlife/spawn/tundra/forest.ron
new file mode 100644
index 0000000000..eb652f8e42
--- /dev/null
+++ b/assets/world/wildlife/spawn/tundra/forest.ron
@@ -0,0 +1,16 @@
+SpawnEntry (
+    name: "Tundra forest animals.",
+    note: "",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.aggressive.frostfang")),
+                (1, (1, 1, "common.entity.wild.aggressive.snow_leopard")),
+                (1, (1, 1, "common.entity.wild.aggressive.yale")),
+                (1, (1, 1, "common.entity.wild.aggressive.grolgar")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tundra/rock.ron b/assets/world/wildlife/spawn/tundra/rock.ron
new file mode 100644
index 0000000000..79353d8f0d
--- /dev/null
+++ b/assets/world/wildlife/spawn/tundra/rock.ron
@@ -0,0 +1,13 @@
+SpawnEntry (
+    name: "Tundra rocky animals.",
+    note: "Search for the rocks in tundra and you will find them.",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 1, "common.entity.wild.aggressive.rocksnapper")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/assets/world/wildlife/spawn/tundra/snow.ron b/assets/world/wildlife/spawn/tundra/snow.ron
new file mode 100644
index 0000000000..41fd8a1f3e
--- /dev/null
+++ b/assets/world/wildlife/spawn/tundra/snow.ron
@@ -0,0 +1,15 @@
+SpawnEntry (
+    name: "Tundra animals.",
+    note: "Usually you can find them in snowy areas.",
+    rules: [
+        Pack(
+            groups: [
+                (1, (1, 3, "common.entity.wild.aggressive.frostfang")),
+                (1, (1, 3, "common.entity.wild.aggressive.snow_raptor")),
+                (1, (1, 3, "common.entity.wild.aggressive.roshwalr")),
+            ],
+            is_underwater: false,
+            day_period: [Night, Morning, Noon, Evening],
+        ),
+    ],
+)
diff --git a/common/src/time.rs b/common/src/time.rs
index a13ba253f4..33a888cb85 100644
--- a/common/src/time.rs
+++ b/common/src/time.rs
@@ -1,4 +1,6 @@
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+use serde::Deserialize;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Hash)]
 pub enum DayPeriod {
     Night,
     Morning,
diff --git a/world/src/config.rs b/world/src/config.rs
index b3e841fce7..e6db31e6f2 100644
--- a/world/src/config.rs
+++ b/world/src/config.rs
@@ -53,13 +53,16 @@ pub struct Config {
 pub const CONFIG: Config = Config {
     sea_level: 140.0,
     mountain_scale: 2048.0,
+    // temperature
     snow_temp: -0.8,
     temperate_temp: -0.4,
     tropical_temp: 0.4,
     desert_temp: 0.8,
+    // humidity
     desert_hum: 0.15,
     forest_hum: 0.5,
     jungle_hum: 0.75,
+    // water
     rainfall_chunk_rate: 1.0 / (512.0 * 32.0 * 32.0),
     river_roughness: 0.06125,
     river_max_width: 2.0,
diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs
index df7c579196..e94b43f091 100644
--- a/world/src/layer/wildlife.rs
+++ b/world/src/layer/wildlife.rs
@@ -1,8 +1,9 @@
 use crate::{column::ColumnSample, sim::SimChunk, IndexRef, CONFIG};
 use common::{
+    assets::{self, AssetExt},
     comp::{
-        biped_large, bird_large, bird_medium, fish_medium, fish_small, quadruped_low,
-        quadruped_medium, quadruped_small, theropod, Alignment,
+        fish_medium, fish_small, quadruped_low, quadruped_medium, quadruped_small, theropod,
+        Alignment,
     },
     generation::{ChunkSupplement, EntityInfo},
     resources::TimeOfDay,
@@ -11,6 +12,7 @@ use common::{
     vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
 };
 use rand::prelude::*;
+use serde::Deserialize;
 use std::{f32, ops::Range};
 use vek::*;
 
@@ -20,6 +22,216 @@ fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
 
 const BASE_DENSITY: f32 = 1.0e-5; // Base wildlife density
 
+#[derive(Clone, Debug, Deserialize)]
+pub struct SpawnEntry {
+    /// User-facing info for wiki, statistical tools.
+    pub name: String,
+    pub note: String,
+    /// Rules describing what and when to spawn
+    pub rules: Vec<Pack>,
+}
+
+impl assets::Asset for SpawnEntry {
+    type Loader = assets::RonLoader;
+
+    const EXTENSION: &'static str = "ron";
+}
+
+impl SpawnEntry {
+    pub fn from(asset_specifier: &str) -> Self { Self::load_expect(asset_specifier).read().clone() }
+
+    pub fn request(&self, requested_period: DayPeriod, underwater: bool) -> Option<&Pack> {
+        self.rules.iter().find(|pack| {
+            let time_match = pack
+                .day_period
+                .iter()
+                .any(|period| *period == requested_period);
+            let water_match = pack.is_underwater == underwater;
+            time_match && water_match
+        })
+    }
+}
+
+/// Dataset of animals to spawn
+#[derive(Clone, Debug, Deserialize)]
+pub struct Pack {
+    pub groups: Vec<(u32, (u8, u8, String))>,
+    pub is_underwater: bool,
+    pub day_period: Vec<DayPeriod>,
+}
+
+impl Pack {
+    pub fn generate(&self, pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> (EntityInfo, u8) {
+        let (_, (from, to, entity_asset)) = self
+            .groups
+            .choose_weighted(dynamic_rng, |(p, _group)| *p)
+            .expect("Failed to choose group");
+        let entity = EntityInfo::at(pos).with_asset_expect(entity_asset);
+        let group_size = dynamic_rng.gen_range(*from..=*to);
+
+        (entity, group_size)
+    }
+}
+
+type DensityFn = fn(&SimChunk, &ColumnSample) -> f32;
+
+pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
+    // NOTE: Order matters.
+    // Place entries with more strict requirements before more general ones
+    vec![
+        // **Tundra**
+        // Rock animals
+        ("world.wildlife.spawn.tundra.rock", |c, col| {
+            close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * col.rock * 1.0
+        }),
+        // Core animals
+        ("world.wildlife.spawn.tundra.core", |c, _col| {
+            close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.5
+        }),
+        // Snowy animals
+        ("world.wildlife.spawn.tundra.snow", |c, col| {
+            close(c.temp, CONFIG.snow_temp, 0.3) * BASE_DENSITY * col.snow_cover as i32 as f32 * 1.0
+        }),
+        // Forest animals
+        ("world.wildlife.spawn.tundra.forest", |c, col| {
+            close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4
+        }),
+        // **Taiga**
+        // Forest core animals
+        ("world.wildlife.spawn.taiga.core_forest", |c, col| {
+            close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * col.tree_density * BASE_DENSITY * 0.4
+        }),
+        // Core animals
+        ("world.wildlife.spawn.taiga.core", |c, _col| {
+            close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 1.0
+        }),
+        // Forest area animals
+        ("world.wildlife.spawn.taiga.forest", |c, col| {
+            close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * col.tree_density * BASE_DENSITY * 0.9
+        }),
+        // Area animals
+        ("world.wildlife.spawn.taiga.area", |c, _col| {
+            close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * BASE_DENSITY * 5.0
+        }),
+        // Water animals
+        ("world.wildlife.spawn.taiga.water", |c, col| {
+            close(c.temp, CONFIG.snow_temp, 0.15) * col.tree_density * BASE_DENSITY * 5.0
+        }),
+        // **Temperate**
+        // Area rare
+        ("world.wildlife.spawn.temperate.rare", |c, _col| {
+            close(c.temp, CONFIG.temperate_temp, 0.8) * BASE_DENSITY * 0.08
+        }),
+        // River wildlife
+        ("world.wildlife.spawn.temperate.river", |_c, col| {
+            close(col.temp, CONFIG.temperate_temp, 0.6)
+                * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
+                    0.001
+                } else {
+                    0.0
+                }
+        }),
+        // Forest animals
+        ("world.wildlife.spawn.temperate.wood", |c, col| {
+            close(c.temp, CONFIG.temperate_temp + 0.1, 0.5) * col.tree_density * BASE_DENSITY * 1.0
+        }),
+        // Rainforest animals
+        ("world.wildlife.spawn.temperate.rainforest", |c, _col| {
+            close(c.temp, CONFIG.temperate_temp + 0.1, 0.6)
+                * close(c.humidity, CONFIG.forest_hum, 0.6)
+                * BASE_DENSITY
+                * 4.0
+        }),
+        // Water animals
+        ("world.wildlife.spawn.temperate.water", |c, col| {
+            close(c.temp, CONFIG.temperate_temp, 1.0) * col.tree_density * BASE_DENSITY * 5.0
+        }),
+        // **Jungle**
+        // Rainforest animals
+        ("world.wildlife.spawn.jungle.rainforest", |c, _col| {
+            close(c.temp, CONFIG.tropical_temp + 0.2, 0.2)
+                * close(c.humidity, CONFIG.jungle_hum, 0.2)
+                * BASE_DENSITY
+                * 2.8
+        }),
+        // Rainforest area animals
+        ("world.wildlife.spawn.jungle.rainforest_area", |c, _col| {
+            close(c.temp, CONFIG.tropical_temp + 0.2, 0.3)
+                * close(c.humidity, CONFIG.jungle_hum, 0.2)
+                * BASE_DENSITY
+                * 8.0
+        }),
+        // **Tropical**
+        // Rare river animals
+        ("world.wildlife.spawn.tropical.river_rare", |_c, col| {
+            close(col.temp, CONFIG.tropical_temp + 0.2, 0.5)
+                * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
+                    0.0001
+                } else {
+                    0.0
+                }
+        }),
+        // River animals
+        ("world.wildlife.spawn.tropical.river", |c_, col| {
+            close(col.temp, CONFIG.tropical_temp, 0.5)
+                * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
+                    0.001
+                } else {
+                    0.0
+                }
+        }),
+        // Rainforest area animals
+        (
+            "world.wildlife.spawn.tropical.rainforest_area",
+            |c, _col| {
+                close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
+                    * close(c.humidity, CONFIG.desert_hum, 0.4)
+                    * BASE_DENSITY
+                    * 2.0
+            },
+        ),
+        // Rock animals
+        ("world.wildlife.spawn.tropical.rock", |c, col| {
+            close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock * BASE_DENSITY * 5.0
+        }),
+        // **Desert**
+        // Area animals
+        ("world.wildlife.spawn.desert.area", |c, _col| {
+            close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
+                * close(c.humidity, CONFIG.desert_hum, 0.4)
+                * BASE_DENSITY
+                * 0.8
+        }),
+        // Hot area animals
+        ("world.wildlife.spawn.desert.wasteland", |c, _col| {
+            close(c.temp, CONFIG.desert_temp + 0.2, 0.3)
+                * close(c.humidity, CONFIG.desert_hum, 0.5)
+                * BASE_DENSITY
+                * 1.3
+        }),
+        // River animals
+        ("world.wildlife.spawn.desert.river", |_c, _col| {
+            close(col.temp, CONFIG.desert_temp + 0.2, 0.3)
+                * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
+                    0.0001
+                } else {
+                    0.0
+                }
+        }),
+        ("world.wildlife.spawn.desert.hot", |c, _col| {
+            close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 3.8
+        }),
+    ]
+}
+
+fn wildlife_spawn_entries() -> Vec<(SpawnEntry, DensityFn)> {
+    let manifest = wildlife_spawn_manifest();
+    manifest
+        .into_iter()
+        .map(|(e, f)| (SpawnEntry::from(e), f))
+        .collect()
+}
+
 #[allow(clippy::eval_order_dependence)]
 pub fn apply_wildlife_supplement<'a, R: Rng>(
     // NOTE: Used only for dynamic elements like chests and entities!
@@ -32,1026 +244,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
     supplement: &mut ChunkSupplement,
     time: Option<TimeOfDay>,
 ) {
-    struct Entry<R> {
-        make_entity: fn(Vec3<f32>, &mut R) -> EntityInfo, // Entity
-        group_size: Range<usize>,                         // Group size range
-        is_underwater: bool,                              // Underwater?
-        day_period: Vec<DayPeriod>,                       // Period of the day
-        get_density: fn(&SimChunk, &ColumnSample) -> f32, // Density
-    }
-
-    let scatter: &[Entry<R>] = &[
-        // Tundra snow pack ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Frostfang,
-                        )
-                        .into(),
-                        1 => {
-                            theropod::Body::random_with(rng, &theropod::Species::Snowraptor).into()
-                        },
-                        _ => quadruped_medium::Body {
-                            species: quadruped_medium::Species::Roshwalr,
-                            body_type: quadruped_medium::BodyType::Male,
-                        }
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..4,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.snow_temp, 0.3)
-                    * BASE_DENSITY
-                    * col.snow_cover as i32 as f32
-                    * 1.0
-            },
-        },
-        // Tundra solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..4) {
-                        0 => {
-                            theropod::Body::random_with(rng, &theropod::Species::Snowraptor).into()
-                        },
-                        1 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Snowleopard,
-                        )
-                        .into(),
-                        2 => theropod::Body::random_with(rng, &theropod::Species::Yale).into(),
-                        _ => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Grolgar,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.snow_temp, 0.3) * col.tree_density * BASE_DENSITY * 1.4
-            },
-        },
-        // Tundra rare solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        theropod::Body::random_with(rng, &theropod::Species::Snowraptor).into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.5,
-        },
-        // Tundra rarer solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => biped_large::Body::random_with(rng, &biped_large::Species::Wendigo)
-                            .into(),
-                        1 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Mammoth,
-                        )
-                        .into(),
-                        _ => biped_large::Body::random_with(
-                            rng,
-                            &biped_large::Species::Mountaintroll,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.15,
-        },
-        // Tundra rock solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_low::Body::random_with(rng, &quadruped_low::Species::Rocksnapper)
-                            .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * col.rock * 1.0
-            },
-        },
-        // Taiga rare solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..2) {
-                        0 => biped_large::Body::random_with(rng, &biped_large::Species::Wendigo)
-                            .into(),
-                        _ => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Dreadhorn,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * col.tree_density * BASE_DENSITY * 0.4
-            },
-        },
-        // Taiga pack ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Wolf)
-                            .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 3..8,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * col.tree_density * BASE_DENSITY * 0.9
-            },
-        },
-        // Taiga pack wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..5) {
-                        0 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Mouflon,
-                        )
-                        .into(),
-                        1 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Yak,
-                        )
-                        .into(),
-                        2 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Llama,
-                        )
-                        .into(),
-                        3 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Alpaca,
-                        )
-                        .into(),
-                        _ => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Highland,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..4,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 1.0,
-        },
-        // Taiga solitary wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..6) {
-                        0 => {
-                            bird_medium::Body::random_with(rng, &bird_medium::Species::Eagle).into()
-                        },
-                        1 => bird_medium::Body::random_with(rng, &bird_medium::Species::Owl).into(),
-                        2 => quadruped_small::Body {
-                            species: quadruped_small::Species::Fox,
-                            body_type: quadruped_small::BodyType::Female,
-                        }
-                        .into(),
-                        3 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Moose,
-                        )
-                        .into(),
-                        4 => {
-                            quadruped_small::Body {
-                                species: quadruped_small::Species::Hare,
-                                body_type: quadruped_small::BodyType::Female,
-                            }
-                        }
-                        .into(),
-                        _ => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Tuskram,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * BASE_DENSITY * 5.0,
-        },
-        // Temperate solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..5) {
-                        0 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Tarasque,
-                        )
-                        .into(),
-                        1 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Bear,
-                        )
-                        .into(),
-                        2 => {
-                            theropod::Body::random_with(rng, &theropod::Species::Woodraptor).into()
-                        },
-                        3 => {
-                            quadruped_low::Body::random_with(rng, &quadruped_low::Species::Deadwood)
-                                .into()
-                        },
-                        _ => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Saber,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.temperate_temp + 0.1, 0.5)
-                    * col.tree_density
-                    * BASE_DENSITY
-                    * 1.0
-            },
-        },
-        // Temperate pack wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..14) {
-                        0 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Deer,
-                        )
-                        .into(),
-                        1 => {
-                            quadruped_small::Body::random_with(rng, &quadruped_small::Species::Rat)
-                                .into()
-                        },
-                        2 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Rabbit,
-                        )
-                        .into(),
-                        3 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Jackalope,
-                        )
-                        .into(),
-                        4 => {
-                            quadruped_small::Body::random_with(rng, &quadruped_small::Species::Boar)
-                                .into()
-                        },
-                        5 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Sheep,
-                        )
-                        .into(),
-                        6 => {
-                            quadruped_small::Body::random_with(rng, &quadruped_small::Species::Pig)
-                                .into()
-                        },
-                        7 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Squirrel,
-                        )
-                        .into(),
-                        8 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Horse,
-                        )
-                        .into(),
-                        9 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Cattle,
-                        )
-                        .into(),
-                        10 => {
-                            quadruped_small::Body::random_with(rng, &quadruped_small::Species::Goat)
-                                .into()
-                        },
-                        11 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Llama,
-                        )
-                        .into(),
-                        12 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Alpaca,
-                        )
-                        .into(),
-                        _ => bird_medium::Body::random_with(rng, &bird_medium::Species::Chicken)
-                            .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..8,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.temperate_temp + 0.1, 0.6)
-                    * close(c.humidity, CONFIG.forest_hum, 0.6)
-                    * BASE_DENSITY
-                    * 4.0
-            },
-        },
-        // Temperate solitary wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..10) {
-                        0 => quadruped_small::Body {
-                            species: quadruped_small::Species::Fox,
-                            body_type: quadruped_small::BodyType::Male,
-                        }
-                        .into(),
-                        1 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Donkey,
-                        )
-                        .into(),
-                        2 => {
-                            bird_medium::Body::random_with(rng, &bird_medium::Species::Goose).into()
-                        },
-                        3 => bird_medium::Body::random_with(rng, &bird_medium::Species::Peacock)
-                            .into(),
-                        4 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Skunk,
-                        )
-                        .into(),
-                        5 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Raccoon,
-                        )
-                        .into(),
-                        6 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Catoblepas,
-                        )
-                        .into(),
-                        7 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Turtle,
-                        )
-                        .into(),
-                        8 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Hirdrasil,
-                        )
-                        .into(),
-                        _ => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Truffler,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.temperate_temp + 0.1, 0.6)
-                    * BASE_DENSITY
-                    * close(c.humidity, CONFIG.forest_hum, 0.6)
-                    * 8.0
-            },
-        },
-        // Temperate solitary wild night
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_small::Body::random_with(rng, &quadruped_small::Species::Batfox)
-                            .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.temperate_temp + 0.1, 0.6)
-                    * BASE_DENSITY
-                    * close(c.humidity, CONFIG.forest_hum, 0.6)
-                    * 0.8
-            },
-        },
-        // Rare temperate solitary enemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => {
-                            biped_large::Body::random_with(rng, &biped_large::Species::Ogre).into()
-                        },
-                        1 => biped_large::Body::random_with(rng, &biped_large::Species::Swamptroll)
-                            .into(),
-                        _ => biped_large::Body::random_with(rng, &biped_large::Species::Cyclops)
-                            .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| close(c.temp, CONFIG.temperate_temp, 0.8) * BASE_DENSITY * 0.08,
-        },
-        // Temperate river wildlife
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..4) {
-                        0 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Beaver,
-                        )
-                        .into(),
-                        1 => quadruped_low::Body {
-                            species: quadruped_low::Species::Salamander,
-                            body_type: quadruped_low::BodyType::Female,
-                        }
-                        .into(),
-                        2 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Kelpie,
-                        )
-                        .into(),
-                        _ => {
-                            bird_medium::Body::random_with(rng, &bird_medium::Species::Duck).into()
-                        },
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |_c, col| {
-                close(col.temp, CONFIG.temperate_temp, 0.6)
-                    * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
-                        0.001
-                    } else {
-                        0.0
-                    }
-            },
-        },
-        // Temperate rare river ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Kelpie,
-                        )
-                        .into(),
-                    )
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |_c, col| {
-                close(col.temp, CONFIG.temperate_temp, 0.6)
-                    * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
-                        0.00005
-                    } else {
-                        0.0
-                    }
-            },
-        },
-        // Temperate river ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_low::Body::random_with(rng, &quadruped_low::Species::Hakulaq)
-                            .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |_c, col| {
-                close(col.temp, CONFIG.temperate_temp, 0.6)
-                    * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
-                        0.0001
-                    } else {
-                        0.0
-                    }
-            },
-        },
-        // Tropical rock solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Dodarock,
-                        )
-                        .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock * BASE_DENSITY * 5.0
-            },
-        },
-        // Jungle solitary ennemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => {
-                            quadruped_low::Body::random_with(rng, &quadruped_low::Species::Maneater)
-                                .into()
-                        },
-                        1 => quadruped_low::Body::random_with(rng, &quadruped_low::Species::Asp)
-                            .into(),
-                        _ => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Tiger,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.2, 0.2)
-                    * close(c.humidity, CONFIG.jungle_hum, 0.2)
-                    * BASE_DENSITY
-                    * 2.8
-            },
-        },
-        // Jungle solitary ennemies day
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        theropod::Body::random_with(rng, &theropod::Species::Sunlizard).into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.2, 0.2)
-                    * close(c.humidity, CONFIG.jungle_hum, 0.2)
-                    * BASE_DENSITY
-                    * 0.5
-            },
-        },
-        // Jungle rare solitary wild day
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..5) {
-                        0 => theropod::Body::random_with(rng, &theropod::Species::Odonto).into(),
-                        1 => {
-                            biped_large::Body::random_with(rng, &biped_large::Species::Mightysaurok)
-                                .into()
-                        },
-                        2 => {
-                            biped_large::Body::random_with(rng, &biped_large::Species::Occultsaurok)
-                                .into()
-                        },
-                        3 => bird_large::Body::random_with(rng, &bird_large::Species::Cockatrice)
-                            .into(),
-                        _ => biped_large::Body::random_with(rng, &biped_large::Species::Slysaurok)
-                            .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.2, 0.2)
-                    * close(c.humidity, CONFIG.jungle_hum, 0.2)
-                    * BASE_DENSITY
-                    * 0.8
-            },
-        },
-        // Jungle solitary wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => bird_medium::Body::random_with(rng, &bird_medium::Species::Parrot)
-                            .into(),
-
-                        1 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Quokka,
-                        )
-                        .into(),
-                        _ => {
-                            quadruped_low::Body::random_with(rng, &quadruped_low::Species::Tortoise)
-                                .into()
-                        },
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.2, 0.3)
-                    * close(c.humidity, CONFIG.jungle_hum, 0.2)
-                    * BASE_DENSITY
-                    * 8.0
-            },
-        },
-        // Jungle solitary wild day
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_low::Body::random_with(rng, &quadruped_low::Species::Monitor)
-                            .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.2, 0.3)
-                    * close(c.humidity, CONFIG.jungle_hum, 0.2)
-                    * BASE_DENSITY
-                    * 2.0
-            },
-        },
-        // Tropical rare river enemy
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_low::Body::random_with(rng, &quadruped_low::Species::Alligator)
-                            .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..3,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |_c, col| {
-                close(col.temp, CONFIG.tropical_temp + 0.2, 0.5)
-                    * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
-                        0.0001
-                    } else {
-                        0.0
-                    }
-            },
-        },
-        // Tropical rare river wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => {
-                            quadruped_small::Body::random_with(rng, &quadruped_small::Species::Frog)
-                                .into()
-                        },
-                        1 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Axolotl,
-                        )
-                        .into(),
-                        _ => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Fungome,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..3,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |_c, col| {
-                close(col.temp, CONFIG.tropical_temp, 0.5)
-                    * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
-                        0.001
-                    } else {
-                        0.0
-                    }
-            },
-        },
-        // Tropical pack enemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..2) {
-                        0 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Lion,
-                        )
-                        .into(),
-                        _ => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Hyena,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..3,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
-                    * close(c.humidity, CONFIG.desert_hum, 0.4)
-                    * BASE_DENSITY
-                    * 2.0
-            },
-        },
-        // Desert pack wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..2) {
-                        0 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Zebra,
-                        )
-                        .into(),
-                        _ => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Antelope,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 3..7,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
-                    * close(c.humidity, CONFIG.desert_hum, 0.4)
-                    * BASE_DENSITY
-                    * 0.8
-            },
-        },
-        // Desert solitary enemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..4) {
-                        0 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Bonerattler,
-                        )
-                        .into(),
-                        1 => {
-                            theropod::Body::random_with(rng, &theropod::Species::Sandraptor).into()
-                        },
-                        2 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Ngoubou,
-                        )
-                        .into(),
-                        _ => quadruped_low::Body::random_with(
-                            rng,
-                            &quadruped_low::Species::Sandshark,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.desert_temp + 0.2, 0.3)
-                    * close(c.humidity, CONFIG.desert_hum, 0.5)
-                    * BASE_DENSITY
-                    * 1.3
-            },
-        },
-        // Desert rare solitary enemies
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => quadruped_low::Body::random_with(
-                            rng,
-                            &quadruped_low::Species::Lavadrake,
-                        )
-                        .into(),
-                        1 => theropod::Body::random_with(rng, &theropod::Species::Ntouka).into(),
-                        _ => theropod::Body::random_with(rng, &theropod::Species::Archaeos).into(),
-                    })
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.desert_temp + 0.2, 0.3)
-                    * close(c.humidity, CONFIG.desert_hum, 0.5)
-                    * BASE_DENSITY
-                    * 0.15
-            },
-        },
-        // Desert river solitary enemy
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_low::Body::random_with(rng, &quadruped_low::Species::Crocodile)
-                            .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..3,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |_c, col| {
-                close(col.temp, CONFIG.desert_temp + 0.2, 0.3)
-                    * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
-                        0.0001
-                    } else {
-                        0.0
-                    }
-            },
-        },
-        // Desert secret solitary enemy
-        Entry {
-            make_entity: |pos, _rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        quadruped_medium::Body {
-                            species: quadruped_medium::Species::Roshwalr,
-                            body_type: quadruped_medium::BodyType::Female,
-                        }
-                        .into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..3,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.desert_temp + 0.2, 0.3)
-                    * close(c.humidity, CONFIG.desert_hum, 0.5)
-                    * BASE_DENSITY
-                    * 0.005
-            },
-        },
-        // Desert solitary wild
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..4) {
-                        0 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Holladon,
-                        )
-                        .into(),
-                        1 => {
-                            quadruped_low::Body::random_with(rng, &quadruped_low::Species::Pangolin)
-                                .into()
-                        },
-                        2 => quadruped_medium::Body::random_with(
-                            rng,
-                            &quadruped_medium::Species::Camel,
-                        )
-                        .into(),
-                        3 => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Porcupine,
-                        )
-                        .into(),
-                        _ => quadruped_small::Body {
-                            species: quadruped_small::Species::Hare,
-                            body_type: quadruped_small::BodyType::Male,
-                        }
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 3.8
-            },
-        },
-        // Desert solitary wild day
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        1 => quadruped_low::Body {
-                            species: quadruped_low::Species::Salamander,
-                            body_type: quadruped_low::BodyType::Male,
-                        }
-                        .into(),
-                        _ => quadruped_small::Body::random_with(
-                            rng,
-                            &quadruped_small::Species::Gecko,
-                        )
-                        .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 1..2,
-            is_underwater: false,
-            day_period: vec![Morning, Noon, Evening],
-            get_density: |c, _col| {
-                close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 1.0
-            },
-        },
-        // Underwater temperate
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(match rng.gen_range(0..3) {
-                        0 => fish_medium::Body::random_with(rng, &fish_medium::Species::Marlin)
-                            .into(),
-                        1 => {
-                            fish_small::Body::random_with(rng, &fish_small::Species::Piranha).into()
-                        },
-                        _ => fish_small::Body::random_with(rng, &fish_small::Species::Clownfish)
-                            .into(),
-                    })
-                    .with_alignment(Alignment::Wild)
-            },
-            group_size: 3..5,
-            is_underwater: true,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.temperate_temp, 1.0) * col.tree_density * BASE_DENSITY * 5.0
-            },
-        },
-        // Underwater taiga
-        Entry {
-            make_entity: |pos, rng| {
-                EntityInfo::at(pos)
-                    .with_body(
-                        fish_medium::Body::random_with(rng, &fish_medium::Species::Icepike).into(),
-                    )
-                    .with_alignment(Alignment::Enemy)
-            },
-            group_size: 1..3,
-            is_underwater: true,
-            day_period: vec![Night, Morning, Noon, Evening],
-            get_density: |c, col| {
-                close(c.temp, CONFIG.snow_temp, 0.15) * col.tree_density * BASE_DENSITY * 5.0
-            },
-        },
-    ];
+    let scatter = wildlife_spawn_entries();
 
     for y in 0..vol.size_xy().y as i32 {
         for x in 0..vol.size_xy().x as i32 {
@@ -1074,36 +267,28 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
                 current_day_period = Noon
             }
 
-            let entity_group = scatter.iter().enumerate().find_map(
-                |(
-                    _i,
-                    Entry {
-                        make_entity,
-                        group_size,
-                        is_underwater,
-                        day_period,
-                        get_density,
-                    },
-                )| {
+            let entity_group = scatter
+                .iter()
+                .enumerate()
+                .find_map(|(_i, (entry, get_density))| {
                     let density = get_density(chunk, col_sample);
-                    if density > 0.0
-                        && dynamic_rng.gen::<f32>() < density * col_sample.spawn_rate
-                        && underwater == *is_underwater
-                        && day_period.contains(&current_day_period)
-                        && col_sample.gradient < Some(1.3)
-                    {
-                        Some((make_entity, group_size.clone()))
-                    } else {
-                        None
-                    }
-                },
-            );
+                    (density > 0.0)
+                        .then(|| {
+                            entry
+                                .request(current_day_period, underwater)
+                                .and_then(|pack| {
+                                    (dynamic_rng.gen::<f32>() < density * col_sample.spawn_rate
+                                        && col_sample.gradient < Some(1.3))
+                                    .then(|| pack)
+                                })
+                        })
+                        .flatten()
+                });
 
             let alt = col_sample.alt as i32;
 
-            if let Some((make_entity, group_size)) = entity_group {
-                let group_size = dynamic_rng.gen_range(group_size.start..group_size.end);
-                let entity = make_entity(
+            if let Some(pack) = entity_group {
+                let (entity, group_size) = pack.generate(
                     (wpos2d.map(|e| e as f32) + 0.5).with_z(alt as f32),
                     dynamic_rng,
                 );
@@ -1137,3 +322,103 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use hashbrown::{HashMap, HashSet};
+
+    // Checks that each entry in spawn manifest is loadable
+    #[test]
+    fn test_load_entries() {
+        let scatter = wildlife_spawn_manifest();
+        for (entry, _) in scatter.into_iter() {
+            std::mem::drop(SpawnEntry::from(entry));
+        }
+    }
+
+    // Check that each spawn entry has unique name
+    #[test]
+    fn test_name_uniqueness() {
+        let scatter = wildlife_spawn_manifest();
+        let mut names = HashMap::new();
+        for (entry, _) in scatter.into_iter() {
+            let SpawnEntry { name, .. } = SpawnEntry::from(entry);
+            if let Some(old_entry) = names.insert(name, entry) {
+                panic!("{}: Found name duplicate with {}", entry, old_entry);
+            }
+        }
+    }
+
+    // Checks that day_period aren't overlapping
+    // Quite strict rule, but otherwise may produce unexpected behaviour
+    #[test]
+    fn test_check_day_periods() {
+        let scatter = wildlife_spawn_manifest();
+        for (entry, _) in scatter.into_iter() {
+            let mut day_periods = HashSet::with_capacity(4);
+            let SpawnEntry { rules, .. } = SpawnEntry::from(entry);
+            for pack in rules {
+                let Pack {
+                    day_period,
+                    is_underwater,
+                    ..
+                } = pack;
+                for period in day_period {
+                    if !day_periods.insert((period, is_underwater)) {
+                        panic!(
+                            r#"
+                        == {}: ==
+                            Found rules with duplicated `day_period` and `is_underwater`
+                        If you have two of such entries,
+                        there are big chances that second rule will be unreachable.
+
+                            If you want some animals spawned in different Packs
+                        with same day_period, add those animals to both Packs
+                        and choose day_period to not overlap.
+"#,
+                            entry
+                        )
+                    }
+                }
+            }
+        }
+    }
+
+    // Checks that each entity is loadable
+    #[test]
+    fn test_load_entities() {
+        let scatter = wildlife_spawn_manifest();
+        for (entry, _) in scatter.into_iter() {
+            let SpawnEntry { rules, .. } = SpawnEntry::from(entry);
+            for pack in rules {
+                let Pack { groups, .. } = pack;
+                for group in &groups {
+                    println!("{}:", entry);
+                    let (_, (_, _, asset)) = group;
+                    let dummy_pos = Vec3::new(0.0, 0.0, 0.0);
+                    let entity = EntityInfo::at(dummy_pos).with_asset_expect(asset);
+                    std::mem::drop(entity);
+                }
+            }
+        }
+    }
+
+    // Checks that group distribution has valid form
+    #[test]
+    fn test_group_choose() {
+        let scatter = wildlife_spawn_manifest();
+        for (entry, _) in scatter.into_iter() {
+            let SpawnEntry { rules, .. } = SpawnEntry::from(entry);
+            for pack in rules {
+                let Pack { groups, .. } = pack;
+                let dynamic_rng = &mut rand::thread_rng();
+                let _ = groups
+                    .choose_weighted(dynamic_rng, |(p, _group)| *p)
+                    .unwrap_or_else(|err| {
+                        panic!("{}: Failed to choose random group. Err: {}", entry, err)
+                    });
+            }
+        }
+    }
+}

From b74dd21d3bb8b394d0e4d13539843face0f418f2 Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Sat, 10 Jul 2021 21:44:53 +0300
Subject: [PATCH 04/12] Merge None-loot in EntityConfig to LootKind

---
 .../common/entity/dungeon/fallback/boss.ron   |  2 +-
 .../common/entity/dungeon/fallback/enemy.ron  |  2 +-
 .../entity/dungeon/fallback/miniboss.ron      |  2 +-
 assets/common/entity/dungeon/tier-0/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-0/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-0/miniboss.ron |  2 +-
 assets/common/entity/dungeon/tier-0/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-0/staff.ron |  2 +-
 assets/common/entity/dungeon/tier-1/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-1/bow.ron   |  2 +-
 assets/common/entity/dungeon/tier-1/rat.ron   |  2 +-
 assets/common/entity/dungeon/tier-1/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-1/staff.ron |  2 +-
 assets/common/entity/dungeon/tier-2/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-2/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-2/hakulaq.ron  |  2 +-
 assets/common/entity/dungeon/tier-2/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-2/staff.ron |  2 +-
 .../entity/dungeon/tier-3/bonerattler.ron     |  2 +-
 assets/common/entity/dungeon/tier-3/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-3/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-3/sentry.ron   |  2 +-
 assets/common/entity/dungeon/tier-3/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-3/staff.ron |  2 +-
 assets/common/entity/dungeon/tier-4/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-4/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-4/miniboss.ron |  2 +-
 assets/common/entity/dungeon/tier-4/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-4/staff.ron |  2 +-
 .../entity/dungeon/tier-5/beastmaster.ron     |  2 +-
 assets/common/entity/dungeon/tier-5/boss.ron  |  2 +-
 .../common/entity/dungeon/tier-5/cultist.ron  |  2 +-
 assets/common/entity/dungeon/tier-5/hound.ron |  2 +-
 assets/common/entity/dungeon/tier-5/husk.ron  |  2 +-
 .../entity/dungeon/tier-5/husk_brute.ron      |  2 +-
 .../common/entity/dungeon/tier-5/turret.ron   |  2 +-
 .../common/entity/dungeon/tier-5/warlock.ron  |  2 +-
 .../common/entity/dungeon/tier-5/warlord.ron  |  2 +-
 assets/common/entity/test.ron                 |  3 +-
 assets/common/entity/village/dummy.ron        |  2 +-
 assets/common/entity/village/guard.ron        |  2 +-
 assets/common/entity/village/merchant.ron     |  2 +-
 assets/common/entity/village/villager.ron     |  2 +-
 .../entity/wild/aggressive/dreadhorn.ron      |  2 +-
 .../entity/wild/aggressive/frostfang.ron      |  2 +-
 .../common/entity/wild/aggressive/grolgar.ron |  2 +-
 .../common/entity/wild/aggressive/mammoth.ron |  2 +-
 .../entity/wild/aggressive/mountain_troll.ron |  2 +-
 .../entity/wild/aggressive/rocksnapper.ron    |  2 +-
 .../entity/wild/aggressive/roshwalr.ron       |  2 +-
 .../entity/wild/aggressive/snow_leopard.ron   |  2 +-
 .../entity/wild/aggressive/snow_raptor.ron    |  2 +-
 .../common/entity/wild/aggressive/wendigo.ron |  2 +-
 assets/common/entity/wild/aggressive/wolf.ron |  2 +-
 assets/common/entity/wild/aggressive/yale.ron |  2 +-
 assets/common/entity/wild/peaceful/alpaca.ron |  2 +-
 .../entity/wild/peaceful/arctic_fox.ron       |  2 +-
 .../entity/wild/peaceful/arctic_hare.ron      |  2 +-
 assets/common/entity/wild/peaceful/eagle.ron  |  2 +-
 .../common/entity/wild/peaceful/highland.ron  |  2 +-
 assets/common/entity/wild/peaceful/llama.ron  |  2 +-
 assets/common/entity/wild/peaceful/moose.ron  |  2 +-
 .../common/entity/wild/peaceful/mouflon.ron   |  2 +-
 assets/common/entity/wild/peaceful/owl.ron    |  2 +-
 .../common/entity/wild/peaceful/tuskram.ron   |  2 +-
 assets/common/entity/wild/peaceful/yak.ron    |  2 +-
 common/src/generation.rs                      | 47 ++++++++++---------
 world/src/layer/wildlife.rs                   | 32 +++++--------
 68 files changed, 104 insertions(+), 108 deletions(-)

diff --git a/assets/common/entity/dungeon/fallback/boss.ron b/assets/common/entity/dungeon/fallback/boss.ron
index 002479e311..4429bb7012 100644
--- a/assets/common/entity/dungeon/fallback/boss.ron
+++ b/assets/common/entity/dungeon/fallback/boss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("sheep"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.fallback")),
+    loot: LootTable("common.loot_tables.fallback"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/fallback/enemy.ron b/assets/common/entity/dungeon/fallback/enemy.ron
index 71d2483b36..4580f95f60 100644
--- a/assets/common/entity/dungeon/fallback/enemy.ron
+++ b/assets/common/entity/dungeon/fallback/enemy.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("humanoid"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.fallback")),
+    loot: LootTable("common.loot_tables.fallback"),
 
     main_tool: Some(Choice([
         (1.0, Some(Item("common.items.weapons.tool.broom"))),
diff --git a/assets/common/entity/dungeon/fallback/miniboss.ron b/assets/common/entity/dungeon/fallback/miniboss.ron
index dfeb34cb24..f770fd6386 100644
--- a/assets/common/entity/dungeon/fallback/miniboss.ron
+++ b/assets/common/entity/dungeon/fallback/miniboss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("goose"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.fallback")),
+    loot: LootTable("common.loot_tables.fallback"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-0/boss.ron b/assets/common/entity/dungeon/tier-0/boss.ron
index 04f32f3935..b1335d2505 100644
--- a/assets/common/entity/dungeon/tier-0/boss.ron
+++ b/assets/common/entity/dungeon/tier-0/boss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("harvester"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-0.boss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-0.boss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron
index a26c225b74..1efc251451 100644
--- a/assets/common/entity/dungeon/tier-0/bow.ron
+++ b/assets/common/entity/dungeon/tier-0/bow.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("gnarling"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-0/miniboss.ron b/assets/common/entity/dungeon/tier-0/miniboss.ron
index 8f091f4065..56025c4a17 100644
--- a/assets/common/entity/dungeon/tier-0/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-0/miniboss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("deadwood"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-0.miniboss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-0.miniboss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron
index df95fc9982..dfd50cb6b7 100644
--- a/assets/common/entity/dungeon/tier-0/spear.ron
+++ b/assets/common/entity/dungeon/tier-0/spear.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("gnarling"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron
index 593e7a38f6..37de7ce3e3 100644
--- a/assets/common/entity/dungeon/tier-0/staff.ron
+++ b/assets/common/entity/dungeon/tier-0/staff.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("gnarling"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/tier-1/boss.ron
index 3dfcbfca13..f6ba0c0c60 100644
--- a/assets/common/entity/dungeon/tier-1/boss.ron
+++ b/assets/common/entity/dungeon/tier-1/boss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("yeti"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-1.boss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-1.boss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron
index cf8a1e7d82..5c00765f1b 100644
--- a/assets/common/entity/dungeon/tier-1/bow.ron
+++ b/assets/common/entity/dungeon/tier-1/bow.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("adlet"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron
index 0cfae2fa49..f198855733 100644
--- a/assets/common/entity/dungeon/tier-1/rat.ron
+++ b/assets/common/entity/dungeon/tier-1/rat.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("rat"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.creature.quad_small.generic")),
+    loot: LootTable("common.loot_tables.creature.quad_small.generic"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron
index 7bfd55d539..27a708fe5f 100644
--- a/assets/common/entity/dungeon/tier-1/spear.ron
+++ b/assets/common/entity/dungeon/tier-1/spear.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("adlet"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron
index c0bcc443a2..234165085c 100644
--- a/assets/common/entity/dungeon/tier-1/staff.ron
+++ b/assets/common/entity/dungeon/tier-1/staff.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("adlet"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-2/boss.ron b/assets/common/entity/dungeon/tier-2/boss.ron
index 661c35dbd2..17b3759a13 100644
--- a/assets/common/entity/dungeon/tier-2/boss.ron
+++ b/assets/common/entity/dungeon/tier-2/boss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("tidalwarrior"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-2.boss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-2.boss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron
index 5a2803862e..0679c25e90 100644
--- a/assets/common/entity/dungeon/tier-2/bow.ron
+++ b/assets/common/entity/dungeon/tier-2/bow.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("sahagin"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-2.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-2/hakulaq.ron b/assets/common/entity/dungeon/tier-2/hakulaq.ron
index 0b42e2968b..605a9d8abe 100644
--- a/assets/common/entity/dungeon/tier-2/hakulaq.ron
+++ b/assets/common/entity/dungeon/tier-2/hakulaq.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("hakulaq"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.creature.quad_low.fanged")),
+    loot: LootTable("common.loot_tables.creature.quad_low.fanged"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron
index 43c4d19381..5391c5b491 100644
--- a/assets/common/entity/dungeon/tier-2/spear.ron
+++ b/assets/common/entity/dungeon/tier-2/spear.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("sahagin"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-2.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron
index 889d656483..5f5d5282ea 100644
--- a/assets/common/entity/dungeon/tier-2/staff.ron
+++ b/assets/common/entity/dungeon/tier-2/staff.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("sahagin"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-2.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-3/bonerattler.ron b/assets/common/entity/dungeon/tier-3/bonerattler.ron
index 6d53d539fb..9fad589960 100644
--- a/assets/common/entity/dungeon/tier-3/bonerattler.ron
+++ b/assets/common/entity/dungeon/tier-3/bonerattler.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("bonerattler"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.creature.quad_medium.carapace")),
+    loot: LootTable("common.loot_tables.creature.quad_medium.carapace"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-3/boss.ron b/assets/common/entity/dungeon/tier-3/boss.ron
index 6b86a0ff60..47b854163e 100644
--- a/assets/common/entity/dungeon/tier-3/boss.ron
+++ b/assets/common/entity/dungeon/tier-3/boss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("claygolem"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-3.boss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-3.boss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron
index b5550656ac..1ccb4539de 100644
--- a/assets/common/entity/dungeon/tier-3/bow.ron
+++ b/assets/common/entity/dungeon/tier-3/bow.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("haniwa"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-3.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-3/sentry.ron b/assets/common/entity/dungeon/tier-3/sentry.ron
index 90dd422e20..3dd3a9abd2 100644
--- a/assets/common/entity/dungeon/tier-3/sentry.ron
+++ b/assets/common/entity/dungeon/tier-3/sentry.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: Exact(Object(HaniwaSentry)),
     alignment: Alignment(Enemy),
 
-    loot: Some(Item("common.items.crafting_ing.stones")),
+    loot: Item("common.items.crafting_ing.stones"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron
index 0fa6541dc0..b4ccb3cde7 100644
--- a/assets/common/entity/dungeon/tier-3/spear.ron
+++ b/assets/common/entity/dungeon/tier-3/spear.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("haniwa"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-3.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron
index c2450d26d6..cc9ce0d9de 100644
--- a/assets/common/entity/dungeon/tier-3/staff.ron
+++ b/assets/common/entity/dungeon/tier-3/staff.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("haniwa"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-3.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-4/boss.ron b/assets/common/entity/dungeon/tier-4/boss.ron
index 756eb9c71e..31fc9f38cf 100644
--- a/assets/common/entity/dungeon/tier-4/boss.ron
+++ b/assets/common/entity/dungeon/tier-4/boss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("minotaur"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-4.boss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-4.boss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron
index 14b7719c2b..4804cccdf1 100644
--- a/assets/common/entity/dungeon/tier-4/bow.ron
+++ b/assets/common/entity/dungeon/tier-4/bow.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("myrmidon"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-4.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-4/miniboss.ron b/assets/common/entity/dungeon/tier-4/miniboss.ron
index 30446ac213..c095ccd698 100644
--- a/assets/common/entity/dungeon/tier-4/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-4/miniboss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("dullahan"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-4.miniboss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-4.miniboss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron
index 5cb98458c3..a53fdcaa4c 100644
--- a/assets/common/entity/dungeon/tier-4/spear.ron
+++ b/assets/common/entity/dungeon/tier-4/spear.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("myrmidon"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-4.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron
index 1f26828f6b..45d18e6528 100644
--- a/assets/common/entity/dungeon/tier-4/staff.ron
+++ b/assets/common/entity/dungeon/tier-4/staff.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("myrmidon"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-4.enemy"),
 
     main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")),
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron
index 4c1abf5318..48eebf85ac 100644
--- a/assets/common/entity/dungeon/tier-5/beastmaster.ron
+++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("humanoid"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.miniboss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.miniboss"),
 
     main_tool: Some(Choice([
         (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))),
diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron
index 18557c26a3..63380f61fe 100644
--- a/assets/common/entity/dungeon/tier-5/boss.ron
+++ b/assets/common/entity/dungeon/tier-5/boss.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("mindflayer"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.boss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.boss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron
index 3b8097244e..052a994552 100644
--- a/assets/common/entity/dungeon/tier-5/cultist.ron
+++ b/assets/common/entity/dungeon/tier-5/cultist.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("humanoid"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.enemy"),
 
     main_tool: Some(Choice([
         (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))),
diff --git a/assets/common/entity/dungeon/tier-5/hound.ron b/assets/common/entity/dungeon/tier-5/hound.ron
index f8116062fe..22f78ce8b6 100644
--- a/assets/common/entity/dungeon/tier-5/hound.ron
+++ b/assets/common/entity/dungeon/tier-5/hound.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("darkhound"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.minion"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron
index 54aba3f90c..4beae402bb 100644
--- a/assets/common/entity/dungeon/tier-5/husk.ron
+++ b/assets/common/entity/dungeon/tier-5/husk.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("husk"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.minion"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-5/husk_brute.ron b/assets/common/entity/dungeon/tier-5/husk_brute.ron
index ec39247f96..ebae08f07c 100644
--- a/assets/common/entity/dungeon/tier-5/husk_brute.ron
+++ b/assets/common/entity/dungeon/tier-5/husk_brute.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("husk_brute"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.miniboss")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.miniboss"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-5/turret.ron b/assets/common/entity/dungeon/tier-5/turret.ron
index 7834953f58..e02dbc097d 100644
--- a/assets/common/entity/dungeon/tier-5/turret.ron
+++ b/assets/common/entity/dungeon/tier-5/turret.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: Exact(Object(Crossbow)),
     alignment: Alignment(Enemy),
 
-    loot: Some(Item("common.items.crafting_ing.twigs")),
+    loot: Item("common.items.crafting_ing.twigs"),
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron
index 53f2ef16bd..527fb4c4f3 100644
--- a/assets/common/entity/dungeon/tier-5/warlock.ron
+++ b/assets/common/entity/dungeon/tier-5/warlock.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("cultist_warlock"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.enemy"),
 
     main_tool: Some(Choice([
         (1.5, Some(Item("common.items.npc_weapons.staff.bipedlarge-cultist"))),
diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron
index be50ee1aa8..9b6d616c85 100644
--- a/assets/common/entity/dungeon/tier-5/warlord.ron
+++ b/assets/common/entity/dungeon/tier-5/warlord.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("cultist_warlord"),
     alignment: Alignment(Enemy),
 
-    loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")),
+    loot: LootTable("common.loot_tables.dungeon.tier-5.enemy"),
 
     main_tool: Some(Choice([
         (1.5, Some(Item("common.items.npc_weapons.sword.bipedlarge-cultist"))),
diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron
index b24d5e713d..235a009e7d 100644
--- a/assets/common/entity/test.ron
+++ b/assets/common/entity/test.ron
@@ -27,7 +27,8 @@ EntityConfig (
     /// Loot
     /// Can be Item (with asset_specifier for item)
     /// or LootTable (with asset_specifier for loot table)
-    loot: Some(LootTable("common.loot_tables.humanoids")),
+    /// or Uninit (means it should be specified something in the code)
+    loot: LootTable("common.loot_tables.humanoids"),
 
     /// Meta Info (level, alignment, agency, etc)
     // meta: {},
diff --git a/assets/common/entity/village/dummy.ron b/assets/common/entity/village/dummy.ron
index ca958ede04..8d23eb244b 100644
--- a/assets/common/entity/village/dummy.ron
+++ b/assets/common/entity/village/dummy.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: Exact(Object(TrainingDummy)),
     alignment: Alignment(Passive),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron
index dc642b2a81..462137ae0d 100644
--- a/assets/common/entity/village/guard.ron
+++ b/assets/common/entity/village/guard.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("humanoid"),
     alignment: Alignment(Npc),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: Some(Item("common.items.weapons.sword.iron-4")),
     second_tool: None,
diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron
index 74491b729e..ec1c8cdd18 100644
--- a/assets/common/entity/village/merchant.ron
+++ b/assets/common/entity/village/merchant.ron
@@ -4,7 +4,7 @@ EntityConfig (
     alignment: Alignment(Npc),
 
     // considering giving some gold/gems/materials?
-    loot: None,
+    loot: Uninit,
 
     main_tool: Some(Item("common.items.weapons.bow.eldwood-0")),
     second_tool: None,
diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron
index b1bac72a82..a95985694a 100644
--- a/assets/common/entity/village/villager.ron
+++ b/assets/common/entity/village/villager.ron
@@ -4,7 +4,7 @@ EntityConfig (
     body: RandomWith("humanoid"),
     alignment: Alignment(Npc),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: Some(Choice([
         (1.0, Some(Item("common.items.weapons.tool.broom"))),
diff --git a/assets/common/entity/wild/aggressive/dreadhorn.ron b/assets/common/entity/wild/aggressive/dreadhorn.ron
index bf65e87d08..76f7d1ce23 100644
--- a/assets/common/entity/wild/aggressive/dreadhorn.ron
+++ b/assets/common/entity/wild/aggressive/dreadhorn.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("dreadhorn"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/frostfang.ron b/assets/common/entity/wild/aggressive/frostfang.ron
index 3335604852..6436fed49e 100644
--- a/assets/common/entity/wild/aggressive/frostfang.ron
+++ b/assets/common/entity/wild/aggressive/frostfang.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("frostfang"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/grolgar.ron b/assets/common/entity/wild/aggressive/grolgar.ron
index 07912bb204..4fd0e2be04 100644
--- a/assets/common/entity/wild/aggressive/grolgar.ron
+++ b/assets/common/entity/wild/aggressive/grolgar.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("grolgar"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/mammoth.ron b/assets/common/entity/wild/aggressive/mammoth.ron
index 24a8321cc9..9fb0e20309 100644
--- a/assets/common/entity/wild/aggressive/mammoth.ron
+++ b/assets/common/entity/wild/aggressive/mammoth.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("mammoth"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/mountain_troll.ron b/assets/common/entity/wild/aggressive/mountain_troll.ron
index ca127f45e9..5014b134be 100644
--- a/assets/common/entity/wild/aggressive/mountain_troll.ron
+++ b/assets/common/entity/wild/aggressive/mountain_troll.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("troll_mountain"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/rocksnapper.ron b/assets/common/entity/wild/aggressive/rocksnapper.ron
index 43f6fbfcfa..da2e112efe 100644
--- a/assets/common/entity/wild/aggressive/rocksnapper.ron
+++ b/assets/common/entity/wild/aggressive/rocksnapper.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("rocksnapper"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/roshwalr.ron b/assets/common/entity/wild/aggressive/roshwalr.ron
index de322d8653..c237f9a82b 100644
--- a/assets/common/entity/wild/aggressive/roshwalr.ron
+++ b/assets/common/entity/wild/aggressive/roshwalr.ron
@@ -6,7 +6,7 @@ EntityConfig (
     ))),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/snow_leopard.ron b/assets/common/entity/wild/aggressive/snow_leopard.ron
index 2d6e376a12..75678d667d 100644
--- a/assets/common/entity/wild/aggressive/snow_leopard.ron
+++ b/assets/common/entity/wild/aggressive/snow_leopard.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("snowleopard"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/snow_raptor.ron b/assets/common/entity/wild/aggressive/snow_raptor.ron
index 8c07519312..5849d9852e 100644
--- a/assets/common/entity/wild/aggressive/snow_raptor.ron
+++ b/assets/common/entity/wild/aggressive/snow_raptor.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("raptor_snow"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/wendigo.ron b/assets/common/entity/wild/aggressive/wendigo.ron
index a655f63a9e..9a49ed4731 100644
--- a/assets/common/entity/wild/aggressive/wendigo.ron
+++ b/assets/common/entity/wild/aggressive/wendigo.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("wendigo"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/wolf.ron b/assets/common/entity/wild/aggressive/wolf.ron
index 4d0d3de3dd..4624d2bc42 100644
--- a/assets/common/entity/wild/aggressive/wolf.ron
+++ b/assets/common/entity/wild/aggressive/wolf.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("wolf"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/aggressive/yale.ron b/assets/common/entity/wild/aggressive/yale.ron
index f0e64cb936..441492c463 100644
--- a/assets/common/entity/wild/aggressive/yale.ron
+++ b/assets/common/entity/wild/aggressive/yale.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("yale"),
     alignment: Alignment(Enemy),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/alpaca.ron b/assets/common/entity/wild/peaceful/alpaca.ron
index aca63d07d7..0a6567c036 100644
--- a/assets/common/entity/wild/peaceful/alpaca.ron
+++ b/assets/common/entity/wild/peaceful/alpaca.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("alpaca"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/arctic_fox.ron b/assets/common/entity/wild/peaceful/arctic_fox.ron
index 986c771aea..4380e983ea 100644
--- a/assets/common/entity/wild/peaceful/arctic_fox.ron
+++ b/assets/common/entity/wild/peaceful/arctic_fox.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: Exact(QuadrupedSmall(Body(species: Fox, body_type: Female))),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/arctic_hare.ron b/assets/common/entity/wild/peaceful/arctic_hare.ron
index b839a7c4de..ca398541b3 100644
--- a/assets/common/entity/wild/peaceful/arctic_hare.ron
+++ b/assets/common/entity/wild/peaceful/arctic_hare.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: Exact(QuadrupedSmall(Body(species: Hare, body_type: Female))),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/eagle.ron b/assets/common/entity/wild/peaceful/eagle.ron
index debf5ce6f5..a010819196 100644
--- a/assets/common/entity/wild/peaceful/eagle.ron
+++ b/assets/common/entity/wild/peaceful/eagle.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("eagle"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/highland.ron b/assets/common/entity/wild/peaceful/highland.ron
index 36baa5539a..a7e26a8b8d 100644
--- a/assets/common/entity/wild/peaceful/highland.ron
+++ b/assets/common/entity/wild/peaceful/highland.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("highland"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/llama.ron b/assets/common/entity/wild/peaceful/llama.ron
index 1a92cced90..697dabef77 100644
--- a/assets/common/entity/wild/peaceful/llama.ron
+++ b/assets/common/entity/wild/peaceful/llama.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("llama"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/moose.ron b/assets/common/entity/wild/peaceful/moose.ron
index 1475c586aa..be44787e2d 100644
--- a/assets/common/entity/wild/peaceful/moose.ron
+++ b/assets/common/entity/wild/peaceful/moose.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("moose"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/mouflon.ron b/assets/common/entity/wild/peaceful/mouflon.ron
index 1788f8f9de..f72e24bfab 100644
--- a/assets/common/entity/wild/peaceful/mouflon.ron
+++ b/assets/common/entity/wild/peaceful/mouflon.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("mouflon"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/owl.ron b/assets/common/entity/wild/peaceful/owl.ron
index bcc12c8e64..a8df3ba91c 100644
--- a/assets/common/entity/wild/peaceful/owl.ron
+++ b/assets/common/entity/wild/peaceful/owl.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("owl"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/tuskram.ron b/assets/common/entity/wild/peaceful/tuskram.ron
index ffcadff2a3..41cd8a0a0d 100644
--- a/assets/common/entity/wild/peaceful/tuskram.ron
+++ b/assets/common/entity/wild/peaceful/tuskram.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("tuskram"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/assets/common/entity/wild/peaceful/yak.ron b/assets/common/entity/wild/peaceful/yak.ron
index 58988f2ae8..788209ae65 100644
--- a/assets/common/entity/wild/peaceful/yak.ron
+++ b/assets/common/entity/wild/peaceful/yak.ron
@@ -3,7 +3,7 @@ EntityConfig (
     body: RandomWith("yak"),
     alignment: Alignment(Wild),
 
-    loot: None,
+    loot: Uninit,
 
     main_tool: None,
     second_tool: None,
diff --git a/common/src/generation.rs b/common/src/generation.rs
index 8f856553d4..fcbd0f2ecd 100644
--- a/common/src/generation.rs
+++ b/common/src/generation.rs
@@ -24,6 +24,7 @@ enum BodyBuilder {
 enum LootKind {
     Item(String),
     LootTable(String),
+    Uninit,
 }
 
 #[derive(Debug, Deserialize, Clone)]
@@ -37,9 +38,11 @@ struct EntityConfig {
     name: Option<String>,
     body: BodyBuilder,
     alignment: AlignmentMark,
-    loot: Option<LootKind>,
+    loot: LootKind,
+    // TODO: replace with `hands` field to support one 2h weapon/ pair 1h weapons
     main_tool: Option<ItemSpec>,
     second_tool: Option<ItemSpec>,
+    // Meta fields
     loadout_asset: Option<String>,
     skillset_asset: Option<String>,
 }
@@ -141,17 +144,16 @@ impl EntityInfo {
             self = self.with_alignment(alignment);
         }
 
-        if let Some(loot) = loot {
-            match loot {
-                LootKind::Item(asset) => {
-                    self = self.with_loot_drop(Item::new_from_asset_expect(&asset));
-                },
-                LootKind::LootTable(asset) => {
-                    let table = Lottery::<LootSpec<String>>::load_expect(&asset);
-                    let drop = table.read().choose().to_item();
-                    self = self.with_loot_drop(drop);
-                },
-            }
+        match loot {
+            LootKind::Item(asset) => {
+                self = self.with_loot_drop(Item::new_from_asset_expect(&asset));
+            },
+            LootKind::LootTable(asset) => {
+                let table = Lottery::<LootSpec<String>>::load_expect(&asset);
+                let drop = table.read().choose().to_item();
+                self = self.with_loot_drop(drop);
+            },
+            LootKind::Uninit => {},
         }
 
         let rng = &mut rand::thread_rng();
@@ -360,17 +362,16 @@ mod tests {
                 BodyBuilder::Exact { .. } | BodyBuilder::Uninit => {},
             }
 
-            if let Some(loot) = loot {
-                match loot {
-                    LootKind::Item(asset) => {
-                        std::mem::drop(Item::new_from_asset_expect(&asset));
-                    },
-                    LootKind::LootTable(asset) => {
-                        // we need to just load it check if it exists,
-                        // because all loot tables are tested in Lottery module
-                        let _ = Lottery::<LootSpec<String>>::load_expect(&asset);
-                    },
-                }
+            match loot {
+                LootKind::Item(asset) => {
+                    std::mem::drop(Item::new_from_asset_expect(&asset));
+                },
+                LootKind::LootTable(asset) => {
+                    // we need to just load it check if it exists,
+                    // because all loot tables are tested in Lottery module
+                    let _ = Lottery::<LootSpec<String>>::load_expect(&asset);
+                },
+                LootKind::Uninit => {},
             }
 
             if let Some(loadout_asset) = loadout_asset {
diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs
index e94b43f091..ad81f61fa4 100644
--- a/world/src/layer/wildlife.rs
+++ b/world/src/layer/wildlife.rs
@@ -1,19 +1,15 @@
 use crate::{column::ColumnSample, sim::SimChunk, IndexRef, CONFIG};
 use common::{
     assets::{self, AssetExt},
-    comp::{
-        fish_medium, fish_small, quadruped_low, quadruped_medium, quadruped_small, theropod,
-        Alignment,
-    },
     generation::{ChunkSupplement, EntityInfo},
     resources::TimeOfDay,
     terrain::Block,
-    time::DayPeriod::{self, Evening, Morning, Night, Noon},
+    time::DayPeriod,
     vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
 };
 use rand::prelude::*;
 use serde::Deserialize;
-use std::{f32, ops::Range};
+use std::f32;
 use vek::*;
 
 fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
@@ -172,7 +168,7 @@ pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
                 }
         }),
         // River animals
-        ("world.wildlife.spawn.tropical.river", |c_, col| {
+        ("world.wildlife.spawn.tropical.river", |_c, col| {
             close(col.temp, CONFIG.tropical_temp, 0.5)
                 * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
                     0.001
@@ -181,15 +177,12 @@ pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
                 }
         }),
         // Rainforest area animals
-        (
-            "world.wildlife.spawn.tropical.rainforest_area",
-            |c, _col| {
-                close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
-                    * close(c.humidity, CONFIG.desert_hum, 0.4)
-                    * BASE_DENSITY
-                    * 2.0
-            },
-        ),
+        ("world.wildlife.spawn.tropical.rainforest", |c, _col| {
+            close(c.temp, CONFIG.tropical_temp + 0.1, 0.4)
+                * close(c.humidity, CONFIG.desert_hum, 0.4)
+                * BASE_DENSITY
+                * 2.0
+        }),
         // Rock animals
         ("world.wildlife.spawn.tropical.rock", |c, col| {
             close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock * BASE_DENSITY * 5.0
@@ -202,7 +195,7 @@ pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
                 * BASE_DENSITY
                 * 0.8
         }),
-        // Hot area animals
+        // Wasteland animals
         ("world.wildlife.spawn.desert.wasteland", |c, _col| {
             close(c.temp, CONFIG.desert_temp + 0.2, 0.3)
                 * close(c.humidity, CONFIG.desert_hum, 0.5)
@@ -210,7 +203,7 @@ pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
                 * 1.3
         }),
         // River animals
-        ("world.wildlife.spawn.desert.river", |_c, _col| {
+        ("world.wildlife.spawn.desert.river", |_c, col| {
             close(col.temp, CONFIG.desert_temp + 0.2, 0.3)
                 * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) {
                     0.0001
@@ -218,6 +211,7 @@ pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
                     0.0
                 }
         }),
+        // Hot area desert
         ("world.wildlife.spawn.desert.hot", |c, _col| {
             close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 3.8
         }),
@@ -264,7 +258,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
             if let Some(time) = time {
                 current_day_period = DayPeriod::from(time.0)
             } else {
-                current_day_period = Noon
+                current_day_period = DayPeriod::Noon
             }
 
             let entity_group = scatter

From 335dbf4cc8257d3fba7c217d460a32f2e3bc50ed Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Sat, 10 Jul 2021 23:51:29 +0300
Subject: [PATCH 05/12] Move builder assets in EntityConfig to meta field

---
 .../common/entity/dungeon/fallback/boss.ron   |  3 +-
 .../common/entity/dungeon/fallback/enemy.ron  |  3 +-
 .../entity/dungeon/fallback/miniboss.ron      |  3 +-
 assets/common/entity/dungeon/tier-0/boss.ron  |  3 +-
 assets/common/entity/dungeon/tier-0/bow.ron   |  6 +-
 .../common/entity/dungeon/tier-0/miniboss.ron |  3 +-
 assets/common/entity/dungeon/tier-0/spear.ron |  5 +-
 assets/common/entity/dungeon/tier-0/staff.ron |  5 +-
 assets/common/entity/dungeon/tier-1/boss.ron  |  3 +-
 assets/common/entity/dungeon/tier-1/bow.ron   |  6 +-
 assets/common/entity/dungeon/tier-1/rat.ron   |  3 +-
 assets/common/entity/dungeon/tier-1/spear.ron |  5 +-
 assets/common/entity/dungeon/tier-1/staff.ron |  5 +-
 assets/common/entity/dungeon/tier-2/boss.ron  |  3 +-
 assets/common/entity/dungeon/tier-2/bow.ron   |  6 +-
 .../common/entity/dungeon/tier-2/hakulaq.ron  |  3 +-
 assets/common/entity/dungeon/tier-2/spear.ron |  5 +-
 assets/common/entity/dungeon/tier-2/staff.ron |  5 +-
 .../entity/dungeon/tier-3/bonerattler.ron     |  3 +-
 assets/common/entity/dungeon/tier-3/boss.ron  |  3 +-
 assets/common/entity/dungeon/tier-3/bow.ron   |  6 +-
 .../common/entity/dungeon/tier-3/sentry.ron   |  3 +-
 assets/common/entity/dungeon/tier-3/spear.ron |  5 +-
 assets/common/entity/dungeon/tier-3/staff.ron |  5 +-
 assets/common/entity/dungeon/tier-4/boss.ron  |  3 +-
 assets/common/entity/dungeon/tier-4/bow.ron   |  6 +-
 .../common/entity/dungeon/tier-4/miniboss.ron |  3 +-
 assets/common/entity/dungeon/tier-4/spear.ron |  5 +-
 assets/common/entity/dungeon/tier-4/staff.ron |  5 +-
 .../entity/dungeon/tier-5/beastmaster.ron     |  8 ++-
 assets/common/entity/dungeon/tier-5/boss.ron  |  3 +-
 .../common/entity/dungeon/tier-5/cultist.ron  |  6 +-
 assets/common/entity/dungeon/tier-5/hound.ron |  3 +-
 assets/common/entity/dungeon/tier-5/husk.ron  |  5 +-
 .../entity/dungeon/tier-5/husk_brute.ron      |  3 +-
 .../common/entity/dungeon/tier-5/turret.ron   |  3 +-
 .../common/entity/dungeon/tier-5/warlock.ron  |  3 +-
 .../common/entity/dungeon/tier-5/warlord.ron  |  3 +-
 assets/common/entity/test.ron                 | 28 +++++-----
 assets/common/entity/village/dummy.ron        |  3 +-
 assets/common/entity/village/guard.ron        |  5 +-
 assets/common/entity/village/merchant.ron     |  5 +-
 assets/common/entity/village/villager.ron     |  3 +-
 .../entity/wild/aggressive/dreadhorn.ron      |  3 +-
 .../entity/wild/aggressive/frostfang.ron      |  3 +-
 .../common/entity/wild/aggressive/grolgar.ron |  3 +-
 .../common/entity/wild/aggressive/mammoth.ron |  3 +-
 .../entity/wild/aggressive/mountain_troll.ron |  3 +-
 .../entity/wild/aggressive/rocksnapper.ron    |  3 +-
 .../entity/wild/aggressive/roshwalr.ron       |  3 +-
 .../entity/wild/aggressive/snow_leopard.ron   |  3 +-
 .../entity/wild/aggressive/snow_raptor.ron    |  3 +-
 .../common/entity/wild/aggressive/wendigo.ron |  3 +-
 assets/common/entity/wild/aggressive/wolf.ron |  3 +-
 assets/common/entity/wild/aggressive/yale.ron |  3 +-
 assets/common/entity/wild/peaceful/alpaca.ron |  3 +-
 .../entity/wild/peaceful/arctic_fox.ron       |  3 +-
 .../entity/wild/peaceful/arctic_hare.ron      |  3 +-
 assets/common/entity/wild/peaceful/eagle.ron  |  3 +-
 .../common/entity/wild/peaceful/highland.ron  |  3 +-
 assets/common/entity/wild/peaceful/llama.ron  |  3 +-
 assets/common/entity/wild/peaceful/moose.ron  |  3 +-
 .../common/entity/wild/peaceful/mouflon.ron   |  3 +-
 assets/common/entity/wild/peaceful/owl.ron    |  3 +-
 .../common/entity/wild/peaceful/tuskram.ron   |  3 +-
 assets/common/entity/wild/peaceful/yak.ron    |  3 +-
 common/src/generation.rs                      | 55 +++++++++++--------
 67 files changed, 159 insertions(+), 168 deletions(-)

diff --git a/assets/common/entity/dungeon/fallback/boss.ron b/assets/common/entity/dungeon/fallback/boss.ron
index 4429bb7012..cfda6d5ee3 100644
--- a/assets/common/entity/dungeon/fallback/boss.ron
+++ b/assets/common/entity/dungeon/fallback/boss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/fallback/enemy.ron b/assets/common/entity/dungeon/fallback/enemy.ron
index 4580f95f60..22dea6881a 100644
--- a/assets/common/entity/dungeon/fallback/enemy.ron
+++ b/assets/common/entity/dungeon/fallback/enemy.ron
@@ -16,6 +16,5 @@ EntityConfig (
     ])),
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/fallback/miniboss.ron b/assets/common/entity/dungeon/fallback/miniboss.ron
index f770fd6386..018dcfbb6e 100644
--- a/assets/common/entity/dungeon/fallback/miniboss.ron
+++ b/assets/common/entity/dungeon/fallback/miniboss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-0/boss.ron b/assets/common/entity/dungeon/tier-0/boss.ron
index b1335d2505..c45b913721 100644
--- a/assets/common/entity/dungeon/tier-0/boss.ron
+++ b/assets/common/entity/dungeon/tier-0/boss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron
index 1efc251451..362cf82734 100644
--- a/assets/common/entity/dungeon/tier-0/bow.ron
+++ b/assets/common/entity/dungeon/tier-0/bow.ron
@@ -8,6 +8,8 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"),
-    skillset_asset: Some("common.skillset.dungeon.tier-0.bow"),
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-0.gnarling"),
+        SkillSetAsset("common.skillset.dungeon.tier-0.bow"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-0/miniboss.ron b/assets/common/entity/dungeon/tier-0/miniboss.ron
index 56025c4a17..99ebb6165c 100644
--- a/assets/common/entity/dungeon/tier-0/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-0/miniboss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron
index dfd50cb6b7..ade46b767d 100644
--- a/assets/common/entity/dungeon/tier-0/spear.ron
+++ b/assets/common/entity/dungeon/tier-0/spear.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-0.gnarling"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron
index 37de7ce3e3..8c855b9e70 100644
--- a/assets/common/entity/dungeon/tier-0/staff.ron
+++ b/assets/common/entity/dungeon/tier-0/staff.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-0.gnarling"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/tier-1/boss.ron
index f6ba0c0c60..1ff096a52c 100644
--- a/assets/common/entity/dungeon/tier-1/boss.ron
+++ b/assets/common/entity/dungeon/tier-1/boss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron
index 5c00765f1b..ac3b28e5e9 100644
--- a/assets/common/entity/dungeon/tier-1/bow.ron
+++ b/assets/common/entity/dungeon/tier-1/bow.ron
@@ -8,6 +8,8 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_bow"),
-    skillset_asset: Some("common.skillset.dungeon.tier-1.bow"),
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-1.adlet_bow"),
+        SkillSetAsset("common.skillset.dungeon.tier-1.bow"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron
index f198855733..c2672e0f3c 100644
--- a/assets/common/entity/dungeon/tier-1/rat.ron
+++ b/assets/common/entity/dungeon/tier-1/rat.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron
index 27a708fe5f..f32e2021fa 100644
--- a/assets/common/entity/dungeon/tier-1/spear.ron
+++ b/assets/common/entity/dungeon/tier-1/spear.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_spear"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-1.adlet_spear"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron
index 234165085c..5591010069 100644
--- a/assets/common/entity/dungeon/tier-1/staff.ron
+++ b/assets/common/entity/dungeon/tier-1/staff.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_spear"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-1.adlet_spear"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-2/boss.ron b/assets/common/entity/dungeon/tier-2/boss.ron
index 17b3759a13..d24afd982f 100644
--- a/assets/common/entity/dungeon/tier-2/boss.ron
+++ b/assets/common/entity/dungeon/tier-2/boss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron
index 0679c25e90..e0cfe24086 100644
--- a/assets/common/entity/dungeon/tier-2/bow.ron
+++ b/assets/common/entity/dungeon/tier-2/bow.ron
@@ -8,6 +8,8 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"),
-    skillset_asset: Some("common.skillset.dungeon.tier-2.bow"),
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-2.sahagin"),
+        SkillSetAsset("common.skillset.dungeon.tier-2.bow"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-2/hakulaq.ron b/assets/common/entity/dungeon/tier-2/hakulaq.ron
index 605a9d8abe..9ef070ab67 100644
--- a/assets/common/entity/dungeon/tier-2/hakulaq.ron
+++ b/assets/common/entity/dungeon/tier-2/hakulaq.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron
index 5391c5b491..abc9b1b961 100644
--- a/assets/common/entity/dungeon/tier-2/spear.ron
+++ b/assets/common/entity/dungeon/tier-2/spear.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-2.sahagin"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron
index 5f5d5282ea..b13508dd1b 100644
--- a/assets/common/entity/dungeon/tier-2/staff.ron
+++ b/assets/common/entity/dungeon/tier-2/staff.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-2.sahagin"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-3/bonerattler.ron b/assets/common/entity/dungeon/tier-3/bonerattler.ron
index 9fad589960..ddbb8b5952 100644
--- a/assets/common/entity/dungeon/tier-3/bonerattler.ron
+++ b/assets/common/entity/dungeon/tier-3/bonerattler.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-3/boss.ron b/assets/common/entity/dungeon/tier-3/boss.ron
index 47b854163e..aea8d5cf91 100644
--- a/assets/common/entity/dungeon/tier-3/boss.ron
+++ b/assets/common/entity/dungeon/tier-3/boss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron
index 1ccb4539de..24ceaabafd 100644
--- a/assets/common/entity/dungeon/tier-3/bow.ron
+++ b/assets/common/entity/dungeon/tier-3/bow.ron
@@ -8,6 +8,8 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"),
-    skillset_asset: Some("common.skillset.dungeon.tier-3.bow"),
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-3.haniwa"),
+        SkillSetAsset("common.skillset.dungeon.tier-3.bow"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-3/sentry.ron b/assets/common/entity/dungeon/tier-3/sentry.ron
index 3dd3a9abd2..08cb3e73b7 100644
--- a/assets/common/entity/dungeon/tier-3/sentry.ron
+++ b/assets/common/entity/dungeon/tier-3/sentry.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron
index b4ccb3cde7..38bdb85f0f 100644
--- a/assets/common/entity/dungeon/tier-3/spear.ron
+++ b/assets/common/entity/dungeon/tier-3/spear.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-3.haniwa"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron
index cc9ce0d9de..85aa5ea5a1 100644
--- a/assets/common/entity/dungeon/tier-3/staff.ron
+++ b/assets/common/entity/dungeon/tier-3/staff.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-3.haniwa"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-4/boss.ron b/assets/common/entity/dungeon/tier-4/boss.ron
index 31fc9f38cf..c1f43f164c 100644
--- a/assets/common/entity/dungeon/tier-4/boss.ron
+++ b/assets/common/entity/dungeon/tier-4/boss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron
index 4804cccdf1..7e693009e0 100644
--- a/assets/common/entity/dungeon/tier-4/bow.ron
+++ b/assets/common/entity/dungeon/tier-4/bow.ron
@@ -8,6 +8,8 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"),
-    skillset_asset: Some("common.skillset.dungeon.tier-4.bow"),
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-4.myrmidon"),
+        SkillSetAsset("common.skillset.dungeon.tier-4.bow"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-4/miniboss.ron b/assets/common/entity/dungeon/tier-4/miniboss.ron
index c095ccd698..23fc8f7ae2 100644
--- a/assets/common/entity/dungeon/tier-4/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-4/miniboss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron
index a53fdcaa4c..d0f2335239 100644
--- a/assets/common/entity/dungeon/tier-4/spear.ron
+++ b/assets/common/entity/dungeon/tier-4/spear.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-4.myrmidon"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron
index 45d18e6528..9448b6e1a9 100644
--- a/assets/common/entity/dungeon/tier-4/staff.ron
+++ b/assets/common/entity/dungeon/tier-4/staff.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-4.myrmidon"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron
index 48eebf85ac..76d28d1746 100644
--- a/assets/common/entity/dungeon/tier-5/beastmaster.ron
+++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron
@@ -12,7 +12,9 @@ EntityConfig (
     ])),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-5.beastmaster"),
-    // TODO: make own skillset for him?
-    skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"),
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-5.beastmaster"),
+        // TODO: make own skillset for him?
+        SkillSetAsset("common.skillset.dungeon.tier-5.enemy"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron
index 63380f61fe..b2289aa240 100644
--- a/assets/common/entity/dungeon/tier-5/boss.ron
+++ b/assets/common/entity/dungeon/tier-5/boss.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron
index 052a994552..8743a20285 100644
--- a/assets/common/entity/dungeon/tier-5/cultist.ron
+++ b/assets/common/entity/dungeon/tier-5/cultist.ron
@@ -15,6 +15,8 @@ EntityConfig (
     ])),
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-5.cultist"),
-    skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"),
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-5.cultist"),
+        SkillSetAsset("common.skillset.dungeon.tier-5.enemy"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-5/hound.ron b/assets/common/entity/dungeon/tier-5/hound.ron
index 22f78ce8b6..81b37f01f4 100644
--- a/assets/common/entity/dungeon/tier-5/hound.ron
+++ b/assets/common/entity/dungeon/tier-5/hound.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron
index 4beae402bb..a7a00ab6a2 100644
--- a/assets/common/entity/dungeon/tier-5/husk.ron
+++ b/assets/common/entity/dungeon/tier-5/husk.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: Some("common.loadout.dungeon.tier-5.husk"),
-    skillset_asset: None,
+    meta: [
+        LoadoutAsset("common.loadout.dungeon.tier-5.husk"),
+    ],
 )
diff --git a/assets/common/entity/dungeon/tier-5/husk_brute.ron b/assets/common/entity/dungeon/tier-5/husk_brute.ron
index ebae08f07c..da3e55583c 100644
--- a/assets/common/entity/dungeon/tier-5/husk_brute.ron
+++ b/assets/common/entity/dungeon/tier-5/husk_brute.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/turret.ron b/assets/common/entity/dungeon/tier-5/turret.ron
index e02dbc097d..3991d54909 100644
--- a/assets/common/entity/dungeon/tier-5/turret.ron
+++ b/assets/common/entity/dungeon/tier-5/turret.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron
index 527fb4c4f3..34c10532c5 100644
--- a/assets/common/entity/dungeon/tier-5/warlock.ron
+++ b/assets/common/entity/dungeon/tier-5/warlock.ron
@@ -11,6 +11,5 @@ EntityConfig (
     ])),
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron
index 9b6d616c85..12a235ab97 100644
--- a/assets/common/entity/dungeon/tier-5/warlord.ron
+++ b/assets/common/entity/dungeon/tier-5/warlord.ron
@@ -11,6 +11,5 @@ EntityConfig (
     ])),
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron
index 235a009e7d..f9ca26fe5d 100644
--- a/assets/common/entity/test.ron
+++ b/assets/common/entity/test.ron
@@ -11,6 +11,12 @@ EntityConfig (
     /// Alignment, can be Uninit
     alignment: Alignment(Enemy),
 
+    /// Loot
+    /// Can be Item (with asset_specifier for item)
+    /// or LootTable (with asset_specifier for loot table)
+    /// or Uninit (means it should be specified something in the code)
+    loot: LootTable("common.loot_tables.humanoids"),
+
     /// Main and second tools
     /// Can be Option<Item> (with asset_specifier for item)
     /// or Choice
@@ -18,18 +24,12 @@ EntityConfig (
     main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")),
     second_tool: None,
 
-    /// Loadout Config (with asset_specifier for loadout)
-    loadout_asset: Some("common.loadout.village.merchant"),
-
-    /// Skillset Config (with asset_specifier for skillset)
-    skillset_asset: Some("common.skillset.village.merchant"),
-
-    /// Loot
-    /// Can be Item (with asset_specifier for item)
-    /// or LootTable (with asset_specifier for loot table)
-    /// or Uninit (means it should be specified something in the code)
-    loot: LootTable("common.loot_tables.humanoids"),
-
-    /// Meta Info (level, alignment, agency, etc)
-    // meta: {},
+    /// Meta Info
+    /// Possible fields:
+    /// LoadoutAsset(String) with asset_specifier for loadout
+    /// SkillSetAsset(String) with asset_specifier for skillset
+    meta: [
+        LoadoutAsset("common.loadout.village.merchant"),
+        SkillSetAsset("common.skillset.village.merchant"),
+    ],
 )
diff --git a/assets/common/entity/village/dummy.ron b/assets/common/entity/village/dummy.ron
index 8d23eb244b..0c4d11eb4b 100644
--- a/assets/common/entity/village/dummy.ron
+++ b/assets/common/entity/village/dummy.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron
index 462137ae0d..7aa5297040 100644
--- a/assets/common/entity/village/guard.ron
+++ b/assets/common/entity/village/guard.ron
@@ -8,6 +8,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.weapons.sword.iron-4")),
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: Some("common.skillset.village.guard"),
+    meta: [
+        SkillSetAsset("common.skillset.village.guard"),
+    ],
 )
diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron
index ec1c8cdd18..1c6c26fceb 100644
--- a/assets/common/entity/village/merchant.ron
+++ b/assets/common/entity/village/merchant.ron
@@ -9,6 +9,7 @@ EntityConfig (
     main_tool: Some(Item("common.items.weapons.bow.eldwood-0")),
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: Some("common.skillset.village.merchant"),
+    meta: [
+        SkillSetAsset("common.skillset.village.merchant"),
+    ],
 )
diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron
index a95985694a..07100a4c2e 100644
--- a/assets/common/entity/village/villager.ron
+++ b/assets/common/entity/village/villager.ron
@@ -16,6 +16,5 @@ EntityConfig (
     ])),
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/dreadhorn.ron b/assets/common/entity/wild/aggressive/dreadhorn.ron
index 76f7d1ce23..0ae6564a5d 100644
--- a/assets/common/entity/wild/aggressive/dreadhorn.ron
+++ b/assets/common/entity/wild/aggressive/dreadhorn.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/frostfang.ron b/assets/common/entity/wild/aggressive/frostfang.ron
index 6436fed49e..e0c62a02c6 100644
--- a/assets/common/entity/wild/aggressive/frostfang.ron
+++ b/assets/common/entity/wild/aggressive/frostfang.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/grolgar.ron b/assets/common/entity/wild/aggressive/grolgar.ron
index 4fd0e2be04..291c0e451d 100644
--- a/assets/common/entity/wild/aggressive/grolgar.ron
+++ b/assets/common/entity/wild/aggressive/grolgar.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/mammoth.ron b/assets/common/entity/wild/aggressive/mammoth.ron
index 9fb0e20309..12b75368a1 100644
--- a/assets/common/entity/wild/aggressive/mammoth.ron
+++ b/assets/common/entity/wild/aggressive/mammoth.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/mountain_troll.ron b/assets/common/entity/wild/aggressive/mountain_troll.ron
index 5014b134be..9ca4bc236b 100644
--- a/assets/common/entity/wild/aggressive/mountain_troll.ron
+++ b/assets/common/entity/wild/aggressive/mountain_troll.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/rocksnapper.ron b/assets/common/entity/wild/aggressive/rocksnapper.ron
index da2e112efe..d4225eb7ef 100644
--- a/assets/common/entity/wild/aggressive/rocksnapper.ron
+++ b/assets/common/entity/wild/aggressive/rocksnapper.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/roshwalr.ron b/assets/common/entity/wild/aggressive/roshwalr.ron
index c237f9a82b..a68d735114 100644
--- a/assets/common/entity/wild/aggressive/roshwalr.ron
+++ b/assets/common/entity/wild/aggressive/roshwalr.ron
@@ -11,6 +11,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/snow_leopard.ron b/assets/common/entity/wild/aggressive/snow_leopard.ron
index 75678d667d..fa62cf44b6 100644
--- a/assets/common/entity/wild/aggressive/snow_leopard.ron
+++ b/assets/common/entity/wild/aggressive/snow_leopard.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/snow_raptor.ron b/assets/common/entity/wild/aggressive/snow_raptor.ron
index 5849d9852e..f727af4abd 100644
--- a/assets/common/entity/wild/aggressive/snow_raptor.ron
+++ b/assets/common/entity/wild/aggressive/snow_raptor.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/wendigo.ron b/assets/common/entity/wild/aggressive/wendigo.ron
index 9a49ed4731..465167c043 100644
--- a/assets/common/entity/wild/aggressive/wendigo.ron
+++ b/assets/common/entity/wild/aggressive/wendigo.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/wolf.ron b/assets/common/entity/wild/aggressive/wolf.ron
index 4624d2bc42..a2e50a60bd 100644
--- a/assets/common/entity/wild/aggressive/wolf.ron
+++ b/assets/common/entity/wild/aggressive/wolf.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/yale.ron b/assets/common/entity/wild/aggressive/yale.ron
index 441492c463..0e13c5a8be 100644
--- a/assets/common/entity/wild/aggressive/yale.ron
+++ b/assets/common/entity/wild/aggressive/yale.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/alpaca.ron b/assets/common/entity/wild/peaceful/alpaca.ron
index 0a6567c036..b737205ca1 100644
--- a/assets/common/entity/wild/peaceful/alpaca.ron
+++ b/assets/common/entity/wild/peaceful/alpaca.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/arctic_fox.ron b/assets/common/entity/wild/peaceful/arctic_fox.ron
index 4380e983ea..9e455bc0c6 100644
--- a/assets/common/entity/wild/peaceful/arctic_fox.ron
+++ b/assets/common/entity/wild/peaceful/arctic_fox.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/arctic_hare.ron b/assets/common/entity/wild/peaceful/arctic_hare.ron
index ca398541b3..0b24333750 100644
--- a/assets/common/entity/wild/peaceful/arctic_hare.ron
+++ b/assets/common/entity/wild/peaceful/arctic_hare.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/eagle.ron b/assets/common/entity/wild/peaceful/eagle.ron
index a010819196..61bf290111 100644
--- a/assets/common/entity/wild/peaceful/eagle.ron
+++ b/assets/common/entity/wild/peaceful/eagle.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/highland.ron b/assets/common/entity/wild/peaceful/highland.ron
index a7e26a8b8d..7a6ab8fde9 100644
--- a/assets/common/entity/wild/peaceful/highland.ron
+++ b/assets/common/entity/wild/peaceful/highland.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/llama.ron b/assets/common/entity/wild/peaceful/llama.ron
index 697dabef77..c4d0373547 100644
--- a/assets/common/entity/wild/peaceful/llama.ron
+++ b/assets/common/entity/wild/peaceful/llama.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/moose.ron b/assets/common/entity/wild/peaceful/moose.ron
index be44787e2d..7bdab79760 100644
--- a/assets/common/entity/wild/peaceful/moose.ron
+++ b/assets/common/entity/wild/peaceful/moose.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/mouflon.ron b/assets/common/entity/wild/peaceful/mouflon.ron
index f72e24bfab..55982414a8 100644
--- a/assets/common/entity/wild/peaceful/mouflon.ron
+++ b/assets/common/entity/wild/peaceful/mouflon.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/owl.ron b/assets/common/entity/wild/peaceful/owl.ron
index a8df3ba91c..74c918bb4a 100644
--- a/assets/common/entity/wild/peaceful/owl.ron
+++ b/assets/common/entity/wild/peaceful/owl.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/tuskram.ron b/assets/common/entity/wild/peaceful/tuskram.ron
index 41cd8a0a0d..672dc221cf 100644
--- a/assets/common/entity/wild/peaceful/tuskram.ron
+++ b/assets/common/entity/wild/peaceful/tuskram.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/yak.ron b/assets/common/entity/wild/peaceful/yak.ron
index 788209ae65..702cf18607 100644
--- a/assets/common/entity/wild/peaceful/yak.ron
+++ b/assets/common/entity/wild/peaceful/yak.ron
@@ -8,6 +8,5 @@ EntityConfig (
     main_tool: None,
     second_tool: None,
 
-    loadout_asset: None,
-    skillset_asset: None,
+    meta: [],
 )
diff --git a/common/src/generation.rs b/common/src/generation.rs
index fcbd0f2ecd..9c6e6f4b9b 100644
--- a/common/src/generation.rs
+++ b/common/src/generation.rs
@@ -33,6 +33,12 @@ enum AlignmentMark {
     Uninit,
 }
 
+#[derive(Debug, Deserialize, Clone)]
+enum Meta {
+    LoadoutAsset(String),
+    SkillSetAsset(String),
+}
+
 #[derive(Debug, Deserialize, Clone)]
 struct EntityConfig {
     name: Option<String>,
@@ -43,8 +49,7 @@ struct EntityConfig {
     main_tool: Option<ItemSpec>,
     second_tool: Option<ItemSpec>,
     // Meta fields
-    loadout_asset: Option<String>,
-    skillset_asset: Option<String>,
+    meta: Vec<Meta>,
 }
 
 impl assets::Asset for EntityConfig {
@@ -117,8 +122,7 @@ impl EntityInfo {
             loot,
             main_tool,
             second_tool,
-            loadout_asset,
-            skillset_asset,
+            meta,
         } = config;
 
         if let Some(name) = name {
@@ -168,12 +172,15 @@ impl EntityInfo {
             self = self.with_main_tool(second_tool);
         }
 
-        if let Some(loadout_asset) = loadout_asset {
-            self = self.with_loadout_asset(loadout_asset);
-        }
-
-        if let Some(skillset_asset) = skillset_asset {
-            self = self.with_skillset_asset(skillset_asset);
+        for field in meta {
+            match field {
+                Meta::LoadoutAsset(asset) => {
+                    self = self.with_loadout_asset(asset);
+                },
+                Meta::SkillSetAsset(asset) => {
+                    self = self.with_skillset_asset(asset);
+                },
+            }
         }
 
         self
@@ -335,10 +342,9 @@ mod tests {
             let EntityConfig {
                 main_tool,
                 second_tool,
-                loadout_asset,
-                skillset_asset,
-                body,
                 loot,
+                body,
+                meta,
                 name: _name,           // can't fail if serialized, it's a boring String
                 alignment: _alignment, // can't fail if serialized, it's a boring enum
             } = config.clone();
@@ -374,16 +380,19 @@ mod tests {
                 LootKind::Uninit => {},
             }
 
-            if let Some(loadout_asset) = loadout_asset {
-                let rng = &mut rand::thread_rng();
-                let builder = LoadoutBuilder::default();
-                // we need to just load it check if it exists,
-                // because all loadouts are tested in LoadoutBuilder module
-                std::mem::drop(builder.with_asset_expect(&loadout_asset, rng));
-            }
-
-            if let Some(skillset_asset) = skillset_asset {
-                std::mem::drop(SkillSetBuilder::from_asset_expect(&skillset_asset));
+            for field in meta {
+                match field {
+                    Meta::LoadoutAsset(asset) => {
+                        let rng = &mut rand::thread_rng();
+                        let builder = LoadoutBuilder::default();
+                        // we need to just load it check if it exists,
+                        // because all loadouts are tested in LoadoutBuilder module
+                        std::mem::drop(builder.with_asset_expect(&asset, rng));
+                    },
+                    Meta::SkillSetAsset(asset) => {
+                        std::mem::drop(SkillSetBuilder::from_asset_expect(&asset));
+                    },
+                }
             }
         }
     }

From 91b3f49c155fd404ac37a49362abae8842745b09 Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Sun, 11 Jul 2021 14:28:56 +0300
Subject: [PATCH 06/12] Replace EntityConfig main/second_tool with hands

+ Check equip_slot can hold item in LoadoutBuilder
---
 .../common/entity/dungeon/fallback/boss.ron   |   3 +-
 .../common/entity/dungeon/fallback/enemy.ron  |   3 +-
 .../entity/dungeon/fallback/miniboss.ron      |   3 +-
 assets/common/entity/dungeon/tier-0/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-0/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-0/miniboss.ron |   3 +-
 assets/common/entity/dungeon/tier-0/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-0/staff.ron |   3 +-
 assets/common/entity/dungeon/tier-1/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-1/bow.ron   |   3 +-
 assets/common/entity/dungeon/tier-1/rat.ron   |   3 +-
 assets/common/entity/dungeon/tier-1/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-1/staff.ron |   3 +-
 assets/common/entity/dungeon/tier-2/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-2/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-2/hakulaq.ron  |   3 +-
 assets/common/entity/dungeon/tier-2/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-2/staff.ron |   3 +-
 .../entity/dungeon/tier-3/bonerattler.ron     |   3 +-
 assets/common/entity/dungeon/tier-3/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-3/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-3/sentry.ron   |   3 +-
 assets/common/entity/dungeon/tier-3/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-3/staff.ron |   3 +-
 assets/common/entity/dungeon/tier-4/boss.ron  |   3 +-
 assets/common/entity/dungeon/tier-4/bow.ron   |   3 +-
 .../common/entity/dungeon/tier-4/miniboss.ron |   3 +-
 assets/common/entity/dungeon/tier-4/spear.ron |   3 +-
 assets/common/entity/dungeon/tier-4/staff.ron |   3 +-
 .../entity/dungeon/tier-5/beastmaster.ron     |   3 +-
 assets/common/entity/dungeon/tier-5/boss.ron  |   3 +-
 .../common/entity/dungeon/tier-5/cultist.ron  |   3 +-
 assets/common/entity/dungeon/tier-5/hound.ron |   3 +-
 assets/common/entity/dungeon/tier-5/husk.ron  |   3 +-
 .../entity/dungeon/tier-5/husk_brute.ron      |   3 +-
 .../common/entity/dungeon/tier-5/turret.ron   |   3 +-
 .../common/entity/dungeon/tier-5/warlock.ron  |   3 +-
 .../common/entity/dungeon/tier-5/warlord.ron  |   3 +-
 .../common/entity/{test.ron => template.ron}  |  16 ++-
 assets/common/entity/village/dummy.ron        |   3 +-
 assets/common/entity/village/guard.ron        |   3 +-
 assets/common/entity/village/merchant.ron     |   3 +-
 assets/common/entity/village/villager.ron     |   3 +-
 .../entity/wild/aggressive/dreadhorn.ron      |   3 +-
 .../entity/wild/aggressive/frostfang.ron      |   3 +-
 .../common/entity/wild/aggressive/grolgar.ron |   3 +-
 .../common/entity/wild/aggressive/mammoth.ron |   3 +-
 .../entity/wild/aggressive/mountain_troll.ron |   3 +-
 .../entity/wild/aggressive/rocksnapper.ron    |   3 +-
 .../entity/wild/aggressive/roshwalr.ron       |   3 +-
 .../entity/wild/aggressive/snow_leopard.ron   |   3 +-
 .../entity/wild/aggressive/snow_raptor.ron    |   3 +-
 .../common/entity/wild/aggressive/wendigo.ron |   3 +-
 assets/common/entity/wild/aggressive/wolf.ron |   3 +-
 assets/common/entity/wild/aggressive/yale.ron |   3 +-
 assets/common/entity/wild/peaceful/alpaca.ron |   3 +-
 .../entity/wild/peaceful/arctic_fox.ron       |   3 +-
 .../entity/wild/peaceful/arctic_hare.ron      |   3 +-
 assets/common/entity/wild/peaceful/eagle.ron  |   3 +-
 .../common/entity/wild/peaceful/highland.ron  |   3 +-
 assets/common/entity/wild/peaceful/llama.ron  |   3 +-
 assets/common/entity/wild/peaceful/moose.ron  |   3 +-
 .../common/entity/wild/peaceful/mouflon.ron   |   3 +-
 assets/common/entity/wild/peaceful/owl.ron    |   3 +-
 .../common/entity/wild/peaceful/tuskram.ron   |   3 +-
 assets/common/entity/wild/peaceful/yak.ron    |   3 +-
 common/src/comp/inventory/loadout_builder.rs  | 109 ++++++++++--------
 common/src/generation.rs                      |  98 +++++++++++-----
 68 files changed, 209 insertions(+), 209 deletions(-)
 rename assets/common/entity/{test.ron => template.ron} (67%)

diff --git a/assets/common/entity/dungeon/fallback/boss.ron b/assets/common/entity/dungeon/fallback/boss.ron
index cfda6d5ee3..3f11d76f76 100644
--- a/assets/common/entity/dungeon/fallback/boss.ron
+++ b/assets/common/entity/dungeon/fallback/boss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.fallback"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/fallback/enemy.ron b/assets/common/entity/dungeon/fallback/enemy.ron
index 22dea6881a..04b2f43071 100644
--- a/assets/common/entity/dungeon/fallback/enemy.ron
+++ b/assets/common/entity/dungeon/fallback/enemy.ron
@@ -5,7 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.fallback"),
 
-    main_tool: Some(Choice([
+    hands: TwoHanded(Choice([
         (1.0, Some(Item("common.items.weapons.tool.broom"))),
         (1.0, Some(Item("common.items.weapons.tool.hoe"))),
         (1.0, Some(Item("common.items.weapons.tool.pickaxe"))),
@@ -14,7 +14,6 @@ EntityConfig (
         (1.0, Some(Item("common.items.weapons.tool.shovel-1"))),
         (1.0, Some(Item("common.items.weapons.bow.bone-1"))),
     ])),
-    second_tool: None,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/fallback/miniboss.ron b/assets/common/entity/dungeon/fallback/miniboss.ron
index 018dcfbb6e..befb2f8cf6 100644
--- a/assets/common/entity/dungeon/fallback/miniboss.ron
+++ b/assets/common/entity/dungeon/fallback/miniboss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.fallback"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-0/boss.ron b/assets/common/entity/dungeon/tier-0/boss.ron
index c45b913721..b0cecbc954 100644
--- a/assets/common/entity/dungeon/tier-0/boss.ron
+++ b/assets/common/entity/dungeon/tier-0/boss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-0.boss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron
index 362cf82734..55829f748f 100644
--- a/assets/common/entity/dungeon/tier-0/bow.ron
+++ b/assets/common/entity/dungeon/tier-0/bow.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-0.gnarling"),
diff --git a/assets/common/entity/dungeon/tier-0/miniboss.ron b/assets/common/entity/dungeon/tier-0/miniboss.ron
index 99ebb6165c..d17e7fd605 100644
--- a/assets/common/entity/dungeon/tier-0/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-0/miniboss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-0.miniboss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron
index ade46b767d..d71328be25 100644
--- a/assets/common/entity/dungeon/tier-0/spear.ron
+++ b/assets/common/entity/dungeon/tier-0/spear.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-0.gnarling"),
diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron
index 8c855b9e70..e9d9e4e376 100644
--- a/assets/common/entity/dungeon/tier-0/staff.ron
+++ b/assets/common/entity/dungeon/tier-0/staff.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-0.gnarling"),
diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/tier-1/boss.ron
index 1ff096a52c..63bacdcfc8 100644
--- a/assets/common/entity/dungeon/tier-1/boss.ron
+++ b/assets/common/entity/dungeon/tier-1/boss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-1.boss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron
index ac3b28e5e9..06078fb1e2 100644
--- a/assets/common/entity/dungeon/tier-1/bow.ron
+++ b/assets/common/entity/dungeon/tier-1/bow.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-1.adlet_bow"),
diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron
index c2672e0f3c..aeab6f2370 100644
--- a/assets/common/entity/dungeon/tier-1/rat.ron
+++ b/assets/common/entity/dungeon/tier-1/rat.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.creature.quad_small.generic"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron
index f32e2021fa..4fb7d51fa2 100644
--- a/assets/common/entity/dungeon/tier-1/spear.ron
+++ b/assets/common/entity/dungeon/tier-1/spear.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-1.adlet_spear"),
diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron
index 5591010069..1c9e8762f5 100644
--- a/assets/common/entity/dungeon/tier-1/staff.ron
+++ b/assets/common/entity/dungeon/tier-1/staff.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-1.adlet_spear"),
diff --git a/assets/common/entity/dungeon/tier-2/boss.ron b/assets/common/entity/dungeon/tier-2/boss.ron
index d24afd982f..2139489071 100644
--- a/assets/common/entity/dungeon/tier-2/boss.ron
+++ b/assets/common/entity/dungeon/tier-2/boss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-2.boss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron
index e0cfe24086..7e6c2d6dbc 100644
--- a/assets/common/entity/dungeon/tier-2/bow.ron
+++ b/assets/common/entity/dungeon/tier-2/bow.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-2.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-2.sahagin"),
diff --git a/assets/common/entity/dungeon/tier-2/hakulaq.ron b/assets/common/entity/dungeon/tier-2/hakulaq.ron
index 9ef070ab67..7f9199585a 100644
--- a/assets/common/entity/dungeon/tier-2/hakulaq.ron
+++ b/assets/common/entity/dungeon/tier-2/hakulaq.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.creature.quad_low.fanged"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron
index abc9b1b961..239162caf8 100644
--- a/assets/common/entity/dungeon/tier-2/spear.ron
+++ b/assets/common/entity/dungeon/tier-2/spear.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-2.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-2.sahagin"),
diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron
index b13508dd1b..cba57b3351 100644
--- a/assets/common/entity/dungeon/tier-2/staff.ron
+++ b/assets/common/entity/dungeon/tier-2/staff.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-2.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-2.sahagin"),
diff --git a/assets/common/entity/dungeon/tier-3/bonerattler.ron b/assets/common/entity/dungeon/tier-3/bonerattler.ron
index ddbb8b5952..79d7c5e2d3 100644
--- a/assets/common/entity/dungeon/tier-3/bonerattler.ron
+++ b/assets/common/entity/dungeon/tier-3/bonerattler.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.creature.quad_medium.carapace"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-3/boss.ron b/assets/common/entity/dungeon/tier-3/boss.ron
index aea8d5cf91..a8b075c9b2 100644
--- a/assets/common/entity/dungeon/tier-3/boss.ron
+++ b/assets/common/entity/dungeon/tier-3/boss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-3.boss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron
index 24ceaabafd..445d697338 100644
--- a/assets/common/entity/dungeon/tier-3/bow.ron
+++ b/assets/common/entity/dungeon/tier-3/bow.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-3.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-3.haniwa"),
diff --git a/assets/common/entity/dungeon/tier-3/sentry.ron b/assets/common/entity/dungeon/tier-3/sentry.ron
index 08cb3e73b7..43c519c834 100644
--- a/assets/common/entity/dungeon/tier-3/sentry.ron
+++ b/assets/common/entity/dungeon/tier-3/sentry.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Item("common.items.crafting_ing.stones"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron
index 38bdb85f0f..10614bd9c0 100644
--- a/assets/common/entity/dungeon/tier-3/spear.ron
+++ b/assets/common/entity/dungeon/tier-3/spear.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-3.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-3.haniwa"),
diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron
index 85aa5ea5a1..5066edc088 100644
--- a/assets/common/entity/dungeon/tier-3/staff.ron
+++ b/assets/common/entity/dungeon/tier-3/staff.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-3.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-3.haniwa"),
diff --git a/assets/common/entity/dungeon/tier-4/boss.ron b/assets/common/entity/dungeon/tier-4/boss.ron
index c1f43f164c..0d4da80c24 100644
--- a/assets/common/entity/dungeon/tier-4/boss.ron
+++ b/assets/common/entity/dungeon/tier-4/boss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-4.boss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron
index 7e693009e0..a2a220054e 100644
--- a/assets/common/entity/dungeon/tier-4/bow.ron
+++ b/assets/common/entity/dungeon/tier-4/bow.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-4.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-4.myrmidon"),
diff --git a/assets/common/entity/dungeon/tier-4/miniboss.ron b/assets/common/entity/dungeon/tier-4/miniboss.ron
index 23fc8f7ae2..ffe140c084 100644
--- a/assets/common/entity/dungeon/tier-4/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-4/miniboss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-4.miniboss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron
index d0f2335239..1ebf5dec71 100644
--- a/assets/common/entity/dungeon/tier-4/spear.ron
+++ b/assets/common/entity/dungeon/tier-4/spear.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-4.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-4.myrmidon"),
diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron
index 9448b6e1a9..1f174c1924 100644
--- a/assets/common/entity/dungeon/tier-4/staff.ron
+++ b/assets/common/entity/dungeon/tier-4/staff.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-4.enemy"),
 
-    main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")),
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-4.myrmidon"),
diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron
index 76d28d1746..98c11b8d41 100644
--- a/assets/common/entity/dungeon/tier-5/beastmaster.ron
+++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron
@@ -5,12 +5,11 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.miniboss"),
 
-    main_tool: Some(Choice([
+    hands: TwoHanded(Choice([
         (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))),
         (1.0, Some(Item("common.items.weapons.sword.bloodsteel-1"))),
         (1.0, Some(Item("common.items.weapons.bow.velorite"))),
     ])),
-    second_tool: None,
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-5.beastmaster"),
diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron
index b2289aa240..ebeaebefec 100644
--- a/assets/common/entity/dungeon/tier-5/boss.ron
+++ b/assets/common/entity/dungeon/tier-5/boss.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.boss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron
index 8743a20285..1b6728c34f 100644
--- a/assets/common/entity/dungeon/tier-5/cultist.ron
+++ b/assets/common/entity/dungeon/tier-5/cultist.ron
@@ -5,7 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.enemy"),
 
-    main_tool: Some(Choice([
+    hands: TwoHanded(Choice([
         (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))),
         (2.0, Some(Item("common.items.weapons.sword.cultist"))),
         (1.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))),
@@ -13,7 +13,6 @@ EntityConfig (
         (1.0, Some(Item("common.items.weapons.bow.velorite"))),
         (1.0, Some(Item("common.items.weapons.sceptre.sceptre_velorite_0"))),
     ])),
-    second_tool: None,
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-5.cultist"),
diff --git a/assets/common/entity/dungeon/tier-5/hound.ron b/assets/common/entity/dungeon/tier-5/hound.ron
index 81b37f01f4..0cbf1e7c8b 100644
--- a/assets/common/entity/dungeon/tier-5/hound.ron
+++ b/assets/common/entity/dungeon/tier-5/hound.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.minion"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron
index a7a00ab6a2..e68a99f04a 100644
--- a/assets/common/entity/dungeon/tier-5/husk.ron
+++ b/assets/common/entity/dungeon/tier-5/husk.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.minion"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [
         LoadoutAsset("common.loadout.dungeon.tier-5.husk"),
diff --git a/assets/common/entity/dungeon/tier-5/husk_brute.ron b/assets/common/entity/dungeon/tier-5/husk_brute.ron
index da3e55583c..7e77ad8aca 100644
--- a/assets/common/entity/dungeon/tier-5/husk_brute.ron
+++ b/assets/common/entity/dungeon/tier-5/husk_brute.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.miniboss"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/turret.ron b/assets/common/entity/dungeon/tier-5/turret.ron
index 3991d54909..9470ff47ac 100644
--- a/assets/common/entity/dungeon/tier-5/turret.ron
+++ b/assets/common/entity/dungeon/tier-5/turret.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Item("common.items.crafting_ing.twigs"),
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron
index 34c10532c5..3119ad911f 100644
--- a/assets/common/entity/dungeon/tier-5/warlock.ron
+++ b/assets/common/entity/dungeon/tier-5/warlock.ron
@@ -5,11 +5,10 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.enemy"),
 
-    main_tool: Some(Choice([
+    hands: TwoHanded(Choice([
         (1.5, Some(Item("common.items.npc_weapons.staff.bipedlarge-cultist"))),
         (1.0, Some(Item("common.items.npc_weapons.bow.bipedlarge-velorite"))),
     ])),
-    second_tool: None,
 
     meta: [],
 )
diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron
index 12a235ab97..07909a78df 100644
--- a/assets/common/entity/dungeon/tier-5/warlord.ron
+++ b/assets/common/entity/dungeon/tier-5/warlord.ron
@@ -5,11 +5,10 @@ EntityConfig (
 
     loot: LootTable("common.loot_tables.dungeon.tier-5.enemy"),
 
-    main_tool: Some(Choice([
+    hands: TwoHanded(Choice([
         (1.5, Some(Item("common.items.npc_weapons.sword.bipedlarge-cultist"))),
         (1.0, Some(Item("common.items.npc_weapons.hammer.bipedlarge-cultist"))),
     ])),
-    second_tool: None,
 
     meta: [],
 )
diff --git a/assets/common/entity/test.ron b/assets/common/entity/template.ron
similarity index 67%
rename from assets/common/entity/test.ron
rename to assets/common/entity/template.ron
index f9ca26fe5d..0a35c81854 100644
--- a/assets/common/entity/test.ron
+++ b/assets/common/entity/template.ron
@@ -17,12 +17,16 @@ EntityConfig (
     /// or Uninit (means it should be specified something in the code)
     loot: LootTable("common.loot_tables.humanoids"),
 
-    /// Main and second tools
-    /// Can be Option<Item> (with asset_specifier for item)
-    /// or Choice
-    /// (array of pairs with weight of choosing some item and Option<Item>)
-    main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")),
-    second_tool: None,
+    /// Hands:
+    /// - TwoHanded(ItemSpec) for one weapon 2h or 1h,
+    /// - Paired(ItemSpec) for two 1h weapons aka berserker mode,
+    /// - Mix {
+    ///     mainhand: ItemSpec,
+    ///     offhand: ItemSpec,
+    /// } for two different 1h weapons,
+    /// - Uninit which means that tool should be specified somewhere in code,
+    /// Where ItemSpec is taken from loadout_builder module
+    hands: TwoHanded(Item("common.items.weapons.sword.cultist")),
 
     /// Meta Info
     /// Possible fields:
diff --git a/assets/common/entity/village/dummy.ron b/assets/common/entity/village/dummy.ron
index 0c4d11eb4b..d9a83aa730 100644
--- a/assets/common/entity/village/dummy.ron
+++ b/assets/common/entity/village/dummy.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron
index 7aa5297040..d1b7cb2754 100644
--- a/assets/common/entity/village/guard.ron
+++ b/assets/common/entity/village/guard.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: Some(Item("common.items.weapons.sword.iron-4")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.weapons.sword.iron-4")),
 
     meta: [
         SkillSetAsset("common.skillset.village.guard"),
diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron
index 1c6c26fceb..09aaefc238 100644
--- a/assets/common/entity/village/merchant.ron
+++ b/assets/common/entity/village/merchant.ron
@@ -6,8 +6,7 @@ EntityConfig (
     // considering giving some gold/gems/materials?
     loot: Uninit,
 
-    main_tool: Some(Item("common.items.weapons.bow.eldwood-0")),
-    second_tool: None,
+    hands: TwoHanded(Item("common.items.weapons.bow.eldwood-0")),
 
     meta: [
         SkillSetAsset("common.skillset.village.merchant"),
diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron
index 07100a4c2e..eb7e7fbe92 100644
--- a/assets/common/entity/village/villager.ron
+++ b/assets/common/entity/village/villager.ron
@@ -6,7 +6,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: Some(Choice([
+    hands: TwoHanded(Choice([
         (1.0, Some(Item("common.items.weapons.tool.broom"))),
         (1.0, Some(Item("common.items.weapons.tool.hoe"))),
         (1.0, Some(Item("common.items.weapons.tool.pickaxe"))),
@@ -14,7 +14,6 @@ EntityConfig (
         (1.0, Some(Item("common.items.weapons.tool.shovel-0"))),
         (1.0, Some(Item("common.items.weapons.tool.shovel-1"))),
     ])),
-    second_tool: None,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/dreadhorn.ron b/assets/common/entity/wild/aggressive/dreadhorn.ron
index 0ae6564a5d..e348d57ad5 100644
--- a/assets/common/entity/wild/aggressive/dreadhorn.ron
+++ b/assets/common/entity/wild/aggressive/dreadhorn.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/frostfang.ron b/assets/common/entity/wild/aggressive/frostfang.ron
index e0c62a02c6..f3c7376892 100644
--- a/assets/common/entity/wild/aggressive/frostfang.ron
+++ b/assets/common/entity/wild/aggressive/frostfang.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/grolgar.ron b/assets/common/entity/wild/aggressive/grolgar.ron
index 291c0e451d..47863b0fa8 100644
--- a/assets/common/entity/wild/aggressive/grolgar.ron
+++ b/assets/common/entity/wild/aggressive/grolgar.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/mammoth.ron b/assets/common/entity/wild/aggressive/mammoth.ron
index 12b75368a1..744cac3adb 100644
--- a/assets/common/entity/wild/aggressive/mammoth.ron
+++ b/assets/common/entity/wild/aggressive/mammoth.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/mountain_troll.ron b/assets/common/entity/wild/aggressive/mountain_troll.ron
index 9ca4bc236b..8aad271d4c 100644
--- a/assets/common/entity/wild/aggressive/mountain_troll.ron
+++ b/assets/common/entity/wild/aggressive/mountain_troll.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/rocksnapper.ron b/assets/common/entity/wild/aggressive/rocksnapper.ron
index d4225eb7ef..089828f836 100644
--- a/assets/common/entity/wild/aggressive/rocksnapper.ron
+++ b/assets/common/entity/wild/aggressive/rocksnapper.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/roshwalr.ron b/assets/common/entity/wild/aggressive/roshwalr.ron
index a68d735114..cda5c15ee3 100644
--- a/assets/common/entity/wild/aggressive/roshwalr.ron
+++ b/assets/common/entity/wild/aggressive/roshwalr.ron
@@ -8,8 +8,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/snow_leopard.ron b/assets/common/entity/wild/aggressive/snow_leopard.ron
index fa62cf44b6..95e9d44d22 100644
--- a/assets/common/entity/wild/aggressive/snow_leopard.ron
+++ b/assets/common/entity/wild/aggressive/snow_leopard.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/snow_raptor.ron b/assets/common/entity/wild/aggressive/snow_raptor.ron
index f727af4abd..53cfcc3726 100644
--- a/assets/common/entity/wild/aggressive/snow_raptor.ron
+++ b/assets/common/entity/wild/aggressive/snow_raptor.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/wendigo.ron b/assets/common/entity/wild/aggressive/wendigo.ron
index 465167c043..9cae3b1e53 100644
--- a/assets/common/entity/wild/aggressive/wendigo.ron
+++ b/assets/common/entity/wild/aggressive/wendigo.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/wolf.ron b/assets/common/entity/wild/aggressive/wolf.ron
index a2e50a60bd..b343a1c642 100644
--- a/assets/common/entity/wild/aggressive/wolf.ron
+++ b/assets/common/entity/wild/aggressive/wolf.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/aggressive/yale.ron b/assets/common/entity/wild/aggressive/yale.ron
index 0e13c5a8be..fafe4c20c9 100644
--- a/assets/common/entity/wild/aggressive/yale.ron
+++ b/assets/common/entity/wild/aggressive/yale.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/alpaca.ron b/assets/common/entity/wild/peaceful/alpaca.ron
index b737205ca1..d29584c4f7 100644
--- a/assets/common/entity/wild/peaceful/alpaca.ron
+++ b/assets/common/entity/wild/peaceful/alpaca.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/arctic_fox.ron b/assets/common/entity/wild/peaceful/arctic_fox.ron
index 9e455bc0c6..45816ff39b 100644
--- a/assets/common/entity/wild/peaceful/arctic_fox.ron
+++ b/assets/common/entity/wild/peaceful/arctic_fox.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/arctic_hare.ron b/assets/common/entity/wild/peaceful/arctic_hare.ron
index 0b24333750..2e4265de09 100644
--- a/assets/common/entity/wild/peaceful/arctic_hare.ron
+++ b/assets/common/entity/wild/peaceful/arctic_hare.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/eagle.ron b/assets/common/entity/wild/peaceful/eagle.ron
index 61bf290111..aae26839af 100644
--- a/assets/common/entity/wild/peaceful/eagle.ron
+++ b/assets/common/entity/wild/peaceful/eagle.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/highland.ron b/assets/common/entity/wild/peaceful/highland.ron
index 7a6ab8fde9..3ddd070faf 100644
--- a/assets/common/entity/wild/peaceful/highland.ron
+++ b/assets/common/entity/wild/peaceful/highland.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/llama.ron b/assets/common/entity/wild/peaceful/llama.ron
index c4d0373547..2ca5721c68 100644
--- a/assets/common/entity/wild/peaceful/llama.ron
+++ b/assets/common/entity/wild/peaceful/llama.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/moose.ron b/assets/common/entity/wild/peaceful/moose.ron
index 7bdab79760..f720464d76 100644
--- a/assets/common/entity/wild/peaceful/moose.ron
+++ b/assets/common/entity/wild/peaceful/moose.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/mouflon.ron b/assets/common/entity/wild/peaceful/mouflon.ron
index 55982414a8..940edec652 100644
--- a/assets/common/entity/wild/peaceful/mouflon.ron
+++ b/assets/common/entity/wild/peaceful/mouflon.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/owl.ron b/assets/common/entity/wild/peaceful/owl.ron
index 74c918bb4a..f4cfc3118a 100644
--- a/assets/common/entity/wild/peaceful/owl.ron
+++ b/assets/common/entity/wild/peaceful/owl.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/tuskram.ron b/assets/common/entity/wild/peaceful/tuskram.ron
index 672dc221cf..bd4bc62f6c 100644
--- a/assets/common/entity/wild/peaceful/tuskram.ron
+++ b/assets/common/entity/wild/peaceful/tuskram.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/assets/common/entity/wild/peaceful/yak.ron b/assets/common/entity/wild/peaceful/yak.ron
index 702cf18607..32ffaba58b 100644
--- a/assets/common/entity/wild/peaceful/yak.ron
+++ b/assets/common/entity/wild/peaceful/yak.ron
@@ -5,8 +5,7 @@ EntityConfig (
 
     loot: Uninit,
 
-    main_tool: None,
-    second_tool: None,
+    hands: Uninit,
 
     meta: [],
 )
diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs
index 9e1a5da439..7051349812 100644
--- a/common/src/comp/inventory/loadout_builder.rs
+++ b/common/src/comp/inventory/loadout_builder.rs
@@ -92,17 +92,26 @@ impl ItemSpec {
     ///
     /// # Panics
     /// 1) If weights are invalid
-    pub fn validate(&self, key: EquipSlot) {
+    /// 2) If item doesn't correspond to `EquipSlot`
+    pub fn validate(&self, equip_slot: EquipSlot) {
         match self {
-            ItemSpec::Item(specifier) => std::mem::drop(Item::new_from_asset_expect(specifier)),
+            ItemSpec::Item(specifier) => {
+                let item = Item::new_from_asset_expect(specifier);
+                if !equip_slot.can_hold(&item.kind) {
+                    panic!("Tried to place {} into {:?}", specifier, equip_slot);
+                }
+                std::mem::drop(item);
+            },
             ItemSpec::Choice(items) => {
                 for (p, entry) in items {
                     if p <= &0.0 {
-                        let err =
-                            format!("Weight is less or equal to 0.0.\n ({:?}: {:?})", key, self,);
+                        let err = format!(
+                            "Weight is less or equal to 0.0.\n ({:?}: {:?})",
+                            equip_slot, self
+                        );
                         panic!("\n\n{}\n\n", err);
                     } else {
-                        entry.as_ref().map(|e| e.validate(key));
+                        entry.as_ref().map(|e| e.validate(equip_slot));
                     }
                 }
             },
@@ -389,13 +398,13 @@ impl LoadoutBuilder {
             .with_default_equipment(body)
     }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     /// Set default active mainhand weapon based on `body`
     pub fn with_default_maintool(self, body: &Body) -> Self {
         self.active_mainhand(Some(default_main_tool(body)))
     }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     /// Set default equipement based on `body`
     pub fn with_default_equipment(self, body: &Body) -> Self {
         let chest = match body {
@@ -466,7 +475,7 @@ impl LoadoutBuilder {
         }
     }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn with_preset(mut self, preset: Preset) -> Self {
         let rng = &mut rand::thread_rng();
         match preset {
@@ -478,7 +487,7 @@ impl LoadoutBuilder {
         self
     }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn with_creator(
         mut self,
         creator: fn(LoadoutBuilder, Option<&SiteInformation>) -> LoadoutBuilder,
@@ -497,7 +506,7 @@ impl LoadoutBuilder {
     /// 1) Will panic if there is no asset with such `asset_specifier`
     /// 2) Will panic if path to item specified in loadout file doesn't exist
     /// 3) Will panic while runs in tests and asset doesn't have "correct" form
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn with_asset_expect(mut self, asset_specifier: &str, rng: &mut impl Rng) -> Self {
         let spec = LoadoutSpec::load_expect(asset_specifier).read().0.clone();
         for (key, entry) in spec {
@@ -573,88 +582,96 @@ impl LoadoutBuilder {
 
     /// Set default armor items for the loadout. This may vary with game
     /// updates, but should be safe defaults for a new character.
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn defaults(self) -> Self {
         let rng = &mut rand::thread_rng();
         self.with_asset_expect("common.loadout.default", rng)
     }
 
+    #[must_use = "Method consumes builder and returns updated builder."]
     fn with_equipment(mut self, equip_slot: EquipSlot, item: Option<Item>) -> Self {
+        // Panic if item doesn't correspond to slot
+        assert!(
+            item.as_ref()
+                .map_or(true, |item| equip_slot.can_hold(&item.kind))
+        );
+
         self.0.swap(equip_slot, item);
         self
     }
 
-    #[must_use]
-    pub fn active_mainhand(self, item: Option<Item>) -> Self {
-        self.with_equipment(EquipSlot::ActiveMainhand, item)
-    }
-
-    #[must_use]
-    pub fn active_offhand(self, item: Option<Item>) -> Self {
-        self.with_equipment(EquipSlot::ActiveOffhand, item)
-    }
-
-    #[must_use]
-    pub fn inactive_mainhand(self, item: Option<Item>) -> Self {
-        self.with_equipment(EquipSlot::InactiveMainhand, item)
-    }
-
-    #[must_use]
-    pub fn inactive_offhand(self, item: Option<Item>) -> Self {
-        self.with_equipment(EquipSlot::InactiveOffhand, item)
-    }
-
+    #[must_use = "Method consumes builder and returns updated builder."]
     fn with_armor(self, armor_slot: ArmorSlot, item: Option<Item>) -> Self {
         self.with_equipment(EquipSlot::Armor(armor_slot), item)
     }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
+    pub fn active_mainhand(self, item: Option<Item>) -> Self {
+        self.with_equipment(EquipSlot::ActiveMainhand, item)
+    }
+
+    #[must_use = "Method consumes builder and returns updated builder."]
+    pub fn active_offhand(self, item: Option<Item>) -> Self {
+        self.with_equipment(EquipSlot::ActiveOffhand, item)
+    }
+
+    #[must_use = "Method consumes builder and returns updated builder."]
+    pub fn inactive_mainhand(self, item: Option<Item>) -> Self {
+        self.with_equipment(EquipSlot::InactiveMainhand, item)
+    }
+
+    #[must_use = "Method consumes builder and returns updated builder."]
+    pub fn inactive_offhand(self, item: Option<Item>) -> Self {
+        self.with_equipment(EquipSlot::InactiveOffhand, item)
+    }
+
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn shoulder(self, item: Option<Item>) -> Self {
         self.with_armor(ArmorSlot::Shoulders, item)
     }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn chest(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Chest, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn belt(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Belt, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn hands(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Hands, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn pants(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Legs, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn feet(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Feet, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn back(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Back, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn ring1(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Ring1, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn ring2(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Ring2, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn neck(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Neck, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn lantern(self, item: Option<Item>) -> Self {
         self.with_equipment(EquipSlot::Lantern, item)
     }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn glider(self, item: Option<Item>) -> Self { self.with_equipment(EquipSlot::Glider, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn head(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Head, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn tabard(self, item: Option<Item>) -> Self { self.with_armor(ArmorSlot::Tabard, item) }
 
-    #[must_use]
+    #[must_use = "Method consumes builder and returns updated builder."]
     pub fn bag(self, which: ArmorSlot, item: Option<Item>) -> Self { self.with_armor(which, item) }
 
     #[must_use]
diff --git a/common/src/generation.rs b/common/src/generation.rs
index 9c6e6f4b9b..32b7fde77b 100644
--- a/common/src/generation.rs
+++ b/common/src/generation.rs
@@ -40,14 +40,23 @@ enum Meta {
 }
 
 #[derive(Debug, Deserialize, Clone)]
-struct EntityConfig {
+enum Hands {
+    TwoHanded(ItemSpec),
+    Paired(ItemSpec),
+    Mix {
+        mainhand: ItemSpec,
+        offhand: ItemSpec,
+    },
+    Uninit,
+}
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct EntityConfig {
     name: Option<String>,
     body: BodyBuilder,
     alignment: AlignmentMark,
     loot: LootKind,
-    // TODO: replace with `hands` field to support one 2h weapon/ pair 1h weapons
-    main_tool: Option<ItemSpec>,
-    second_tool: Option<ItemSpec>,
+    hands: Hands,
     // Meta fields
     meta: Vec<Meta>,
 }
@@ -58,6 +67,13 @@ impl assets::Asset for EntityConfig {
     const EXTENSION: &'static str = "ron";
 }
 
+impl EntityConfig {
+    pub fn from_asset_expect(asset_specifier: &str) -> EntityConfig {
+        Self::load_owned(asset_specifier)
+            .unwrap_or_else(|e| panic!("Failed to load {}. Error: {}", asset_specifier, e))
+    }
+}
+
 #[derive(Clone)]
 pub struct EntityInfo {
     pub pos: Vec3<f32>,
@@ -114,14 +130,13 @@ impl EntityInfo {
     }
 
     // helper function to apply config
-    fn with_entity_config(mut self, config: EntityConfig, asset_specifier: Option<&str>) -> Self {
+    fn with_entity_config(mut self, config: EntityConfig, config_asset: Option<&str>) -> Self {
         let EntityConfig {
             name,
             body,
             alignment,
             loot,
-            main_tool,
-            second_tool,
+            hands,
             meta,
         } = config;
 
@@ -161,15 +176,36 @@ impl EntityInfo {
         }
 
         let rng = &mut rand::thread_rng();
-        if let Some(main_tool) =
-            main_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng))
-        {
-            self = self.with_main_tool(main_tool);
-        }
-        if let Some(second_tool) =
-            second_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng))
-        {
-            self = self.with_main_tool(second_tool);
+        match hands {
+            Hands::TwoHanded(main_tool) => {
+                let tool = main_tool.try_to_item(config_asset.unwrap_or("??"), rng);
+                if let Some(tool) = tool {
+                    self = self.with_main_tool(tool);
+                }
+            },
+            Hands::Paired(tool) => {
+                //FIXME: very stupid code, which just tries same item two times
+                //figure out reasonable way to clone item
+                let main_tool = tool.try_to_item(config_asset.unwrap_or("??"), rng);
+                let second_tool = tool.try_to_item(config_asset.unwrap_or("??"), rng);
+                if let Some(main_tool) = main_tool {
+                    self = self.with_main_tool(main_tool);
+                }
+                if let Some(second_tool) = second_tool {
+                    self = self.with_second_tool(second_tool);
+                }
+            },
+            Hands::Mix { mainhand, offhand } => {
+                let main_tool = mainhand.try_to_item(config_asset.unwrap_or("??"), rng);
+                let second_tool = offhand.try_to_item(config_asset.unwrap_or("??"), rng);
+                if let Some(main_tool) = main_tool {
+                    self = self.with_main_tool(main_tool);
+                }
+                if let Some(second_tool) = second_tool {
+                    self = self.with_second_tool(second_tool);
+                }
+            },
+            Hands::Uninit => {},
         }
 
         for field in meta {
@@ -337,24 +373,32 @@ mod tests {
     #[test]
     fn test_all_entity_assets() {
         // It just load everything that could
-        let entity_configs = assets::read_expect_dir::<EntityConfig>("common.entity", true);
-        for config in entity_configs {
+        let entity_configs = assets::load_dir::<EntityConfig>("common.entity", true)
+            .expect("Failed to access entity directory");
+        for config_asset in entity_configs.ids() {
+            println!("{}:", config_asset);
             let EntityConfig {
-                main_tool,
-                second_tool,
+                hands,
                 loot,
                 body,
                 meta,
                 name: _name,           // can't fail if serialized, it's a boring String
                 alignment: _alignment, // can't fail if serialized, it's a boring enum
-            } = config.clone();
+            } = EntityConfig::from_asset_expect(config_asset);
 
-            if let Some(main_tool) = main_tool {
-                main_tool.validate(EquipSlot::ActiveMainhand);
-            }
-
-            if let Some(second_tool) = second_tool {
-                second_tool.validate(EquipSlot::ActiveOffhand);
+            match hands {
+                Hands::TwoHanded(main_tool) => {
+                    main_tool.validate(EquipSlot::ActiveMainhand);
+                },
+                Hands::Paired(tool) => {
+                    tool.validate(EquipSlot::ActiveMainhand);
+                    tool.validate(EquipSlot::ActiveOffhand);
+                },
+                Hands::Mix { mainhand, offhand } => {
+                    mainhand.validate(EquipSlot::ActiveMainhand);
+                    offhand.validate(EquipSlot::ActiveOffhand);
+                },
+                Hands::Uninit => {},
             }
 
             match body {

From 4a83cb13c5cece570cd66eeedf48dd6d686dcfb0 Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Sun, 11 Jul 2021 21:24:44 +0300
Subject: [PATCH 07/12] Add Automatic NameKind to EntityConfig

+ add animal entities for wildlife spawns
---
 .../common/entity/dungeon/fallback/boss.ron   |  2 +-
 .../common/entity/dungeon/fallback/enemy.ron  |  2 +-
 .../entity/dungeon/fallback/miniboss.ron      |  2 +-
 assets/common/entity/dungeon/tier-0/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-0/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-0/miniboss.ron |  2 +-
 assets/common/entity/dungeon/tier-0/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-0/staff.ron |  2 +-
 assets/common/entity/dungeon/tier-1/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-1/bow.ron   |  2 +-
 assets/common/entity/dungeon/tier-1/rat.ron   |  2 +-
 assets/common/entity/dungeon/tier-1/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-1/staff.ron |  2 +-
 assets/common/entity/dungeon/tier-2/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-2/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-2/hakulaq.ron  |  2 +-
 assets/common/entity/dungeon/tier-2/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-2/staff.ron |  2 +-
 .../entity/dungeon/tier-3/bonerattler.ron     |  2 +-
 assets/common/entity/dungeon/tier-3/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-3/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-3/sentry.ron   |  2 +-
 assets/common/entity/dungeon/tier-3/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-3/staff.ron |  2 +-
 assets/common/entity/dungeon/tier-4/boss.ron  |  2 +-
 assets/common/entity/dungeon/tier-4/bow.ron   |  2 +-
 .../common/entity/dungeon/tier-4/miniboss.ron |  2 +-
 assets/common/entity/dungeon/tier-4/spear.ron |  2 +-
 assets/common/entity/dungeon/tier-4/staff.ron |  2 +-
 .../entity/dungeon/tier-5/beastmaster.ron     |  2 +-
 assets/common/entity/dungeon/tier-5/boss.ron  |  2 +-
 .../common/entity/dungeon/tier-5/cultist.ron  |  2 +-
 assets/common/entity/dungeon/tier-5/hound.ron |  2 +-
 assets/common/entity/dungeon/tier-5/husk.ron  |  2 +-
 .../entity/dungeon/tier-5/husk_brute.ron      |  2 +-
 .../common/entity/dungeon/tier-5/turret.ron   |  2 +-
 .../common/entity/dungeon/tier-5/warlock.ron  |  2 +-
 .../common/entity/dungeon/tier-5/warlord.ron  |  2 +-
 assets/common/entity/template.ron             |  7 ++-
 assets/common/entity/village/dummy.ron        |  2 +-
 assets/common/entity/village/guard.ron        |  2 +-
 assets/common/entity/village/merchant.ron     |  2 +-
 assets/common/entity/village/villager.ron     |  3 +-
 .../entity/wild/aggressive/alligator.ron      | 11 ++++
 .../entity/wild/aggressive/archaeos.ron       | 11 ++++
 assets/common/entity/wild/aggressive/asp.ron  | 11 ++++
 .../common/entity/wild/aggressive/batfox.ron  | 11 ++++
 assets/common/entity/wild/aggressive/bear.ron | 11 ++++
 .../entity/wild/aggressive/bonerattler.ron    | 11 ++++
 .../entity/wild/aggressive/cockatrice.ron     | 11 ++++
 .../entity/wild/aggressive/crocodile.ron      | 11 ++++
 .../common/entity/wild/aggressive/cyclope.ron | 11 ++++
 .../entity/wild/aggressive/deadwood.ron       | 11 ++++
 .../entity/wild/aggressive/dodarock.ron       | 11 ++++
 .../entity/wild/aggressive/dreadhorn.ron      |  2 +-
 .../entity/wild/aggressive/frostfang.ron      |  2 +-
 .../common/entity/wild/aggressive/grolgar.ron |  2 +-
 .../common/entity/wild/aggressive/hakulaq.ron | 11 ++++
 .../common/entity/wild/aggressive/hyena.ron   | 11 ++++
 .../common/entity/wild/aggressive/icepike.ron | 11 ++++
 .../entity/wild/aggressive/lavadrake.ron      | 11 ++++
 assets/common/entity/wild/aggressive/lion.ron | 11 ++++
 .../common/entity/wild/aggressive/mammoth.ron |  2 +-
 .../entity/wild/aggressive/maneater.ron       | 11 ++++
 .../entity/wild/aggressive/mighty_saurok.ron  | 11 ++++
 .../common/entity/wild/aggressive/monitor.ron | 11 ++++
 .../entity/wild/aggressive/mountain_troll.ron |  2 +-
 .../common/entity/wild/aggressive/ngoubou.ron | 11 ++++
 .../common/entity/wild/aggressive/ntouka.ron  | 11 ++++
 .../entity/wild/aggressive/occult_saurok.ron  | 11 ++++
 .../common/entity/wild/aggressive/odonto.ron  | 11 ++++
 assets/common/entity/wild/aggressive/ogre.ron | 11 ++++
 .../entity/wild/aggressive/rocksnapper.ron    |  2 +-
 .../entity/wild/aggressive/roshwalr.ron       |  2 +-
 .../entity/wild/aggressive/roshwalr_boss.ron  | 11 ++++
 .../common/entity/wild/aggressive/saber.ron   | 11 ++++
 .../entity/wild/aggressive/sand_raptor.ron    | 11 ++++
 .../entity/wild/aggressive/sandshark.ron      | 11 ++++
 .../entity/wild/aggressive/sly_saurok.ron     | 11 ++++
 .../entity/wild/aggressive/snow_leopard.ron   |  2 +-
 .../entity/wild/aggressive/snow_raptor.ron    |  2 +-
 .../entity/wild/aggressive/sunlizard.ron      | 11 ++++
 .../entity/wild/aggressive/swamp_troll.ron    | 11 ++++
 .../entity/wild/aggressive/tarasque.ron       | 11 ++++
 .../common/entity/wild/aggressive/tiger.ron   | 11 ++++
 .../common/entity/wild/aggressive/wendigo.ron |  2 +-
 assets/common/entity/wild/aggressive/wolf.ron |  2 +-
 .../entity/wild/aggressive/wood_raptor.ron    | 11 ++++
 assets/common/entity/wild/aggressive/yale.ron |  2 +-
 assets/common/entity/wild/peaceful/alpaca.ron |  2 +-
 .../common/entity/wild/peaceful/antelope.ron  | 11 ++++
 .../entity/wild/peaceful/arctic_fox.ron       |  2 +-
 .../entity/wild/peaceful/arctic_hare.ron      |  2 +-
 .../common/entity/wild/peaceful/axolotl.ron   | 11 ++++
 assets/common/entity/wild/peaceful/beaver.ron | 11 ++++
 assets/common/entity/wild/peaceful/boar.ron   | 11 ++++
 assets/common/entity/wild/peaceful/camel.ron  | 11 ++++
 .../entity/wild/peaceful/catoblepas.ron       | 11 ++++
 assets/common/entity/wild/peaceful/cattle.ron | 11 ++++
 .../common/entity/wild/peaceful/chicken.ron   | 11 ++++
 .../common/entity/wild/peaceful/clownfish.ron | 11 ++++
 assets/common/entity/wild/peaceful/deer.ron   | 11 ++++
 assets/common/entity/wild/peaceful/donkey.ron | 11 ++++
 assets/common/entity/wild/peaceful/duck.ron   | 11 ++++
 assets/common/entity/wild/peaceful/eagle.ron  |  2 +-
 .../entity/wild/peaceful/forest_fox.ron       | 11 ++++
 assets/common/entity/wild/peaceful/frog.ron   | 11 ++++
 .../common/entity/wild/peaceful/fungome.ron   | 11 ++++
 assets/common/entity/wild/peaceful/gecko.ron  | 11 ++++
 assets/common/entity/wild/peaceful/goat.ron   | 11 ++++
 assets/common/entity/wild/peaceful/goose.ron  | 11 ++++
 .../common/entity/wild/peaceful/highland.ron  |  2 +-
 .../common/entity/wild/peaceful/hirdrasil.ron | 11 ++++
 .../common/entity/wild/peaceful/holladon.ron  | 11 ++++
 assets/common/entity/wild/peaceful/horse.ron  | 11 ++++
 .../common/entity/wild/peaceful/jackalope.ron | 11 ++++
 assets/common/entity/wild/peaceful/kelpie.ron | 11 ++++
 assets/common/entity/wild/peaceful/llama.ron  |  2 +-
 assets/common/entity/wild/peaceful/marlin.ron | 11 ++++
 assets/common/entity/wild/peaceful/moose.ron  |  2 +-
 .../common/entity/wild/peaceful/mouflon.ron   |  2 +-
 assets/common/entity/wild/peaceful/owl.ron    |  2 +-
 .../common/entity/wild/peaceful/pangolin.ron  | 11 ++++
 assets/common/entity/wild/peaceful/parrot.ron | 11 ++++
 .../common/entity/wild/peaceful/peacock.ron   | 11 ++++
 assets/common/entity/wild/peaceful/pig.ron    | 11 ++++
 .../common/entity/wild/peaceful/piranha.ron   | 11 ++++
 .../common/entity/wild/peaceful/porcupine.ron | 11 ++++
 assets/common/entity/wild/peaceful/quokka.ron | 11 ++++
 assets/common/entity/wild/peaceful/rabbit.ron | 11 ++++
 .../common/entity/wild/peaceful/raccoon.ron   | 11 ++++
 assets/common/entity/wild/peaceful/rat.ron    | 11 ++++
 .../entity/wild/peaceful/river_salamander.ron | 11 ++++
 .../common/entity/wild/peaceful/sand_hare.ron | 11 ++++
 .../entity/wild/peaceful/sand_salamander.ron  | 11 ++++
 assets/common/entity/wild/peaceful/sheep.ron  | 11 ++++
 assets/common/entity/wild/peaceful/skunk.ron  | 11 ++++
 .../common/entity/wild/peaceful/squirrel.ron  | 11 ++++
 .../common/entity/wild/peaceful/tortoise.ron  | 11 ++++
 .../common/entity/wild/peaceful/truffler.ron  | 11 ++++
 assets/common/entity/wild/peaceful/turtle.ron | 11 ++++
 .../common/entity/wild/peaceful/tuskram.ron   |  2 +-
 assets/common/entity/wild/peaceful/yak.ron    |  2 +-
 assets/common/entity/wild/peaceful/zebra.ron  | 11 ++++
 assets/world/wildlife/spawn/desert/river.ron  |  2 +-
 .../world/wildlife/spawn/desert/wasteland.ron |  2 +-
 .../wildlife/spawn/temperate/rainforest.ron   |  6 +-
 .../world/wildlife/spawn/temperate/water.ron  |  2 +-
 common/src/generation.rs                      | 55 +++++++++++++------
 world/src/layer/wildlife.rs                   |  4 +-
 world/src/site/settlement/mod.rs              |  3 +-
 151 files changed, 976 insertions(+), 94 deletions(-)
 create mode 100644 assets/common/entity/wild/aggressive/alligator.ron
 create mode 100644 assets/common/entity/wild/aggressive/archaeos.ron
 create mode 100644 assets/common/entity/wild/aggressive/asp.ron
 create mode 100644 assets/common/entity/wild/aggressive/batfox.ron
 create mode 100644 assets/common/entity/wild/aggressive/bear.ron
 create mode 100644 assets/common/entity/wild/aggressive/bonerattler.ron
 create mode 100644 assets/common/entity/wild/aggressive/cockatrice.ron
 create mode 100644 assets/common/entity/wild/aggressive/crocodile.ron
 create mode 100644 assets/common/entity/wild/aggressive/cyclope.ron
 create mode 100644 assets/common/entity/wild/aggressive/deadwood.ron
 create mode 100644 assets/common/entity/wild/aggressive/dodarock.ron
 create mode 100644 assets/common/entity/wild/aggressive/hakulaq.ron
 create mode 100644 assets/common/entity/wild/aggressive/hyena.ron
 create mode 100644 assets/common/entity/wild/aggressive/icepike.ron
 create mode 100644 assets/common/entity/wild/aggressive/lavadrake.ron
 create mode 100644 assets/common/entity/wild/aggressive/lion.ron
 create mode 100644 assets/common/entity/wild/aggressive/maneater.ron
 create mode 100644 assets/common/entity/wild/aggressive/mighty_saurok.ron
 create mode 100644 assets/common/entity/wild/aggressive/monitor.ron
 create mode 100644 assets/common/entity/wild/aggressive/ngoubou.ron
 create mode 100644 assets/common/entity/wild/aggressive/ntouka.ron
 create mode 100644 assets/common/entity/wild/aggressive/occult_saurok.ron
 create mode 100644 assets/common/entity/wild/aggressive/odonto.ron
 create mode 100644 assets/common/entity/wild/aggressive/ogre.ron
 create mode 100644 assets/common/entity/wild/aggressive/roshwalr_boss.ron
 create mode 100644 assets/common/entity/wild/aggressive/saber.ron
 create mode 100644 assets/common/entity/wild/aggressive/sand_raptor.ron
 create mode 100644 assets/common/entity/wild/aggressive/sandshark.ron
 create mode 100644 assets/common/entity/wild/aggressive/sly_saurok.ron
 create mode 100644 assets/common/entity/wild/aggressive/sunlizard.ron
 create mode 100644 assets/common/entity/wild/aggressive/swamp_troll.ron
 create mode 100644 assets/common/entity/wild/aggressive/tarasque.ron
 create mode 100644 assets/common/entity/wild/aggressive/tiger.ron
 create mode 100644 assets/common/entity/wild/aggressive/wood_raptor.ron
 create mode 100644 assets/common/entity/wild/peaceful/antelope.ron
 create mode 100644 assets/common/entity/wild/peaceful/axolotl.ron
 create mode 100644 assets/common/entity/wild/peaceful/beaver.ron
 create mode 100644 assets/common/entity/wild/peaceful/boar.ron
 create mode 100644 assets/common/entity/wild/peaceful/camel.ron
 create mode 100644 assets/common/entity/wild/peaceful/catoblepas.ron
 create mode 100644 assets/common/entity/wild/peaceful/cattle.ron
 create mode 100644 assets/common/entity/wild/peaceful/chicken.ron
 create mode 100644 assets/common/entity/wild/peaceful/clownfish.ron
 create mode 100644 assets/common/entity/wild/peaceful/deer.ron
 create mode 100644 assets/common/entity/wild/peaceful/donkey.ron
 create mode 100644 assets/common/entity/wild/peaceful/duck.ron
 create mode 100644 assets/common/entity/wild/peaceful/forest_fox.ron
 create mode 100644 assets/common/entity/wild/peaceful/frog.ron
 create mode 100644 assets/common/entity/wild/peaceful/fungome.ron
 create mode 100644 assets/common/entity/wild/peaceful/gecko.ron
 create mode 100644 assets/common/entity/wild/peaceful/goat.ron
 create mode 100644 assets/common/entity/wild/peaceful/goose.ron
 create mode 100644 assets/common/entity/wild/peaceful/hirdrasil.ron
 create mode 100644 assets/common/entity/wild/peaceful/holladon.ron
 create mode 100644 assets/common/entity/wild/peaceful/horse.ron
 create mode 100644 assets/common/entity/wild/peaceful/jackalope.ron
 create mode 100644 assets/common/entity/wild/peaceful/kelpie.ron
 create mode 100644 assets/common/entity/wild/peaceful/marlin.ron
 create mode 100644 assets/common/entity/wild/peaceful/pangolin.ron
 create mode 100644 assets/common/entity/wild/peaceful/parrot.ron
 create mode 100644 assets/common/entity/wild/peaceful/peacock.ron
 create mode 100644 assets/common/entity/wild/peaceful/pig.ron
 create mode 100644 assets/common/entity/wild/peaceful/piranha.ron
 create mode 100644 assets/common/entity/wild/peaceful/porcupine.ron
 create mode 100644 assets/common/entity/wild/peaceful/quokka.ron
 create mode 100644 assets/common/entity/wild/peaceful/rabbit.ron
 create mode 100644 assets/common/entity/wild/peaceful/raccoon.ron
 create mode 100644 assets/common/entity/wild/peaceful/rat.ron
 create mode 100644 assets/common/entity/wild/peaceful/river_salamander.ron
 create mode 100644 assets/common/entity/wild/peaceful/sand_hare.ron
 create mode 100644 assets/common/entity/wild/peaceful/sand_salamander.ron
 create mode 100644 assets/common/entity/wild/peaceful/sheep.ron
 create mode 100644 assets/common/entity/wild/peaceful/skunk.ron
 create mode 100644 assets/common/entity/wild/peaceful/squirrel.ron
 create mode 100644 assets/common/entity/wild/peaceful/tortoise.ron
 create mode 100644 assets/common/entity/wild/peaceful/truffler.ron
 create mode 100644 assets/common/entity/wild/peaceful/turtle.ron
 create mode 100644 assets/common/entity/wild/peaceful/zebra.ron

diff --git a/assets/common/entity/dungeon/fallback/boss.ron b/assets/common/entity/dungeon/fallback/boss.ron
index 3f11d76f76..75c4df67af 100644
--- a/assets/common/entity/dungeon/fallback/boss.ron
+++ b/assets/common/entity/dungeon/fallback/boss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Crazy Sheep"),
+    name: Name("Crazy Sheep"),
     body: RandomWith("sheep"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/fallback/enemy.ron b/assets/common/entity/dungeon/fallback/enemy.ron
index 04b2f43071..47f2e8acdf 100644
--- a/assets/common/entity/dungeon/fallback/enemy.ron
+++ b/assets/common/entity/dungeon/fallback/enemy.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Yan Hus"),
+    name: Name("Yan Hus"),
     body: RandomWith("humanoid"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/fallback/miniboss.ron b/assets/common/entity/dungeon/fallback/miniboss.ron
index befb2f8cf6..94112f89f0 100644
--- a/assets/common/entity/dungeon/fallback/miniboss.ron
+++ b/assets/common/entity/dungeon/fallback/miniboss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Big Goose"),
+    name: Name("Big Goose"),
     body: RandomWith("goose"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-0/boss.ron b/assets/common/entity/dungeon/tier-0/boss.ron
index b0cecbc954..b681c19cb5 100644
--- a/assets/common/entity/dungeon/tier-0/boss.ron
+++ b/assets/common/entity/dungeon/tier-0/boss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Harvester"),
+    name: Name("Harvester"),
     body: RandomWith("harvester"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron
index 55829f748f..7ff0555517 100644
--- a/assets/common/entity/dungeon/tier-0/bow.ron
+++ b/assets/common/entity/dungeon/tier-0/bow.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Gnarling Stalker"),
+    name: Name("Gnarling Stalker"),
     body: RandomWith("gnarling"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-0/miniboss.ron b/assets/common/entity/dungeon/tier-0/miniboss.ron
index d17e7fd605..c02abcefee 100644
--- a/assets/common/entity/dungeon/tier-0/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-0/miniboss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Deadwood"),
+    name: Name("Deadwood"),
     body: RandomWith("deadwood"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron
index d71328be25..e30541c047 100644
--- a/assets/common/entity/dungeon/tier-0/spear.ron
+++ b/assets/common/entity/dungeon/tier-0/spear.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Gnarling Mugger"),
+    name: Name("Gnarling Mugger"),
     body: RandomWith("gnarling"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron
index e9d9e4e376..cbcd59a973 100644
--- a/assets/common/entity/dungeon/tier-0/staff.ron
+++ b/assets/common/entity/dungeon/tier-0/staff.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Gnarling Shaman"),
+    name: Name("Gnarling Shaman"),
     body: RandomWith("gnarling"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/tier-1/boss.ron
index 63bacdcfc8..28b829d9bc 100644
--- a/assets/common/entity/dungeon/tier-1/boss.ron
+++ b/assets/common/entity/dungeon/tier-1/boss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Yeti"),
+    name: Name("Yeti"),
     body: RandomWith("yeti"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron
index 06078fb1e2..df8961b483 100644
--- a/assets/common/entity/dungeon/tier-1/bow.ron
+++ b/assets/common/entity/dungeon/tier-1/bow.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Adlet Tracker"),
+    name: Name("Adlet Tracker"),
     body: RandomWith("adlet"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron
index aeab6f2370..f5d548b923 100644
--- a/assets/common/entity/dungeon/tier-1/rat.ron
+++ b/assets/common/entity/dungeon/tier-1/rat.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Rat"),
+    name: Name("Rat"),
     body: RandomWith("rat"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron
index 4fb7d51fa2..882e6ea05d 100644
--- a/assets/common/entity/dungeon/tier-1/spear.ron
+++ b/assets/common/entity/dungeon/tier-1/spear.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Adlet Hunter"),
+    name: Name("Adlet Hunter"),
     body: RandomWith("adlet"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron
index 1c9e8762f5..314e450021 100644
--- a/assets/common/entity/dungeon/tier-1/staff.ron
+++ b/assets/common/entity/dungeon/tier-1/staff.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Adlet Shaman"),
+    name: Name("Adlet Shaman"),
     body: RandomWith("adlet"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-2/boss.ron b/assets/common/entity/dungeon/tier-2/boss.ron
index 2139489071..a9b4c2b26b 100644
--- a/assets/common/entity/dungeon/tier-2/boss.ron
+++ b/assets/common/entity/dungeon/tier-2/boss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Tidal Warrior"),
+    name: Name("Tidal Warrior"),
     body: RandomWith("tidalwarrior"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron
index 7e6c2d6dbc..f780d5c7d0 100644
--- a/assets/common/entity/dungeon/tier-2/bow.ron
+++ b/assets/common/entity/dungeon/tier-2/bow.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Sahagin Sniper"),
+    name: Name("Sahagin Sniper"),
     body: RandomWith("sahagin"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-2/hakulaq.ron b/assets/common/entity/dungeon/tier-2/hakulaq.ron
index 7f9199585a..46bc76a674 100644
--- a/assets/common/entity/dungeon/tier-2/hakulaq.ron
+++ b/assets/common/entity/dungeon/tier-2/hakulaq.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Hakulaq"),
+    name: Name("Hakulaq"),
     body: RandomWith("hakulaq"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron
index 239162caf8..2d867f83fa 100644
--- a/assets/common/entity/dungeon/tier-2/spear.ron
+++ b/assets/common/entity/dungeon/tier-2/spear.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Sahagin Spearman"),
+    name: Name("Sahagin Spearman"),
     body: RandomWith("sahagin"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron
index cba57b3351..e1a883baa7 100644
--- a/assets/common/entity/dungeon/tier-2/staff.ron
+++ b/assets/common/entity/dungeon/tier-2/staff.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Sahagin Sorcerer"),
+    name: Name("Sahagin Sorcerer"),
     body: RandomWith("sahagin"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-3/bonerattler.ron b/assets/common/entity/dungeon/tier-3/bonerattler.ron
index 79d7c5e2d3..0af1afa98f 100644
--- a/assets/common/entity/dungeon/tier-3/bonerattler.ron
+++ b/assets/common/entity/dungeon/tier-3/bonerattler.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Bonerattler"),
+    name: Name("Bonerattler"),
     body: RandomWith("bonerattler"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-3/boss.ron b/assets/common/entity/dungeon/tier-3/boss.ron
index a8b075c9b2..ecc9b398d5 100644
--- a/assets/common/entity/dungeon/tier-3/boss.ron
+++ b/assets/common/entity/dungeon/tier-3/boss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Clay Golem"),
+    name: Name("Clay Golem"),
     body: RandomWith("claygolem"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron
index 445d697338..386a4dc500 100644
--- a/assets/common/entity/dungeon/tier-3/bow.ron
+++ b/assets/common/entity/dungeon/tier-3/bow.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Haniwa Archer"),
+    name: Name("Haniwa Archer"),
     body: RandomWith("haniwa"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-3/sentry.ron b/assets/common/entity/dungeon/tier-3/sentry.ron
index 43c519c834..253e6b6070 100644
--- a/assets/common/entity/dungeon/tier-3/sentry.ron
+++ b/assets/common/entity/dungeon/tier-3/sentry.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Haniwa Sentry"),
+    name: Name("Haniwa Sentry"),
     body: Exact(Object(HaniwaSentry)),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron
index 10614bd9c0..1999cd45af 100644
--- a/assets/common/entity/dungeon/tier-3/spear.ron
+++ b/assets/common/entity/dungeon/tier-3/spear.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Haniwa Guard"),
+    name: Name("Haniwa Guard"),
     body: RandomWith("haniwa"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron
index 5066edc088..d71561c3c5 100644
--- a/assets/common/entity/dungeon/tier-3/staff.ron
+++ b/assets/common/entity/dungeon/tier-3/staff.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Haniwa Sorcerer"),
+    name: Name("Haniwa Sorcerer"),
     body: RandomWith("haniwa"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-4/boss.ron b/assets/common/entity/dungeon/tier-4/boss.ron
index 0d4da80c24..23fdfabe82 100644
--- a/assets/common/entity/dungeon/tier-4/boss.ron
+++ b/assets/common/entity/dungeon/tier-4/boss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Minotaur"),
+    name: Name("Minotaur"),
     body: RandomWith("minotaur"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron
index a2a220054e..f56692f884 100644
--- a/assets/common/entity/dungeon/tier-4/bow.ron
+++ b/assets/common/entity/dungeon/tier-4/bow.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Myrmidon Marksman"),
+    name: Name("Myrmidon Marksman"),
     body: RandomWith("myrmidon"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-4/miniboss.ron b/assets/common/entity/dungeon/tier-4/miniboss.ron
index ffe140c084..352fc3a70e 100644
--- a/assets/common/entity/dungeon/tier-4/miniboss.ron
+++ b/assets/common/entity/dungeon/tier-4/miniboss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Dullahan"),
+    name: Name("Dullahan"),
     body: RandomWith("dullahan"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron
index 1ebf5dec71..579f971a70 100644
--- a/assets/common/entity/dungeon/tier-4/spear.ron
+++ b/assets/common/entity/dungeon/tier-4/spear.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Myrmidon Hoplite"),
+    name: Name("Myrmidon Hoplite"),
     body: RandomWith("myrmidon"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron
index 1f174c1924..e5a800e39a 100644
--- a/assets/common/entity/dungeon/tier-4/staff.ron
+++ b/assets/common/entity/dungeon/tier-4/staff.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Myrmidon Wizard"),
+    name: Name("Myrmidon Wizard"),
     body: RandomWith("myrmidon"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron
index 98c11b8d41..fc58c78a73 100644
--- a/assets/common/entity/dungeon/tier-5/beastmaster.ron
+++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Beastmaster"),
+    name: Name("Beastmaster"),
     body: RandomWith("humanoid"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron
index ebeaebefec..4de7ebd0da 100644
--- a/assets/common/entity/dungeon/tier-5/boss.ron
+++ b/assets/common/entity/dungeon/tier-5/boss.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Mindflayer"),
+    name: Name("Mindflayer"),
     body: RandomWith("mindflayer"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron
index 1b6728c34f..88e29509b0 100644
--- a/assets/common/entity/dungeon/tier-5/cultist.ron
+++ b/assets/common/entity/dungeon/tier-5/cultist.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Cultist"),
+    name: Name("Cultist"),
     body: RandomWith("humanoid"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/hound.ron b/assets/common/entity/dungeon/tier-5/hound.ron
index 0cbf1e7c8b..85c3702bde 100644
--- a/assets/common/entity/dungeon/tier-5/hound.ron
+++ b/assets/common/entity/dungeon/tier-5/hound.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Tamed Darkhound"),
+    name: Name("Tamed Darkhound"),
     body: RandomWith("darkhound"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron
index e68a99f04a..936a938867 100644
--- a/assets/common/entity/dungeon/tier-5/husk.ron
+++ b/assets/common/entity/dungeon/tier-5/husk.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Cultist Husk"),
+    name: Name("Cultist Husk"),
     body: RandomWith("husk"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/husk_brute.ron b/assets/common/entity/dungeon/tier-5/husk_brute.ron
index 7e77ad8aca..9c16fa0721 100644
--- a/assets/common/entity/dungeon/tier-5/husk_brute.ron
+++ b/assets/common/entity/dungeon/tier-5/husk_brute.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Husk Brute"),
+    name: Name("Husk Brute"),
     body: RandomWith("husk_brute"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/turret.ron b/assets/common/entity/dungeon/tier-5/turret.ron
index 9470ff47ac..c31a081875 100644
--- a/assets/common/entity/dungeon/tier-5/turret.ron
+++ b/assets/common/entity/dungeon/tier-5/turret.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Possessed Turret"),
+    name: Name("Possessed Turret"),
     body: Exact(Object(Crossbow)),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron
index 3119ad911f..0ee439d4df 100644
--- a/assets/common/entity/dungeon/tier-5/warlock.ron
+++ b/assets/common/entity/dungeon/tier-5/warlock.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Cultist Warlock"),
+    name: Name("Cultist Warlock"),
     body: RandomWith("cultist_warlock"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron
index 07909a78df..22bbe5741f 100644
--- a/assets/common/entity/dungeon/tier-5/warlord.ron
+++ b/assets/common/entity/dungeon/tier-5/warlord.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Cultist Warlord"),
+    name: Name("Cultist Warlord"),
     body: RandomWith("cultist_warlord"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/template.ron b/assets/common/entity/template.ron
index 0a35c81854..5743f305a3 100644
--- a/assets/common/entity/template.ron
+++ b/assets/common/entity/template.ron
@@ -1,6 +1,9 @@
 EntityConfig (
     /// Name of Entity
-    name: Some("Paddy"),
+    /// Can be Name(String) with given name
+    /// or Automatic which will call automatic name depend on Body
+    /// or Uninit (means it should be specified somewhere in code)
+    name: Name("Paddy"),
 
     /// Body
     /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such)
@@ -18,7 +21,7 @@ EntityConfig (
     loot: LootTable("common.loot_tables.humanoids"),
 
     /// Hands:
-    /// - TwoHanded(ItemSpec) for one weapon 2h or 1h,
+    /// - TwoHanded(ItemSpec) for one 2h or 1h weapon,
     /// - Paired(ItemSpec) for two 1h weapons aka berserker mode,
     /// - Mix {
     ///     mainhand: ItemSpec,
diff --git a/assets/common/entity/village/dummy.ron b/assets/common/entity/village/dummy.ron
index d9a83aa730..96e7b9d467 100644
--- a/assets/common/entity/village/dummy.ron
+++ b/assets/common/entity/village/dummy.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Training Dummy"),
+    name: Name("Training Dummy"),
     body: Exact(Object(TrainingDummy)),
     alignment: Alignment(Passive),
 
diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron
index d1b7cb2754..fc31245bca 100644
--- a/assets/common/entity/village/guard.ron
+++ b/assets/common/entity/village/guard.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Guard"),
+    name: Name("Guard"),
     body: RandomWith("humanoid"),
     alignment: Alignment(Npc),
 
diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron
index 09aaefc238..d0632f737b 100644
--- a/assets/common/entity/village/merchant.ron
+++ b/assets/common/entity/village/merchant.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Merchant"),
+    name: Name("Merchant"),
     body: RandomWith("humanoid"),
     alignment: Alignment(Npc),
 
diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron
index eb7e7fbe92..4335b0df45 100644
--- a/assets/common/entity/village/villager.ron
+++ b/assets/common/entity/village/villager.ron
@@ -1,6 +1,5 @@
 EntityConfig (
-    // name is specified outsite
-    name: None,
+    name: Automatic,
     body: RandomWith("humanoid"),
     alignment: Alignment(Npc),
 
diff --git a/assets/common/entity/wild/aggressive/alligator.ron b/assets/common/entity/wild/aggressive/alligator.ron
new file mode 100644
index 0000000000..9e63eb22a2
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/alligator.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("alligator"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/archaeos.ron b/assets/common/entity/wild/aggressive/archaeos.ron
new file mode 100644
index 0000000000..ef9fd560d9
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/archaeos.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("archaeos"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/asp.ron b/assets/common/entity/wild/aggressive/asp.ron
new file mode 100644
index 0000000000..f40cbcb3ee
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/asp.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("asp"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/batfox.ron b/assets/common/entity/wild/aggressive/batfox.ron
new file mode 100644
index 0000000000..dd0c68e9a3
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/batfox.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("batfox"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/bear.ron b/assets/common/entity/wild/aggressive/bear.ron
new file mode 100644
index 0000000000..66d3bd0042
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/bear.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("bear"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/bonerattler.ron b/assets/common/entity/wild/aggressive/bonerattler.ron
new file mode 100644
index 0000000000..e5fa5910b1
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/bonerattler.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("bonerattler"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/cockatrice.ron b/assets/common/entity/wild/aggressive/cockatrice.ron
new file mode 100644
index 0000000000..a367c30fcc
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/cockatrice.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("cockatrice"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/crocodile.ron b/assets/common/entity/wild/aggressive/crocodile.ron
new file mode 100644
index 0000000000..b839d4b4e5
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/crocodile.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("crocodile"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/cyclope.ron b/assets/common/entity/wild/aggressive/cyclope.ron
new file mode 100644
index 0000000000..2e679b9458
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/cyclope.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("cyclops"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/deadwood.ron b/assets/common/entity/wild/aggressive/deadwood.ron
new file mode 100644
index 0000000000..ebbcadf2e6
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/deadwood.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("deadwood"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/dodarock.ron b/assets/common/entity/wild/aggressive/dodarock.ron
new file mode 100644
index 0000000000..444d4ae085
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/dodarock.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("dodarock"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/dreadhorn.ron b/assets/common/entity/wild/aggressive/dreadhorn.ron
index e348d57ad5..e42bc14392 100644
--- a/assets/common/entity/wild/aggressive/dreadhorn.ron
+++ b/assets/common/entity/wild/aggressive/dreadhorn.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Dreadhorn"),
+    name: Automatic,
     body: RandomWith("dreadhorn"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/frostfang.ron b/assets/common/entity/wild/aggressive/frostfang.ron
index f3c7376892..c315373e4c 100644
--- a/assets/common/entity/wild/aggressive/frostfang.ron
+++ b/assets/common/entity/wild/aggressive/frostfang.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Frostfang"),
+    name: Automatic,
     body: RandomWith("frostfang"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/grolgar.ron b/assets/common/entity/wild/aggressive/grolgar.ron
index 47863b0fa8..2b868ac934 100644
--- a/assets/common/entity/wild/aggressive/grolgar.ron
+++ b/assets/common/entity/wild/aggressive/grolgar.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Grolgar"),
+    name: Automatic,
     body: RandomWith("grolgar"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/hakulaq.ron b/assets/common/entity/wild/aggressive/hakulaq.ron
new file mode 100644
index 0000000000..68e4b77697
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/hakulaq.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("hakulaq"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/hyena.ron b/assets/common/entity/wild/aggressive/hyena.ron
new file mode 100644
index 0000000000..dea8ade1f6
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/hyena.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("hyena"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/icepike.ron b/assets/common/entity/wild/aggressive/icepike.ron
new file mode 100644
index 0000000000..2cb568b75d
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/icepike.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("icepike"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/lavadrake.ron b/assets/common/entity/wild/aggressive/lavadrake.ron
new file mode 100644
index 0000000000..43f9dfd981
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/lavadrake.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("lavadrake"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/lion.ron b/assets/common/entity/wild/aggressive/lion.ron
new file mode 100644
index 0000000000..703577cfa1
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/lion.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("lion"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/mammoth.ron b/assets/common/entity/wild/aggressive/mammoth.ron
index 744cac3adb..6bf88c0e38 100644
--- a/assets/common/entity/wild/aggressive/mammoth.ron
+++ b/assets/common/entity/wild/aggressive/mammoth.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Mammoth"),
+    name: Automatic,
     body: RandomWith("mammoth"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/maneater.ron b/assets/common/entity/wild/aggressive/maneater.ron
new file mode 100644
index 0000000000..92ff16fa66
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/maneater.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("maneater"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/mighty_saurok.ron b/assets/common/entity/wild/aggressive/mighty_saurok.ron
new file mode 100644
index 0000000000..4fd00cc454
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/mighty_saurok.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("saurok_mighty"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/monitor.ron b/assets/common/entity/wild/aggressive/monitor.ron
new file mode 100644
index 0000000000..2195fd63de
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/monitor.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("monitor"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/mountain_troll.ron b/assets/common/entity/wild/aggressive/mountain_troll.ron
index 8aad271d4c..ef24ea5561 100644
--- a/assets/common/entity/wild/aggressive/mountain_troll.ron
+++ b/assets/common/entity/wild/aggressive/mountain_troll.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Mountain Troll"),
+    name: Automatic,
     body: RandomWith("troll_mountain"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/ngoubou.ron b/assets/common/entity/wild/aggressive/ngoubou.ron
new file mode 100644
index 0000000000..191d6642ac
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/ngoubou.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("ngoubou"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/ntouka.ron b/assets/common/entity/wild/aggressive/ntouka.ron
new file mode 100644
index 0000000000..de2e848273
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/ntouka.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("ntouka"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/occult_saurok.ron b/assets/common/entity/wild/aggressive/occult_saurok.ron
new file mode 100644
index 0000000000..332ce79602
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/occult_saurok.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("saurok_occult"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/odonto.ron b/assets/common/entity/wild/aggressive/odonto.ron
new file mode 100644
index 0000000000..a8cba7c234
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/odonto.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("odonto"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/ogre.ron b/assets/common/entity/wild/aggressive/ogre.ron
new file mode 100644
index 0000000000..7e3e655cfa
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/ogre.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("ogre"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/rocksnapper.ron b/assets/common/entity/wild/aggressive/rocksnapper.ron
index 089828f836..b003b4f4d7 100644
--- a/assets/common/entity/wild/aggressive/rocksnapper.ron
+++ b/assets/common/entity/wild/aggressive/rocksnapper.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Rock Snapper"),
+    name: Automatic,
     body: RandomWith("rocksnapper"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/roshwalr.ron b/assets/common/entity/wild/aggressive/roshwalr.ron
index cda5c15ee3..cee362c67b 100644
--- a/assets/common/entity/wild/aggressive/roshwalr.ron
+++ b/assets/common/entity/wild/aggressive/roshwalr.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Roshwalr"),
+    name: Automatic,
     body: Exact(QuadrupedMedium(Body(
         species: Roshwalr,
         body_type: Male,
diff --git a/assets/common/entity/wild/aggressive/roshwalr_boss.ron b/assets/common/entity/wild/aggressive/roshwalr_boss.ron
new file mode 100644
index 0000000000..2f105614a1
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/roshwalr_boss.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: Exact(QuadrupedMedium(Body(species: Roshwalr, body_type: Female))),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/saber.ron b/assets/common/entity/wild/aggressive/saber.ron
new file mode 100644
index 0000000000..08e39d6245
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/saber.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("sabertooth"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/sand_raptor.ron b/assets/common/entity/wild/aggressive/sand_raptor.ron
new file mode 100644
index 0000000000..82f5a1abf3
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/sand_raptor.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("raptor_sand"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/sandshark.ron b/assets/common/entity/wild/aggressive/sandshark.ron
new file mode 100644
index 0000000000..23df7e3f49
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/sandshark.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("sandshark"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/sly_saurok.ron b/assets/common/entity/wild/aggressive/sly_saurok.ron
new file mode 100644
index 0000000000..d52795bda5
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/sly_saurok.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("saurok_sly"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/snow_leopard.ron b/assets/common/entity/wild/aggressive/snow_leopard.ron
index 95e9d44d22..fbdeb737a3 100644
--- a/assets/common/entity/wild/aggressive/snow_leopard.ron
+++ b/assets/common/entity/wild/aggressive/snow_leopard.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Snow Leopard"),
+    name: Automatic,
     body: RandomWith("snowleopard"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/snow_raptor.ron b/assets/common/entity/wild/aggressive/snow_raptor.ron
index 53cfcc3726..f892f197a3 100644
--- a/assets/common/entity/wild/aggressive/snow_raptor.ron
+++ b/assets/common/entity/wild/aggressive/snow_raptor.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Snow Raptor"),
+    name: Automatic,
     body: RandomWith("raptor_snow"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/sunlizard.ron b/assets/common/entity/wild/aggressive/sunlizard.ron
new file mode 100644
index 0000000000..0d5d11803b
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/sunlizard.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("sunlizard"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/swamp_troll.ron b/assets/common/entity/wild/aggressive/swamp_troll.ron
new file mode 100644
index 0000000000..d8410db0e0
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/swamp_troll.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("troll_swamp"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/tarasque.ron b/assets/common/entity/wild/aggressive/tarasque.ron
new file mode 100644
index 0000000000..9240f05109
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/tarasque.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("tarasque"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/tiger.ron b/assets/common/entity/wild/aggressive/tiger.ron
new file mode 100644
index 0000000000..309c9e7df7
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/tiger.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("tiger"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/wendigo.ron b/assets/common/entity/wild/aggressive/wendigo.ron
index 9cae3b1e53..8fef962284 100644
--- a/assets/common/entity/wild/aggressive/wendigo.ron
+++ b/assets/common/entity/wild/aggressive/wendigo.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Wendigo"),
+    name: Automatic,
     body: RandomWith("wendigo"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/wolf.ron b/assets/common/entity/wild/aggressive/wolf.ron
index b343a1c642..57509f6382 100644
--- a/assets/common/entity/wild/aggressive/wolf.ron
+++ b/assets/common/entity/wild/aggressive/wolf.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Wolf"),
+    name: Automatic,
     body: RandomWith("wolf"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/aggressive/wood_raptor.ron b/assets/common/entity/wild/aggressive/wood_raptor.ron
new file mode 100644
index 0000000000..fbc093e14a
--- /dev/null
+++ b/assets/common/entity/wild/aggressive/wood_raptor.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("raptor_wood"),
+    alignment: Alignment(Enemy),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/aggressive/yale.ron b/assets/common/entity/wild/aggressive/yale.ron
index fafe4c20c9..0ea8d6dc09 100644
--- a/assets/common/entity/wild/aggressive/yale.ron
+++ b/assets/common/entity/wild/aggressive/yale.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Yale"),
+    name: Automatic,
     body: RandomWith("yale"),
     alignment: Alignment(Enemy),
 
diff --git a/assets/common/entity/wild/peaceful/alpaca.ron b/assets/common/entity/wild/peaceful/alpaca.ron
index d29584c4f7..b1b06b53a9 100644
--- a/assets/common/entity/wild/peaceful/alpaca.ron
+++ b/assets/common/entity/wild/peaceful/alpaca.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Alpaca"),
+    name: Automatic,
     body: RandomWith("alpaca"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/antelope.ron b/assets/common/entity/wild/peaceful/antelope.ron
new file mode 100644
index 0000000000..573e457850
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/antelope.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("antelope"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/arctic_fox.ron b/assets/common/entity/wild/peaceful/arctic_fox.ron
index 45816ff39b..4a289c0644 100644
--- a/assets/common/entity/wild/peaceful/arctic_fox.ron
+++ b/assets/common/entity/wild/peaceful/arctic_fox.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Fox"),
+    name: Name("Arctic Fox"),
     body: Exact(QuadrupedSmall(Body(species: Fox, body_type: Female))),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/arctic_hare.ron b/assets/common/entity/wild/peaceful/arctic_hare.ron
index 2e4265de09..566b5c15e2 100644
--- a/assets/common/entity/wild/peaceful/arctic_hare.ron
+++ b/assets/common/entity/wild/peaceful/arctic_hare.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Hare"),
+    name: Name("Arctic Hare"),
     body: Exact(QuadrupedSmall(Body(species: Hare, body_type: Female))),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/axolotl.ron b/assets/common/entity/wild/peaceful/axolotl.ron
new file mode 100644
index 0000000000..f7b04e723e
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/axolotl.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("axolotl"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/beaver.ron b/assets/common/entity/wild/peaceful/beaver.ron
new file mode 100644
index 0000000000..fb7ed84625
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/beaver.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("beaver"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/boar.ron b/assets/common/entity/wild/peaceful/boar.ron
new file mode 100644
index 0000000000..bba9eac893
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/boar.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("boar"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/camel.ron b/assets/common/entity/wild/peaceful/camel.ron
new file mode 100644
index 0000000000..fd574f17e1
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/camel.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("camel"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/catoblepas.ron b/assets/common/entity/wild/peaceful/catoblepas.ron
new file mode 100644
index 0000000000..d15457d45c
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/catoblepas.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("catoblepas"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/cattle.ron b/assets/common/entity/wild/peaceful/cattle.ron
new file mode 100644
index 0000000000..72cc745057
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/cattle.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("cattle"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/chicken.ron b/assets/common/entity/wild/peaceful/chicken.ron
new file mode 100644
index 0000000000..7d65a663ee
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/chicken.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("chicken"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/clownfish.ron b/assets/common/entity/wild/peaceful/clownfish.ron
new file mode 100644
index 0000000000..76c8698071
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/clownfish.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("clownfish"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/deer.ron b/assets/common/entity/wild/peaceful/deer.ron
new file mode 100644
index 0000000000..0b12f635ca
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/deer.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("deer"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/donkey.ron b/assets/common/entity/wild/peaceful/donkey.ron
new file mode 100644
index 0000000000..5838cf2710
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/donkey.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("donkey"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/duck.ron b/assets/common/entity/wild/peaceful/duck.ron
new file mode 100644
index 0000000000..c0f9283508
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/duck.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("duck"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/eagle.ron b/assets/common/entity/wild/peaceful/eagle.ron
index aae26839af..093606f786 100644
--- a/assets/common/entity/wild/peaceful/eagle.ron
+++ b/assets/common/entity/wild/peaceful/eagle.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Eagle"),
+    name: Automatic,
     body: RandomWith("eagle"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/forest_fox.ron b/assets/common/entity/wild/peaceful/forest_fox.ron
new file mode 100644
index 0000000000..f2a7e88651
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/forest_fox.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Name("Forest Fox"),
+    body: Exact(QuadrupedSmall(Body(species: Fox, body_type: Male))),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/frog.ron b/assets/common/entity/wild/peaceful/frog.ron
new file mode 100644
index 0000000000..6bc65393f6
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/frog.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("frog"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/fungome.ron b/assets/common/entity/wild/peaceful/fungome.ron
new file mode 100644
index 0000000000..96540f5d61
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/fungome.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("fungome"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/gecko.ron b/assets/common/entity/wild/peaceful/gecko.ron
new file mode 100644
index 0000000000..830c51cb0d
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/gecko.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("gecko"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/goat.ron b/assets/common/entity/wild/peaceful/goat.ron
new file mode 100644
index 0000000000..9f33ed002e
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/goat.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("goat"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/goose.ron b/assets/common/entity/wild/peaceful/goose.ron
new file mode 100644
index 0000000000..294f05754a
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/goose.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("goose"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/highland.ron b/assets/common/entity/wild/peaceful/highland.ron
index 3ddd070faf..ef992cd8fb 100644
--- a/assets/common/entity/wild/peaceful/highland.ron
+++ b/assets/common/entity/wild/peaceful/highland.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Highland"),
+    name: Automatic,
     body: RandomWith("highland"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/hirdrasil.ron b/assets/common/entity/wild/peaceful/hirdrasil.ron
new file mode 100644
index 0000000000..c97924afd9
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/hirdrasil.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("hirdrasil"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/holladon.ron b/assets/common/entity/wild/peaceful/holladon.ron
new file mode 100644
index 0000000000..a57248dce9
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/holladon.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("holladon"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/horse.ron b/assets/common/entity/wild/peaceful/horse.ron
new file mode 100644
index 0000000000..b42067c4c8
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/horse.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("horse"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/jackalope.ron b/assets/common/entity/wild/peaceful/jackalope.ron
new file mode 100644
index 0000000000..130aad374a
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/jackalope.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("jackalope"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/kelpie.ron b/assets/common/entity/wild/peaceful/kelpie.ron
new file mode 100644
index 0000000000..1062c5bedb
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/kelpie.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("kelpie"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/llama.ron b/assets/common/entity/wild/peaceful/llama.ron
index 2ca5721c68..3b40a2149e 100644
--- a/assets/common/entity/wild/peaceful/llama.ron
+++ b/assets/common/entity/wild/peaceful/llama.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Llama"),
+    name: Automatic,
     body: RandomWith("llama"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/marlin.ron b/assets/common/entity/wild/peaceful/marlin.ron
new file mode 100644
index 0000000000..e127c8aeba
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/marlin.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("marlin"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/moose.ron b/assets/common/entity/wild/peaceful/moose.ron
index f720464d76..47953674fc 100644
--- a/assets/common/entity/wild/peaceful/moose.ron
+++ b/assets/common/entity/wild/peaceful/moose.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Moose"),
+    name: Automatic,
     body: RandomWith("moose"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/mouflon.ron b/assets/common/entity/wild/peaceful/mouflon.ron
index 940edec652..c324dcbba5 100644
--- a/assets/common/entity/wild/peaceful/mouflon.ron
+++ b/assets/common/entity/wild/peaceful/mouflon.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Mouflon"),
+    name: Automatic,
     body: RandomWith("mouflon"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/owl.ron b/assets/common/entity/wild/peaceful/owl.ron
index f4cfc3118a..dd23ad327a 100644
--- a/assets/common/entity/wild/peaceful/owl.ron
+++ b/assets/common/entity/wild/peaceful/owl.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Owl"),
+    name: Automatic,
     body: RandomWith("owl"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/pangolin.ron b/assets/common/entity/wild/peaceful/pangolin.ron
new file mode 100644
index 0000000000..ba207dd831
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/pangolin.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("pangolin"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/parrot.ron b/assets/common/entity/wild/peaceful/parrot.ron
new file mode 100644
index 0000000000..d680c71647
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/parrot.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("parrot"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/peacock.ron b/assets/common/entity/wild/peaceful/peacock.ron
new file mode 100644
index 0000000000..b34c2e830d
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/peacock.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("peacock"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/pig.ron b/assets/common/entity/wild/peaceful/pig.ron
new file mode 100644
index 0000000000..3c20ae39cd
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/pig.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("pig"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/piranha.ron b/assets/common/entity/wild/peaceful/piranha.ron
new file mode 100644
index 0000000000..dc1f92651b
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/piranha.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("piranha"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/porcupine.ron b/assets/common/entity/wild/peaceful/porcupine.ron
new file mode 100644
index 0000000000..79fdade2b9
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/porcupine.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("porcupine"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/quokka.ron b/assets/common/entity/wild/peaceful/quokka.ron
new file mode 100644
index 0000000000..e25c15cb3c
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/quokka.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("quokka"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/rabbit.ron b/assets/common/entity/wild/peaceful/rabbit.ron
new file mode 100644
index 0000000000..23ff937e6e
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/rabbit.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("rabbit"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/raccoon.ron b/assets/common/entity/wild/peaceful/raccoon.ron
new file mode 100644
index 0000000000..b941c98a24
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/raccoon.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("raccoon"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/rat.ron b/assets/common/entity/wild/peaceful/rat.ron
new file mode 100644
index 0000000000..d911990b8b
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/rat.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("rat"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/river_salamander.ron b/assets/common/entity/wild/peaceful/river_salamander.ron
new file mode 100644
index 0000000000..3f2914df60
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/river_salamander.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Name("River Salamander"),
+    body: Exact(QuadrupedLow(Body(species: Salamander, body_type: Female))),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/sand_hare.ron b/assets/common/entity/wild/peaceful/sand_hare.ron
new file mode 100644
index 0000000000..84a417974c
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/sand_hare.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Name("Sand Hare"),
+    body: Exact(QuadrupedSmall(Body(species: Hare, body_type: Male))),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/sand_salamander.ron b/assets/common/entity/wild/peaceful/sand_salamander.ron
new file mode 100644
index 0000000000..2740438c2b
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/sand_salamander.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Name("Desert Salamander"),
+    body: Exact(QuadrupedLow(Body(species: Salamander, body_type: Male))),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/sheep.ron b/assets/common/entity/wild/peaceful/sheep.ron
new file mode 100644
index 0000000000..fff177dfd3
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/sheep.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("sheep"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/skunk.ron b/assets/common/entity/wild/peaceful/skunk.ron
new file mode 100644
index 0000000000..6e782adcfb
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/skunk.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("skunk"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/squirrel.ron b/assets/common/entity/wild/peaceful/squirrel.ron
new file mode 100644
index 0000000000..9daac3cd8e
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/squirrel.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("squirrel"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/tortoise.ron b/assets/common/entity/wild/peaceful/tortoise.ron
new file mode 100644
index 0000000000..4104f1c43a
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/tortoise.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("tortoise"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/truffler.ron b/assets/common/entity/wild/peaceful/truffler.ron
new file mode 100644
index 0000000000..c0c9adfc1f
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/truffler.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("truffler"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/turtle.ron b/assets/common/entity/wild/peaceful/turtle.ron
new file mode 100644
index 0000000000..4b55182fa6
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/turtle.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("turtle"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/common/entity/wild/peaceful/tuskram.ron b/assets/common/entity/wild/peaceful/tuskram.ron
index bd4bc62f6c..32c091db3a 100644
--- a/assets/common/entity/wild/peaceful/tuskram.ron
+++ b/assets/common/entity/wild/peaceful/tuskram.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Tuskram"),
+    name: Automatic,
     body: RandomWith("tuskram"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/yak.ron b/assets/common/entity/wild/peaceful/yak.ron
index 32ffaba58b..95323b48d2 100644
--- a/assets/common/entity/wild/peaceful/yak.ron
+++ b/assets/common/entity/wild/peaceful/yak.ron
@@ -1,5 +1,5 @@
 EntityConfig (
-    name: Some("Yak"),
+    name: Automatic,
     body: RandomWith("yak"),
     alignment: Alignment(Wild),
 
diff --git a/assets/common/entity/wild/peaceful/zebra.ron b/assets/common/entity/wild/peaceful/zebra.ron
new file mode 100644
index 0000000000..fd7667965b
--- /dev/null
+++ b/assets/common/entity/wild/peaceful/zebra.ron
@@ -0,0 +1,11 @@
+EntityConfig (
+    name: Automatic,
+    body: RandomWith("zebra"),
+    alignment: Alignment(Wild),
+
+    loot: Uninit,
+
+    hands: Uninit,
+
+    meta: [],
+)
diff --git a/assets/world/wildlife/spawn/desert/river.ron b/assets/world/wildlife/spawn/desert/river.ron
index 8b60349da6..a032786de7 100644
--- a/assets/world/wildlife/spawn/desert/river.ron
+++ b/assets/world/wildlife/spawn/desert/river.ron
@@ -4,7 +4,7 @@ SpawnEntry (
     rules: [
         Pack(
             groups: [
-                (1, (1, 1, "common.entity.wild.agressive.crocodile")),
+                (1, (1, 1, "common.entity.wild.aggressive.crocodile")),
             ],
             is_underwater: false,
             day_period: [Night, Morning, Noon, Evening],
diff --git a/assets/world/wildlife/spawn/desert/wasteland.ron b/assets/world/wildlife/spawn/desert/wasteland.ron
index f42867d2ed..2752064ade 100644
--- a/assets/world/wildlife/spawn/desert/wasteland.ron
+++ b/assets/world/wildlife/spawn/desert/wasteland.ron
@@ -14,7 +14,7 @@ SpawnEntry (
                 (20, (1, 1, "common.entity.wild.aggressive.ntouka")),
                 (20, (1, 1, "common.entity.wild.aggressive.archaeos")),
                 // Ultra_rare
-                (1, (1, 1, "common.entity.wild.agressive.roshwalr_boss")),
+                (1, (1, 1, "common.entity.wild.aggressive.roshwalr_boss")),
             ],
             is_underwater: false,
             day_period: [Night, Morning, Noon, Evening],
diff --git a/assets/world/wildlife/spawn/temperate/rainforest.ron b/assets/world/wildlife/spawn/temperate/rainforest.ron
index 317870f474..c60b19cf5e 100644
--- a/assets/world/wildlife/spawn/temperate/rainforest.ron
+++ b/assets/world/wildlife/spawn/temperate/rainforest.ron
@@ -25,10 +25,10 @@ SpawnEntry (
                 (2, (1, 1, "common.entity.wild.peaceful.goose")),
                 (2, (1, 1, "common.entity.wild.peaceful.peacock")),
                 (2, (1, 1, "common.entity.wild.peaceful.skunk")),
-                (2, (1, 1, "common.entity.wild.peaceful.raccon")),
+                (2, (1, 1, "common.entity.wild.peaceful.raccoon")),
                 (2, (1, 1, "common.entity.wild.peaceful.catoblepas")),
                 (2, (1, 1, "common.entity.wild.peaceful.turtle")),
-                (2, (1, 1, "common.entity.wild.peaceful.hidrasil")),
+                (2, (1, 1, "common.entity.wild.peaceful.hirdrasil")),
                 (2, (1, 1, "common.entity.wild.peaceful.truffler")),
             ],
             is_underwater: false,
@@ -39,7 +39,7 @@ SpawnEntry (
                 // Solitary
                 (1, (1, 1, "common.entity.wild.aggressive.batfox")),
                 (5, (1, 1, "common.entity.wild.peaceful.forest_fox")),
-                (5, (1, 1, "common.entity.wild.peaceful.raccon")),
+                (5, (1, 1, "common.entity.wild.peaceful.raccoon")),
                 // Pack
                 (5, (1, 3, "common.entity.wild.peaceful.rat")),
                 (5, (1, 3, "common.entity.wild.peaceful.squirrel")),
diff --git a/assets/world/wildlife/spawn/temperate/water.ron b/assets/world/wildlife/spawn/temperate/water.ron
index 55be4600d2..9906d3fa64 100644
--- a/assets/world/wildlife/spawn/temperate/water.ron
+++ b/assets/world/wildlife/spawn/temperate/water.ron
@@ -5,7 +5,7 @@ SpawnEntry (
         Pack(
             groups: [
                 (1, (3, 4, "common.entity.wild.peaceful.marlin")),
-                (1, (3, 4, "common.entity.wild.peaceful.pirahna")),
+                (1, (3, 4, "common.entity.wild.peaceful.piranha")),
                 (1, (3, 4, "common.entity.wild.peaceful.clownfish")),
             ],
             is_underwater: true,
diff --git a/common/src/generation.rs b/common/src/generation.rs
index 32b7fde77b..f587a10f28 100644
--- a/common/src/generation.rs
+++ b/common/src/generation.rs
@@ -14,16 +14,16 @@ use serde::Deserialize;
 use vek::*;
 
 #[derive(Debug, Deserialize, Clone)]
-enum BodyBuilder {
-    RandomWith(String),
-    Exact(Body),
+enum NameKind {
+    Name(String),
+    Automatic,
     Uninit,
 }
 
 #[derive(Debug, Deserialize, Clone)]
-enum LootKind {
-    Item(String),
-    LootTable(String),
+enum BodyBuilder {
+    RandomWith(String),
+    Exact(Body),
     Uninit,
 }
 
@@ -34,9 +34,10 @@ enum AlignmentMark {
 }
 
 #[derive(Debug, Deserialize, Clone)]
-enum Meta {
-    LoadoutAsset(String),
-    SkillSetAsset(String),
+enum LootKind {
+    Item(String),
+    LootTable(String),
+    Uninit,
 }
 
 #[derive(Debug, Deserialize, Clone)]
@@ -50,9 +51,15 @@ enum Hands {
     Uninit,
 }
 
+#[derive(Debug, Deserialize, Clone)]
+enum Meta {
+    LoadoutAsset(String),
+    SkillSetAsset(String),
+}
+
 #[derive(Debug, Deserialize, Clone)]
 pub struct EntityConfig {
-    name: Option<String>,
+    name: NameKind,
     body: BodyBuilder,
     alignment: AlignmentMark,
     loot: LootKind,
@@ -140,10 +147,6 @@ impl EntityInfo {
             meta,
         } = config;
 
-        if let Some(name) = name {
-            self = self.with_name(name);
-        }
-
         match body {
             BodyBuilder::RandomWith(string) => {
                 let npc::NpcBody(_body_kind, mut body_creator) =
@@ -159,6 +162,17 @@ impl EntityInfo {
             BodyBuilder::Uninit => {},
         }
 
+        // NOTE: set name after body, as it's used with automatic name
+        match name {
+            NameKind::Name(name) => {
+                self = self.with_name(name);
+            },
+            NameKind::Automatic => {
+                self = self.with_automatic_name();
+            },
+            NameKind::Uninit => {},
+        }
+
         if let AlignmentMark::Alignment(alignment) = alignment {
             self = self.with_alignment(alignment);
         }
@@ -381,8 +395,8 @@ mod tests {
                 hands,
                 loot,
                 body,
+                name,
                 meta,
-                name: _name,           // can't fail if serialized, it's a boring String
                 alignment: _alignment, // can't fail if serialized, it's a boring enum
             } = EntityConfig::from_asset_expect(config_asset);
 
@@ -409,7 +423,16 @@ mod tests {
                         });
                     let _ = body_creator();
                 },
-                BodyBuilder::Exact { .. } | BodyBuilder::Uninit => {},
+                BodyBuilder::Uninit => {
+                    if let NameKind::Automatic = name {
+                        // there is a big chance to call automatic name
+                        // when body is yet undefined
+                        //
+                        // use .with_automatic_name() in code explicitly
+                        panic!("Used Automatic name with Uninit body in {}", config_asset);
+                    }
+                },
+                BodyBuilder::Exact { .. } => {},
             }
 
             match loot {
diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs
index ad81f61fa4..5492f04261 100644
--- a/world/src/layer/wildlife.rs
+++ b/world/src/layer/wildlife.rs
@@ -20,7 +20,7 @@ const BASE_DENSITY: f32 = 1.0e-5; // Base wildlife density
 
 #[derive(Clone, Debug, Deserialize)]
 pub struct SpawnEntry {
-    /// User-facing info for wiki, statistical tools.
+    /// User-facing info for wiki, statistical tools, etc.
     pub name: String,
     pub note: String,
     /// Rules describing what and when to spawn
@@ -309,7 +309,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
                     }) {
                         let mut entity = entity.clone();
                         entity.pos += offs_wpos2d.with_z(solid_end).map(|e| e as f32);
-                        supplement.add_entity(entity.with_automatic_name());
+                        supplement.add_entity(entity);
                     }
                 }
             }
diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs
index 2f18b78b0a..3c0c751063 100644
--- a/world/src/site/settlement/mod.rs
+++ b/world/src/site/settlement/mod.rs
@@ -1022,8 +1022,7 @@ fn human(pos: Vec3<f32>, economy: &SiteInformation, dynamic_rng: &mut impl Rng)
             .with_asset_expect("common.entity.village.merchant"),
         _ => entity
             .with_lazy_loadout(villager_loadout)
-            .with_asset_expect("common.entity.village.villager")
-            .with_automatic_name(),
+            .with_asset_expect("common.entity.village.villager"),
     }
 }
 

From b58211a2f5bf0f62a66d8f806bcff24473c9e204 Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Mon, 12 Jul 2021 13:46:53 +0300
Subject: [PATCH 08/12] Move spawn_manifest to Index

---
 world/src/index.rs          |  7 ++++++
 world/src/layer/wildlife.rs | 48 +++++++++++++++++--------------------
 2 files changed, 29 insertions(+), 26 deletions(-)

diff --git a/world/src/index.rs b/world/src/index.rs
index ab01a20452..383d150e1b 100644
--- a/world/src/index.rs
+++ b/world/src/index.rs
@@ -1,4 +1,5 @@
 use crate::{
+    layer::wildlife::{self, DensityFn, SpawnEntry},
     site::{economy::TradeInformation, Site},
     Colors,
 };
@@ -19,6 +20,7 @@ pub struct Index {
     pub noise: Noise,
     pub sites: Store<Site>,
     pub trade: TradeInformation,
+    pub wildlife_spawn_handles: Vec<(AssetHandle<SpawnEntry>, DensityFn)>,
     colors: AssetHandle<Arc<Colors>>,
 }
 
@@ -58,6 +60,10 @@ impl Index {
     /// NOTE: Panics if the color manifest cannot be loaded.
     pub fn new(seed: u32) -> Self {
         let colors = Arc::<Colors>::load_expect(WORLD_COLORS_MANIFEST);
+        let wildlife_spawn_handles = wildlife::spawn_manifest()
+            .into_iter()
+            .map(|(e, f)| (SpawnEntry::load_expect(e), f))
+            .collect();
 
         Self {
             seed,
@@ -65,6 +71,7 @@ impl Index {
             noise: Noise::new(seed),
             sites: Store::default(),
             trade: Default::default(),
+            wildlife_spawn_handles,
             colors,
         }
     }
diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs
index 5492f04261..07681cddf9 100644
--- a/world/src/layer/wildlife.rs
+++ b/world/src/layer/wildlife.rs
@@ -36,15 +36,18 @@ impl assets::Asset for SpawnEntry {
 impl SpawnEntry {
     pub fn from(asset_specifier: &str) -> Self { Self::load_expect(asset_specifier).read().clone() }
 
-    pub fn request(&self, requested_period: DayPeriod, underwater: bool) -> Option<&Pack> {
-        self.rules.iter().find(|pack| {
-            let time_match = pack
-                .day_period
-                .iter()
-                .any(|period| *period == requested_period);
-            let water_match = pack.is_underwater == underwater;
-            time_match && water_match
-        })
+    pub fn request(&self, requested_period: DayPeriod, underwater: bool) -> Option<Pack> {
+        self.rules
+            .iter()
+            .find(|pack| {
+                let time_match = pack
+                    .day_period
+                    .iter()
+                    .any(|period| *period == requested_period);
+                let water_match = pack.is_underwater == underwater;
+                time_match && water_match
+            })
+            .cloned()
     }
 }
 
@@ -69,9 +72,9 @@ impl Pack {
     }
 }
 
-type DensityFn = fn(&SimChunk, &ColumnSample) -> f32;
+pub type DensityFn = fn(&SimChunk, &ColumnSample) -> f32;
 
-pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
+pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
     // NOTE: Order matters.
     // Place entries with more strict requirements before more general ones
     vec![
@@ -218,14 +221,6 @@ pub fn wildlife_spawn_manifest() -> Vec<(&'static str, DensityFn)> {
     ]
 }
 
-fn wildlife_spawn_entries() -> Vec<(SpawnEntry, DensityFn)> {
-    let manifest = wildlife_spawn_manifest();
-    manifest
-        .into_iter()
-        .map(|(e, f)| (SpawnEntry::from(e), f))
-        .collect()
-}
-
 #[allow(clippy::eval_order_dependence)]
 pub fn apply_wildlife_supplement<'a, R: Rng>(
     // NOTE: Used only for dynamic elements like chests and entities!
@@ -233,12 +228,12 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
     wpos2d: Vec2<i32>,
     mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
     vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
-    _index: IndexRef,
+    index: IndexRef,
     chunk: &SimChunk,
     supplement: &mut ChunkSupplement,
     time: Option<TimeOfDay>,
 ) {
-    let scatter = wildlife_spawn_entries();
+    let scatter = &index.wildlife_spawn_handles;
 
     for y in 0..vol.size_xy().y as i32 {
         for x in 0..vol.size_xy().x as i32 {
@@ -269,6 +264,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
                     (density > 0.0)
                         .then(|| {
                             entry
+                                .read()
                                 .request(current_day_period, underwater)
                                 .and_then(|pack| {
                                     (dynamic_rng.gen::<f32>() < density * col_sample.spawn_rate
@@ -325,7 +321,7 @@ mod tests {
     // Checks that each entry in spawn manifest is loadable
     #[test]
     fn test_load_entries() {
-        let scatter = wildlife_spawn_manifest();
+        let scatter = spawn_manifest();
         for (entry, _) in scatter.into_iter() {
             std::mem::drop(SpawnEntry::from(entry));
         }
@@ -334,7 +330,7 @@ mod tests {
     // Check that each spawn entry has unique name
     #[test]
     fn test_name_uniqueness() {
-        let scatter = wildlife_spawn_manifest();
+        let scatter = spawn_manifest();
         let mut names = HashMap::new();
         for (entry, _) in scatter.into_iter() {
             let SpawnEntry { name, .. } = SpawnEntry::from(entry);
@@ -348,7 +344,7 @@ mod tests {
     // Quite strict rule, but otherwise may produce unexpected behaviour
     #[test]
     fn test_check_day_periods() {
-        let scatter = wildlife_spawn_manifest();
+        let scatter = spawn_manifest();
         for (entry, _) in scatter.into_iter() {
             let mut day_periods = HashSet::with_capacity(4);
             let SpawnEntry { rules, .. } = SpawnEntry::from(entry);
@@ -382,7 +378,7 @@ mod tests {
     // Checks that each entity is loadable
     #[test]
     fn test_load_entities() {
-        let scatter = wildlife_spawn_manifest();
+        let scatter = spawn_manifest();
         for (entry, _) in scatter.into_iter() {
             let SpawnEntry { rules, .. } = SpawnEntry::from(entry);
             for pack in rules {
@@ -401,7 +397,7 @@ mod tests {
     // Checks that group distribution has valid form
     #[test]
     fn test_group_choose() {
-        let scatter = wildlife_spawn_manifest();
+        let scatter = spawn_manifest();
         for (entry, _) in scatter.into_iter() {
             let SpawnEntry { rules, .. } = SpawnEntry::from(entry);
             for pack in rules {

From 84b7addfc36acaed925dcc07be39e41f26746b63 Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Wed, 14 Jul 2021 20:08:53 +0300
Subject: [PATCH 09/12] clippy

---
 world/src/site/settlement/mod.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs
index 3c0c751063..dd4a2c5843 100644
--- a/world/src/site/settlement/mod.rs
+++ b/world/src/site/settlement/mod.rs
@@ -1016,7 +1016,7 @@ fn human(pos: Vec3<f32>, economy: &SiteInformation, dynamic_rng: &mut impl Rng)
             .with_asset_expect("common.entity.village.guard"),
         1 | 2 => entity
             .with_agent_mark(agent::Mark::Merchant)
-            .with_economy(&economy)
+            .with_economy(economy)
             .with_lazy_loadout(merchant_loadout)
             .with_level(dynamic_rng.gen_range(10..15))
             .with_asset_expect("common.entity.village.merchant"),

From e7e3f60e84019ebb6aea93b2b8287cf73606ad5e Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Fri, 16 Jul 2021 17:28:08 +0300
Subject: [PATCH 10/12] adress review

---
 world/src/site/settlement/mod.rs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs
index dd4a2c5843..43ebb539c7 100644
--- a/world/src/site/settlement/mod.rs
+++ b/world/src/site/settlement/mod.rs
@@ -913,7 +913,7 @@ impl Settlement {
                         match dynamic_rng.gen_range(0..=4) {
                             0 => barnyard(entity_wpos, dynamic_rng),
                             1 => bird(entity_wpos, dynamic_rng),
-                            _ => human(entity_wpos, &economy, dynamic_rng),
+                            _ => humanoid(entity_wpos, &economy, dynamic_rng),
                         }
                     };
 
@@ -975,6 +975,7 @@ impl Settlement {
 }
 
 fn barnyard(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
+    //TODO: use Lottery instead of ad-hoc RNG system
     let species = match dynamic_rng.gen_range(0..5) {
         0 => quadruped_small::Species::Pig,
         1 => quadruped_small::Species::Sheep,
@@ -991,6 +992,7 @@ fn barnyard(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
 }
 
 fn bird(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
+    //TODO: use Lottery instead of ad-hoc RNG system
     let species = match dynamic_rng.gen_range(0..4) {
         0 => bird_medium::Species::Duck,
         1 => bird_medium::Species::Chicken,
@@ -1006,7 +1008,7 @@ fn bird(pos: Vec3<f32>, dynamic_rng: &mut impl Rng) -> EntityInfo {
         .with_automatic_name()
 }
 
-fn human(pos: Vec3<f32>, economy: &SiteInformation, dynamic_rng: &mut impl Rng) -> EntityInfo {
+fn humanoid(pos: Vec3<f32>, economy: &SiteInformation, dynamic_rng: &mut impl Rng) -> EntityInfo {
     let entity = EntityInfo::at(pos);
     match dynamic_rng.gen_range(0..12) {
         0 => entity

From e5de076d7a7a2ddd145fe9c5776d0f607c11966f Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Fri, 16 Jul 2021 17:30:10 +0300
Subject: [PATCH 11/12] Better naming & comments

---
 world/src/index.rs          |  6 ++---
 world/src/layer/wildlife.rs | 47 ++++++++++++++++++++++++++++++++++---
 2 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/world/src/index.rs b/world/src/index.rs
index 383d150e1b..7d5d6e4685 100644
--- a/world/src/index.rs
+++ b/world/src/index.rs
@@ -20,7 +20,7 @@ pub struct Index {
     pub noise: Noise,
     pub sites: Store<Site>,
     pub trade: TradeInformation,
-    pub wildlife_spawn_handles: Vec<(AssetHandle<SpawnEntry>, DensityFn)>,
+    pub wildlife_spawns: Vec<(AssetHandle<SpawnEntry>, DensityFn)>,
     colors: AssetHandle<Arc<Colors>>,
 }
 
@@ -60,7 +60,7 @@ impl Index {
     /// NOTE: Panics if the color manifest cannot be loaded.
     pub fn new(seed: u32) -> Self {
         let colors = Arc::<Colors>::load_expect(WORLD_COLORS_MANIFEST);
-        let wildlife_spawn_handles = wildlife::spawn_manifest()
+        let wildlife_spawns = wildlife::spawn_manifest()
             .into_iter()
             .map(|(e, f)| (SpawnEntry::load_expect(e), f))
             .collect();
@@ -71,7 +71,7 @@ impl Index {
             noise: Noise::new(seed),
             sites: Store::default(),
             trade: Default::default(),
-            wildlife_spawn_handles,
+            wildlife_spawns,
             colors,
         }
     }
diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs
index 07681cddf9..e82b1afbc7 100644
--- a/world/src/layer/wildlife.rs
+++ b/world/src/layer/wildlife.rs
@@ -12,6 +12,10 @@ use serde::Deserialize;
 use std::f32;
 use vek::*;
 
+type Weight = u32;
+type Min = u8;
+type Max = u8;
+
 fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
     (1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.125)
 }
@@ -52,9 +56,45 @@ impl SpawnEntry {
 }
 
 /// Dataset of animals to spawn
+///
+/// Example:
+/// ```text
+///        Pack(
+///            groups: [
+///                (3, (1, 2, "common.entity.wild.aggressive.frostfang")),
+///                (1, (1, 1, "common.entity.wild.aggressive.snow_leopard")),
+///                (1, (1, 1, "common.entity.wild.aggressive.yale")),
+///                (1, (1, 1, "common.entity.wild.aggressive.grolgar")),
+///            ],
+///            is_underwater: false,
+///            day_period: [Night, Morning, Noon, Evening],
+///        ),
+/// ```
+/// Groups:
+/// ```text
+///                (3, (1, 2, "common.entity.wild.aggressive.frostfang")),
+/// ```
+/// (3, ...) means that it has x3 chance to spawn (3/6 when every other has
+/// 1/6).
+///
+/// (.., (1, 2, ...)) is `1..=2` group size which means that it has
+/// chance to spawn as single mob or in pair
+///
+/// (..., (..., "common.entity.wild.aggressive.frostfang")) corresponds
+/// to `assets/common/entity/wild/aggressive/frostfang.ron` file with
+/// EntityConfig
+///
+/// Underwater:
+/// `is_underwater: false` means mobs from this pack can't be spawned underwater
+/// in rivers, lakes or ocean
+///
+/// Day period:
+/// `day_period: [Night, Morning, Noon, Evening]`
+/// means that mobs from this pack may be spawned in any day period without
+/// exception
 #[derive(Clone, Debug, Deserialize)]
 pub struct Pack {
-    pub groups: Vec<(u32, (u8, u8, String))>,
+    pub groups: Vec<(Weight, (Min, Max, String))>,
     pub is_underwater: bool,
     pub day_period: Vec<DayPeriod>,
 }
@@ -76,7 +116,8 @@ pub type DensityFn = fn(&SimChunk, &ColumnSample) -> f32;
 
 pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
     // NOTE: Order matters.
-    // Place entries with more strict requirements before more general ones
+    // Entries with more specific requirements
+    // and overall scarcity should come first, where possible.
     vec![
         // **Tundra**
         // Rock animals
@@ -233,7 +274,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
     supplement: &mut ChunkSupplement,
     time: Option<TimeOfDay>,
 ) {
-    let scatter = &index.wildlife_spawn_handles;
+    let scatter = &index.wildlife_spawns;
 
     for y in 0..vol.size_xy().y as i32 {
         for x in 0..vol.size_xy().x as i32 {

From aa9ecdfa83bf0493985ef8223c1bf8c9340bf15f Mon Sep 17 00:00:00 2001
From: juliancoffee <lightdarkdaughter@gmail.com>
Date: Sat, 17 Jul 2021 00:45:53 +0300
Subject: [PATCH 12/12] Add test for duplicates in `EntityConfig::meta`

+ make tests cleaner
---
 common/src/generation.rs | 187 ++++++++++++++++++++++++++-------------
 1 file changed, 125 insertions(+), 62 deletions(-)

diff --git a/common/src/generation.rs b/common/src/generation.rs
index f587a10f28..bfc819cc1b 100644
--- a/common/src/generation.rs
+++ b/common/src/generation.rs
@@ -383,6 +383,115 @@ pub fn get_npc_name<
 mod tests {
     use super::*;
     use crate::{comp::inventory::slot::EquipSlot, SkillSetBuilder};
+    use hashbrown::HashMap;
+
+    #[derive(Debug, Eq, Hash, PartialEq)]
+    enum MetaId {
+        LoadoutAsset,
+        SkillSetAsset,
+    }
+
+    impl Meta {
+        fn id(&self) -> MetaId {
+            match self {
+                Meta::LoadoutAsset(_) => MetaId::LoadoutAsset,
+                Meta::SkillSetAsset(_) => MetaId::SkillSetAsset,
+            }
+        }
+    }
+
+    fn validate_hands(hands: Hands, _config_asset: &str) {
+        match hands {
+            Hands::TwoHanded(main_tool) => {
+                main_tool.validate(EquipSlot::ActiveMainhand);
+            },
+            Hands::Paired(tool) => {
+                tool.validate(EquipSlot::ActiveMainhand);
+                tool.validate(EquipSlot::ActiveOffhand);
+            },
+            Hands::Mix { mainhand, offhand } => {
+                mainhand.validate(EquipSlot::ActiveMainhand);
+                offhand.validate(EquipSlot::ActiveOffhand);
+            },
+            Hands::Uninit => {},
+        }
+    }
+
+    fn validate_body_and_name(body: BodyBuilder, name: NameKind, config_asset: &str) {
+        match body {
+            BodyBuilder::RandomWith(string) => {
+                let npc::NpcBody(_body_kind, mut body_creator) =
+                    string.parse::<npc::NpcBody>().unwrap_or_else(|err| {
+                        panic!(
+                            "failed to parse body {:?} in {}. Err: {:?}",
+                            &string, config_asset, err
+                        )
+                    });
+                let _ = body_creator();
+            },
+            BodyBuilder::Uninit => {
+                if let NameKind::Automatic = name {
+                    // there is a big chance to call automatic name
+                    // when body is yet undefined
+                    //
+                    // use .with_automatic_name() in code explicitly
+                    panic!("Used Automatic name with Uninit body in {}", config_asset);
+                }
+            },
+            BodyBuilder::Exact { .. } => {},
+        }
+    }
+
+    fn validate_loot(loot: LootKind, config_asset: &str) {
+        match loot {
+            LootKind::Item(asset) => {
+                if let Err(e) = Item::new_from_asset(&asset) {
+                    panic!(
+                        "Unable to parse loot item ({}) in {}. Err: {:?}",
+                        asset, config_asset, e
+                    );
+                }
+            },
+            LootKind::LootTable(asset) => {
+                // we need to just load it check if it exists,
+                // because all loot tables are tested in Lottery module
+                if let Err(e) = Lottery::<LootSpec<String>>::load(&asset) {
+                    panic!(
+                        "Unable to parse loot table ({}) in {}. Err: {:?}",
+                        asset, config_asset, e
+                    );
+                }
+            },
+            LootKind::Uninit => {},
+        }
+    }
+
+    fn validate_meta(meta: Vec<Meta>, config_asset: &str) {
+        let mut meta_counter = HashMap::new();
+        for field in meta {
+            meta_counter
+                .entry(field.id())
+                .and_modify(|c| *c += 1)
+                .or_insert(1);
+            match field {
+                Meta::LoadoutAsset(asset) => {
+                    let rng = &mut rand::thread_rng();
+                    let builder = LoadoutBuilder::default();
+                    // we need to just load it check if it exists,
+                    // because all loadouts are tested in LoadoutBuilder module
+                    std::mem::drop(builder.with_asset_expect(&asset, rng));
+                },
+                Meta::SkillSetAsset(asset) => {
+                    std::mem::drop(SkillSetBuilder::from_asset_expect(&asset));
+                },
+            }
+        }
+        for (meta_id, counter) in meta_counter {
+            if counter > 1 {
+                panic!("Duplicate {:?} in {}", meta_id, config_asset);
+            }
+        }
+    }
 
     #[test]
     fn test_all_entity_assets() {
@@ -390,77 +499,31 @@ mod tests {
         let entity_configs = assets::load_dir::<EntityConfig>("common.entity", true)
             .expect("Failed to access entity directory");
         for config_asset in entity_configs.ids() {
+            // print asset name so we don't need to find errors everywhere
+            // it'll be ignored by default so you'll see it only in case of errors
+            //
+            // TODO:
+            // 1) Add try_validate() for loadout_builder::ItemSpec which will return
+            // Result and we will happily panic in validate_hands() with name of
+            // config_asset.
+            // 2) Add try_from_asset() for LoadoutBuilder and
+            // SkillSet builder which will return Result and we will happily
+            // panic in validate_meta() with the name of config_asset
             println!("{}:", config_asset);
+
             let EntityConfig {
                 hands,
-                loot,
                 body,
                 name,
+                loot,
                 meta,
                 alignment: _alignment, // can't fail if serialized, it's a boring enum
             } = EntityConfig::from_asset_expect(config_asset);
 
-            match hands {
-                Hands::TwoHanded(main_tool) => {
-                    main_tool.validate(EquipSlot::ActiveMainhand);
-                },
-                Hands::Paired(tool) => {
-                    tool.validate(EquipSlot::ActiveMainhand);
-                    tool.validate(EquipSlot::ActiveOffhand);
-                },
-                Hands::Mix { mainhand, offhand } => {
-                    mainhand.validate(EquipSlot::ActiveMainhand);
-                    offhand.validate(EquipSlot::ActiveOffhand);
-                },
-                Hands::Uninit => {},
-            }
-
-            match body {
-                BodyBuilder::RandomWith(string) => {
-                    let npc::NpcBody(_body_kind, mut body_creator) =
-                        string.parse::<npc::NpcBody>().unwrap_or_else(|err| {
-                            panic!("failed to parse body {:?}. Err: {:?}", &string, err)
-                        });
-                    let _ = body_creator();
-                },
-                BodyBuilder::Uninit => {
-                    if let NameKind::Automatic = name {
-                        // there is a big chance to call automatic name
-                        // when body is yet undefined
-                        //
-                        // use .with_automatic_name() in code explicitly
-                        panic!("Used Automatic name with Uninit body in {}", config_asset);
-                    }
-                },
-                BodyBuilder::Exact { .. } => {},
-            }
-
-            match loot {
-                LootKind::Item(asset) => {
-                    std::mem::drop(Item::new_from_asset_expect(&asset));
-                },
-                LootKind::LootTable(asset) => {
-                    // we need to just load it check if it exists,
-                    // because all loot tables are tested in Lottery module
-                    let _ = Lottery::<LootSpec<String>>::load_expect(&asset);
-                },
-                LootKind::Uninit => {},
-            }
-
-            for field in meta {
-                match field {
-                    Meta::LoadoutAsset(asset) => {
-                        let rng = &mut rand::thread_rng();
-                        let builder = LoadoutBuilder::default();
-                        // we need to just load it check if it exists,
-                        // because all loadouts are tested in LoadoutBuilder module
-                        std::mem::drop(builder.with_asset_expect(&asset, rng));
-                    },
-                    Meta::SkillSetAsset(asset) => {
-                        std::mem::drop(SkillSetBuilder::from_asset_expect(&asset));
-                    },
-                }
-            }
+            validate_hands(hands, config_asset);
+            validate_body_and_name(body, name, config_asset);
+            validate_loot(loot, config_asset);
+            validate_meta(meta, config_asset);
         }
     }
 }