summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--db/_generated/areas/candy-town.json1
-rw-r--r--db/_generated/areas/city-park.json2
-rw-r--r--db/_generated/areas/flower-city.json1
-rw-r--r--db/_generated/areas/leather-town.json2
-rw-r--r--db/_generated/areas/lion-mountain.json1
-rw-r--r--db/_generated/areas/paper-town.json2
-rw-r--r--db/_generated/areas/route1.json2
-rw-r--r--db/_generated/areas/route2.json2
-rw-r--r--db/_generated/areas/route3.json2
-rw-r--r--db/_generated/areas/route4.json1
-rw-r--r--db/_generated/areas/route5.json1
-rw-r--r--db/_generated/areas/route6.json1
-rw-r--r--db/_generated/areas/route7.json1
-rw-r--r--db/_generated/areas/sea-route-c.json1
-rw-r--r--db/_generated/areas/side-route-a.json1
-rw-r--r--db/_generated/areas/sphalian-town.json1
-rw-r--r--db/_generated/areas/timber-town.json1
-rw-r--r--db/_generated/areas/tunnel-b.json1
-rw-r--r--db/areas.php7
-rw-r--r--db/areas/candy-town.json26
-rw-r--r--db/areas/city-park.json3
-rw-r--r--db/areas/flower-city.json42
-rw-r--r--db/areas/lion-mountain.json34
-rw-r--r--db/areas/paper-town.json34
-rw-r--r--db/areas/route1.json2
-rw-r--r--db/areas/route2.json6
-rw-r--r--db/areas/route3.json7
-rw-r--r--db/areas/route4.json63
-rw-r--r--db/areas/route5.json45
-rw-r--r--db/areas/route6.json45
-rw-r--r--db/areas/route7.json49
-rw-r--r--db/areas/sea-route-c.json37
-rw-r--r--db/areas/side-route-a.json49
-rw-r--r--db/areas/sphalian-town.json20
-rw-r--r--db/areas/timber-town.json26
-rw-r--r--db/areas/tunnel-b.json45
-rw-r--r--db/maps/Buddha_mountain.pngbin0 -> 273750 bytes
-rw-r--r--db/maps/Candy_Town.pngbin0 -> 81863 bytes
-rw-r--r--db/maps/City_park.pngbin0 -> 64260 bytes
-rw-r--r--db/maps/Flower_City.pngbin0 -> 70997 bytes
-rw-r--r--db/maps/Leather_Town.pngbin0 -> 93541 bytes
-rw-r--r--db/maps/Paper_town.pngbin0 -> 37887 bytes
-rw-r--r--db/maps/Route_1.pngbin0 -> 32549 bytes
-rw-r--r--db/maps/Route_2.pngbin0 -> 50363 bytes
-rw-r--r--db/maps/Route_3.pngbin0 -> 94897 bytes
-rw-r--r--db/maps/Route_4.pngbin0 -> 36668 bytes
-rw-r--r--db/maps/Route_5.pngbin0 -> 49869 bytes
-rw-r--r--db/maps/Route_6.pngbin0 -> 32650 bytes
-rw-r--r--db/maps/Route_7.pngbin0 -> 82231 bytes
-rw-r--r--db/maps/Sea_Route_C.pngbin0 -> 52069 bytes
-rw-r--r--db/maps/Side_Route_A.pngbin0 -> 48239 bytes
-rw-r--r--db/maps/Sphalian_town.pngbin0 -> 34948 bytes
-rw-r--r--db/maps/Timber_Town.pngbin0 -> 82872 bytes
-rw-r--r--db/maps/Tunnel_Route_B.pngbin0 -> 45757 bytes
-rw-r--r--db/maps/Tunnel_Route_B_-_Below.pngbin0 -> 35832 bytes
-rw-r--r--db/maps/candy-town.svg10
-rw-r--r--db/maps/city-park.svg7
-rw-r--r--db/maps/flower-city.svg10
-rw-r--r--db/maps/leather-town.svg13
-rw-r--r--db/maps/lion-mountain.svg7
-rw-r--r--db/maps/paper-town.svg10
-rw-r--r--db/maps/route1.svg7
-rw-r--r--db/maps/route2.svg7
-rw-r--r--db/maps/route3.svg7
-rw-r--r--db/maps/route4.svg7
-rw-r--r--db/maps/route5.svg7
-rw-r--r--db/maps/route6.svg7
-rw-r--r--db/maps/route7.svg7
-rw-r--r--db/maps/sea-route-c.svg7
-rw-r--r--db/maps/side-route-a.svg7
-rw-r--r--db/maps/sphalian-town.svg9
-rw-r--r--db/maps/timber-town.svg10
-rw-r--r--db/maps/tunnel-b-underground.svg7
-rw-r--r--db/maps/tunnel-b.svg7
-rw-r--r--index.html39
-rw-r--r--resources/css/battle.css1
-rw-r--r--resources/css/menu.css28
-rw-r--r--resources/css/page.css10
-rw-r--r--resources/css/town.css24
-rw-r--r--resources/js/classes/Area.js3
-rw-r--r--resources/js/classes/Item.js8
-rw-r--r--resources/js/classes/Monster.js15
-rw-r--r--resources/js/classes/State.js5
-rw-r--r--resources/js/classes/StatusEffect.js3
-rw-r--r--resources/js/definitions.js1
-rw-r--r--resources/js/formula.js82
-rw-r--r--resources/js/game.js153
-rw-r--r--resources/js/helpers.js20
-rw-r--r--resources/js/main.js44
-rw-r--r--resources/js/memory.js31
-rw-r--r--resources/js/ui.js399
91 files changed, 1390 insertions, 193 deletions
diff --git a/db/_generated/areas/candy-town.json b/db/_generated/areas/candy-town.json
new file mode 100644
index 0000000..a11f4b4
--- /dev/null
+++ b/db/_generated/areas/candy-town.json
@@ -0,0 +1 @@
+{"name":"Candy Town","locations":{"hospital":{"type":"healingCenter","price":30},"scoop_store":{"type":"shop","economy":"spyder_candy_scoop","items":[{"item_name":"potion","price":20,"cost":5},{"item_name":"revive","price":100,"cost":20},{"item_name":"tuxeball","price":50,"cost":10}]}},"connections":{"route6":{"conditions":[],"name":"Route 6"},"sea-route-c":{"conditions":[],"name":"Sea Route C"}},"encounters":[],"requiredEncounters":0,"trainers":[],"environment":null,"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/Candy_Town.png\" \/>\n\n <rect data-location=\"hospital\" x=\"127\" y=\"431\" width=\"210\" height=\"130\" stroke=\"red\" fill=\"transparent\" \/>\n <rect data-location=\"scoop_store\" x=\"17\" y=\"144\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/city-park.json b/db/_generated/areas/city-park.json
index 9080b55..f6621fb 100644
--- a/db/_generated/areas/city-park.json
+++ b/db/_generated/areas/city-park.json
@@ -1 +1 @@
-{"name":"City Park","encounter_slug":"citypark","encounters":[{"monster":"cardiling","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[5,8]},{"monster":"cardiwing","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[8,11]},{"monster":"cataspike","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[6,9]},{"monster":"eyenemy","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[5,8]},{"monster":"axylightl","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[8,12]},{"monster":"tourbidi","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[8,12]},{"monster":"squabbit","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[8,12]},{"monster":"puparmor","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[9,14]}],"requiredEncounters":10,"trainers":[{"name":"Frances","sprite":"florist.png","monsters":[{"slug":"shybulb","level":8},{"slug":"shybulb","level":8}],"inventory":[]}],"environment_slug":"forest","connections":{"route2":{"conditions":[],"name":"Route 2"},"leather-town":{"conditions":["encounters","trainers"],"name":"Leather Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":false} \ No newline at end of file
+{"name":"City Park","encounter_slug":"citypark","encounters":[{"monster":"cardiling","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[5,8]},{"monster":"cardiwing","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[8,11]},{"monster":"cataspike","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[6,9]},{"monster":"eyenemy","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[5,8]},{"monster":"axylightl","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[8,12]},{"monster":"tourbidi","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[8,12]},{"monster":"squabbit","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[8,12]},{"monster":"puparmor","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[9,14]}],"requiredEncounters":10,"trainers":[{"name":"Frances","sprite":"florist.png","monsters":[{"slug":"shybulb","level":8},{"slug":"shybulb","level":8}]}],"environment_slug":"forest","connections":{"route2":{"conditions":[],"name":"Route 2"},"leather-town":{"conditions":["encounters","trainers"],"name":"Leather Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/City_park.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/flower-city.json b/db/_generated/areas/flower-city.json
new file mode 100644
index 0000000..477399d
--- /dev/null
+++ b/db/_generated/areas/flower-city.json
@@ -0,0 +1 @@
+{"name":"Flower City","encounter_slug":"","encounters":[],"requiredEncounters":0,"trainers":[],"environment_slug":"interior","locations":{"healing_center":{"type":"healingCenter","price":30},"scoop_store":{"type":"shop","economy":"spyder_flower_scoop","items":[{"item_name":"potion","price":20,"cost":5},{"item_name":"revive","price":100,"cost":20},{"item_name":"tuxeball","price":50,"cost":10}]}},"events":[],"connections":{"route4":{"conditions":[],"name":"Route 4"},"route5":{"conditions":[],"name":"Route 5"},"side-route-a":{"conditions":[],"name":"Side Route A"}},"environment":{"slug":"interior","battle_graphics":{"island_back":"paper_back_island.png","island_front":"paper_front_island.png","background":"battle_bg03.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/Flower_City.png\" \/>\n\n <rect data-location=\"healing_center\" x=\"224\" y=\"192\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n <rect data-location=\"scoop_store\" x=\"368\" y=\"320\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/leather-town.json b/db/_generated/areas/leather-town.json
index 9cd0551..fc0e69e 100644
--- a/db/_generated/areas/leather-town.json
+++ b/db/_generated/areas/leather-town.json
@@ -1 +1 @@
-{"name":"Leather Town","encounter_slug":"","encounters":[],"requiredEncounters":0,"trainers":[],"environment_slug":"interior","shops":{"leather_scoop":[]},"connections":{"city-park":{"conditions":[],"name":"City Park"},"route3":{"conditions":[],"name":"Route 3"}},"environment":{"slug":"interior","battle_graphics":{"island_back":"paper_back_island.png","island_front":"paper_front_island.png","background":"battle_bg03.png"},"battle_music":"music_battle_loop"},"map":false} \ No newline at end of file
+{"name":"Leather Town","encounter_slug":"","encounters":[],"requiredEncounters":0,"trainers":[],"environment_slug":"interior","locations":{"healing_center":{"type":"healingCenter","price":20},"leather_scoop":{"type":"shop","economy":"leather_scoop","items":[{"item_name":"potion","price":20,"cost":5},{"item_name":"revive","price":100,"cost":20},{"item_name":"tuxeball","price":50,"cost":10}]}},"connections":{"city-park":{"conditions":[],"name":"City Park"},"route3":{"conditions":[],"name":"Route 3"}},"environment":{"slug":"interior","battle_graphics":{"island_back":"paper_back_island.png","island_front":"paper_front_island.png","background":"battle_bg03.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/Leather_Town.png\" \/>\n\n <text x=\"323\" y=\"174\">Healing Center<\/text>\n <rect data-location=\"healing_center\" x=\"336\" y=\"97\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n\n <text x=\"226\" y=\"174\">Shop<\/text>\n <rect data-location=\"leather_scoop\" x=\"208\" y=\"97\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/lion-mountain.json b/db/_generated/areas/lion-mountain.json
new file mode 100644
index 0000000..9edf988
--- /dev/null
+++ b/db/_generated/areas/lion-mountain.json
@@ -0,0 +1 @@
+{"name":"Lion Mountain","encounter_slug":"lion_mountain","encounters":[{"monster":"snowrilla","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[55,65]},{"monster":"snowrilla","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[65,75]},{"monster":"tadcool","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[55,65]},{"monster":"tadcool","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[65,75]},{"monster":"chillimp","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[55,65]},{"monster":"chillimp","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[65,75]},{"monster":"tux","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[55,65]},{"monster":"tux","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[65,75]}],"requiredEncounters":10,"trainers":[{"name":"Miner Jacques","sprite":"monk.png","monsters":[{"slug":"heronquak","level":55},{"slug":"jemuar","level":60}]}],"environment_slug":"snow","connections":{"sphalian-town":{"conditions":[],"name":"Sphalian Town"}},"environment":{"slug":"snow","battle_graphics":{"island_back":"snow_island.png","island_front":"snow_island.png","background":"snow_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 720 1600\"\n>\n <image href=\"\/db\/maps\/Buddha_mountain.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/paper-town.json b/db/_generated/areas/paper-town.json
index 6a41be0..0a763ad 100644
--- a/db/_generated/areas/paper-town.json
+++ b/db/_generated/areas/paper-town.json
@@ -1 +1 @@
-{"name":"Paper Town","encounter_slug":"","encounters":[],"requiredEncounters":0,"trainers":[{"name":"Rival","monsters":[{"slug":"STARTER","level":2}],"inventory":[]}],"environment_slug":"interior","connections":{"route1":{"conditions":["encounters","trainers"],"name":"Route 1"}},"environment":{"slug":"interior","battle_graphics":{"island_back":"paper_back_island.png","island_front":"paper_front_island.png","background":"battle_bg03.png"},"battle_music":"music_battle_loop"},"map":false} \ No newline at end of file
+{"name":"Paper Town","locations":{"scoop_store":{"type":"shop","economy":"spyder_paper_mart","items":[{"item_name":"potion","price":20,"cost":5},{"item_name":"revive","price":100,"cost":20},{"item_name":"tuxeball","price":50,"cost":10}]}},"connections":{"route1":{"conditions":["encounters","trainers"],"name":"Route 1"},"sea-route-c":{"conditions":["area.sea-route-c"],"name":"Sea Route C"}},"encounters":[],"requiredEncounters":0,"trainers":[],"environment":null,"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 320\"\n>\n <image href=\"\/db\/maps\/Paper_town.png\" \/>\n\n <text x=\"294\" y=\"221\">Shop<\/text>\n <rect data-location=\"scoop_store\" x=\"272\" y=\"144\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/route1.json b/db/_generated/areas/route1.json
index 02a1af0..d9b88ed 100644
--- a/db/_generated/areas/route1.json
+++ b/db/_generated/areas/route1.json
@@ -1 +1 @@
-{"name":"Route 1","encounter_slug":"route1","encounters":[{"monster":"pairagrin","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4]},{"monster":"aardorn","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4]},{"monster":"cataspike","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4]},{"monster":"pairagrin","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[3,5]},{"monster":"aardorn","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[3,5]},{"monster":"cataspike","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[3,5]}],"requiredEncounters":10,"trainers":[{"name":"Bruder Mikki","sprite":"dragonrider.png","monsters":[{"slug":"memnomnom","level":5},{"slug":"jelillow","level":5}],"inventory":[{"slug":"potion","quantity":2}]}],"environment_slug":"forest","connections":{"paper-town":{"conditions":[],"name":"Paper Town"},"cotton-town":{"conditions":["encounters","trainers"],"name":"Cotton Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":false} \ No newline at end of file
+{"name":"Route 1","encounter_slug":"route1","encounters":[{"monster":"pairagrin","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4]},{"monster":"aardorn","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4]},{"monster":"cataspike","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4]},{"monster":"pairagrin","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[3,5]},{"monster":"aardorn","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[3,5]},{"monster":"cataspike","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[3,5]}],"requiredEncounters":10,"trainers":[{"name":"Bruder Mikki","sprite":"adventurer.png","monsters":[{"slug":"memnomnom","level":5},{"slug":"jelillow","level":5}],"inventory":[{"slug":"potion","quantity":2}]}],"environment_slug":"forest","connections":{"paper-town":{"conditions":[],"name":"Paper Town"},"cotton-town":{"conditions":["encounters","trainers"],"name":"Cotton Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 320\"\n>\n <image href=\"\/db\/maps\/Route_1.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/route2.json b/db/_generated/areas/route2.json
index 5ef92c5..5511453 100644
--- a/db/_generated/areas/route2.json
+++ b/db/_generated/areas/route2.json
@@ -1 +1 @@
-{"name":"Route 2","encounter_slug":"route2","encounters":[{"monster":"cardiling","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"aardorn","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"eyenemy","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"axylightl","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[4,7]},{"monster":"cataspike","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"cardiling","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6]},{"monster":"aardorn","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[4,8]},{"monster":"eyenemy","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[4,8]},{"monster":"axylightl","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[5,8]},{"monster":"cataspike","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[4,8]}],"requiredEncounters":10,"trainers":[{"name":"Rival","monsters":[{"slug":"cardiling","level":3},{"slug":"eyenemy","level":6},{"slug":"STARTER","level":6}],"inventory":[]}],"environment_slug":"forest","connections":{"cotton-town":{"conditions":[],"name":"Cotton Town"},"city-park":{"conditions":["encounters","trainers"],"name":"City Park"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":false} \ No newline at end of file
+{"name":"Route 2","encounter_slug":"route2","encounters":[{"monster":"cardiling","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"aardorn","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"eyenemy","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"axylightl","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[4,7]},{"monster":"cataspike","encounter_rate":2.5,"daytime":true,"exp_req_mod":1,"level_range":[3,6]},{"monster":"cardiling","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6]},{"monster":"aardorn","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[4,8]},{"monster":"eyenemy","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[4,8]},{"monster":"axylightl","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[5,8]},{"monster":"cataspike","encounter_rate":2.5,"daytime":false,"exp_req_mod":1,"level_range":[4,8]}],"requiredEncounters":10,"trainers":[{"name":"Rival Billie","sprite":"fashionista.png","monsters":[{"slug":"cardiling","level":3},{"slug":"eyenemy","level":6},{"slug":"STARTER","level":6}]}],"environment_slug":"forest","connections":{"cotton-town":{"conditions":[],"name":"Cotton Town"},"city-park":{"conditions":["encounters","trainers"],"name":"City Park"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 320\"\n>\n <image href=\"\/db\/maps\/Route_2.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/route3.json b/db/_generated/areas/route3.json
index fbd07f4..ca197c7 100644
--- a/db/_generated/areas/route3.json
+++ b/db/_generated/areas/route3.json
@@ -1 +1 @@
-{"name":"Route 3","encounter_slug":"route3","encounters":[{"monster":"cardiling","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[7,10]},{"monster":"elofly","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[7,10]},{"monster":"squabbit","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[8,11]},{"monster":"shybulb","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[7,10]},{"monster":"cardiling","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[9,12]},{"monster":"elofly","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[9,12]},{"monster":"squabbit","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[9,12]},{"monster":"shybulb","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[9,12]}],"requiredEncounters":10,"trainers":[{"name":"Miner Roxby","sprite":"miner.png","monsters":[{"slug":"rockitten","level":13},{"slug":"ignibus","level":13}]}],"environment_slug":"forest","connections":{"leather-town":{"conditions":[],"name":"Leather Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":false} \ No newline at end of file
+{"name":"Route 3","encounter_slug":"route3","encounters":[{"monster":"cardiling","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[7,10]},{"monster":"elofly","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[7,10]},{"monster":"squabbit","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[8,11]},{"monster":"shybulb","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[7,10]},{"monster":"cardiling","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[9,12]},{"monster":"elofly","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[9,12]},{"monster":"squabbit","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[9,12]},{"monster":"shybulb","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[9,12]}],"requiredEncounters":10,"trainers":[{"name":"Miner Roxby","sprite":"miner.png","monsters":[{"slug":"rockitten","level":13},{"slug":"ignibus","level":13}]}],"environment_slug":"forest","connections":{"leather-town":{"conditions":[],"name":"Leather Town"},"route4":{"conditions":["encounters","trainers"],"name":"Route 4"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/Route_3.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/route4.json b/db/_generated/areas/route4.json
new file mode 100644
index 0000000..0e2fe11
--- /dev/null
+++ b/db/_generated/areas/route4.json
@@ -0,0 +1 @@
+{"name":"Route 4","encounter_slug":"route4","encounters":[{"monster":"elofly","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[11,14]},{"monster":"sapsnap","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[12,15]},{"monster":"aardorn","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[11,14]},{"monster":"katapill","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[11,14]},{"monster":"elofly","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[13,16]},{"monster":"sapsnap","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[13,16]},{"monster":"aardorn","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[13,16]},{"monster":"katapill","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[13,16]}],"requiredEncounters":10,"trainers":[{"name":"Soldier Marshall","sprite":"soldier.png","monsters":[{"slug":"puparmor","level":16},{"slug":"puparmor","level":16}]},{"name":"Rival Billie","sprite":"fashionista.png","monsters":[{"slug":"cardiwing","level":16},{"slug":"eyesore","level":16},{"slug":"viviphyta","level":16},{"slug":"STARTER","level":18}]}],"environment_slug":"forest","connections":{"route3":{"conditions":[],"name":"Route 3"},"flower-city":{"conditions":["encounters","trainers"],"name":"Flower City"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 320 640\"\n>\n <image href=\"\/db\/maps\/Route_4.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/route5.json b/db/_generated/areas/route5.json
new file mode 100644
index 0000000..e0677fe
--- /dev/null
+++ b/db/_generated/areas/route5.json
@@ -0,0 +1 @@
+{"name":"Route 5","encounter_slug":"route5","encounters":[{"monster":"foofle","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[19,23]},{"monster":"vamporm","encounter_rate":3,"daytime":true,"exp_req_mod":1,"level_range":[16,20]},{"monster":"dracune","encounter_rate":1,"daytime":true,"exp_req_mod":1,"level_range":[18,22]},{"monster":"anoleaf","encounter_rate":3,"daytime":false,"exp_req_mod":1,"level_range":[18,22]},{"monster":"gectile","encounter_rate":1,"daytime":false,"exp_req_mod":1,"level_range":[22,26]}],"requiredEncounters":10,"trainers":[{"name":"Soldier Hunter","sprite":"soldier.png","monsters":[{"slug":"elofly","level":16},{"slug":"elofly","level":16},{"slug":"elowind","level":18}]}],"environment_slug":"forest","connections":{"flower-city":{"conditions":[],"name":"Flower City"},"timber-town":{"conditions":["encounters","trainers"],"name":"Timber Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 320\"\n>\n <image href=\"\/db\/maps\/Route_5.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/route6.json b/db/_generated/areas/route6.json
new file mode 100644
index 0000000..3c8f520
--- /dev/null
+++ b/db/_generated/areas/route6.json
@@ -0,0 +1 @@
+{"name":"Route 6","encounter_slug":"route6","encounters":[{"monster":"dandicub","encounter_rate":10,"daytime":true,"exp_req_mod":1,"level_range":[19,20]},{"monster":"dandylion","encounter_rate":5,"daytime":true,"exp_req_mod":1,"level_range":[21,23]},{"monster":"capiti","encounter_rate":10,"daytime":true,"exp_req_mod":1,"level_range":[19,22]},{"monster":"dandicub","encounter_rate":10,"daytime":false,"exp_req_mod":1,"level_range":[20,24]},{"monster":"dandylion","encounter_rate":5,"daytime":false,"exp_req_mod":1,"level_range":[22,24]},{"monster":"capiti","encounter_rate":10,"daytime":false,"exp_req_mod":1,"level_range":[20,24]}],"requiredEncounters":10,"trainers":[{"name":"Florist Frances","sprite":"florist.png","monsters":[{"slug":"narcileaf","level":30},{"slug":"shybulb","level":30},{"slug":"shybulb","level":30}]}],"environment_slug":"forest","connections":{"tunnel-b":{"conditions":[],"name":"Tunnel B"},"candy-town":{"conditions":["encounters","trainers"],"name":"Candy Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 320\"\n>\n <image href=\"\/db\/maps\/Route_6.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/route7.json b/db/_generated/areas/route7.json
new file mode 100644
index 0000000..dffa7b4
--- /dev/null
+++ b/db/_generated/areas/route7.json
@@ -0,0 +1 @@
+{"name":"Route 7","encounter_slug":"route7","encounters":[{"monster":"pairagrim","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[35,40]},{"monster":"aardart","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[35,40]},{"monster":"weavifly","encounter_rate":3.5,"daytime":true,"exp_req_mod":1,"level_range":[35,40]},{"monster":"pantherafira","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[35,40]},{"monster":"flacono","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[35,40]},{"monster":"slichen","encounter_rate":3.5,"daytime":false,"exp_req_mod":1,"level_range":[35,40]}],"requiredEncounters":10,"trainers":[{"name":"Monk Statius","sprite":"monk.png","monsters":[{"slug":"aardart","level":30},{"slug":"pairagrim","level":30},{"slug":"bolt","level":30},{"slug":"fribbit","level":30}]}],"environment_slug":"forest","connections":{"side-route-a":{"conditions":[],"name":"Side Route A"},"sphalian-town":{"conditions":["encounters","trainers"],"name":"Sphalian Town"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/Route_7.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/sea-route-c.json b/db/_generated/areas/sea-route-c.json
new file mode 100644
index 0000000..5045969
--- /dev/null
+++ b/db/_generated/areas/sea-route-c.json
@@ -0,0 +1 @@
+{"name":"Sea Route C","encounter_slug":"default_encounter","encounters":[{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"eyenemy"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"dandicub"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"cardiling"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"budaye"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"cataspike"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"elofly"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"aardorn"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"nut"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"grimachin"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"spighter"}],"requiredEncounters":10,"trainers":[{"name":"Dragonrider Nigel","sprite":"dragonrider.png","monsters":[{"slug":"agnigon","level":36}]}],"environment_slug":"sea","connections":{"candy-town":{"conditions":[],"name":"Candy Town"},"paper-town":{"conditions":["encounters","trainers"],"name":"Paper Town"}},"environment":{"slug":"sea","battle_graphics":{"island_back":"water_island.png","island_front":"water_island.png","background":"sea_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/Sea_Route_C.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/side-route-a.json b/db/_generated/areas/side-route-a.json
new file mode 100644
index 0000000..76cb968
--- /dev/null
+++ b/db/_generated/areas/side-route-a.json
@@ -0,0 +1 @@
+{"name":"Side Route A","encounter_slug":"default_encounter","encounters":[{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"eyenemy"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"dandicub"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"cardiling"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"budaye"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"cataspike"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"elofly"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"aardorn"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"nut"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"grimachin"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"spighter"}],"requiredEncounters":10,"trainers":[{"name":"Rival Billie","sprite":"fashionista.png","monsters":[{"slug":"cardiwing","level":17},{"slug":"eyesore","level":17},{"slug":"viviphyta","level":17},{"slug":"STARTER","level":20}]}],"environment_slug":"forest","connections":{"flower-city":{"conditions":[],"name":"Flower City"},"route7":{"conditions":["encounters","trainers"],"name":"Route 7"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 320 640\"\n>\n <image href=\"\/db\/maps\/Side_Route_A.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/sphalian-town.json b/db/_generated/areas/sphalian-town.json
new file mode 100644
index 0000000..5f436dd
--- /dev/null
+++ b/db/_generated/areas/sphalian-town.json
@@ -0,0 +1 @@
+{"name":"Sphalian Town","locations":{"healing_center":{"type":"healingCenter","price":40}},"connections":{"route7":{"conditions":[],"name":"Route 7"},"lion-mountain":{"conditions":[],"name":"Lion Mountain"}},"encounters":[],"requiredEncounters":0,"trainers":[],"environment":null,"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 368 400\"\n>\n <image href=\"\/db\/maps\/Sphalian_town.png\" \/>\n\n <rect data-location=\"healing_center\" x=\"240\" y=\"48\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/timber-town.json b/db/_generated/areas/timber-town.json
new file mode 100644
index 0000000..041309c
--- /dev/null
+++ b/db/_generated/areas/timber-town.json
@@ -0,0 +1 @@
+{"name":"Timber Town","locations":{"healing_center":{"type":"healingCenter","price":20},"scoop_store":{"type":"shop","economy":"spyder_timber_scoop","items":[{"item_name":"potion","price":20,"cost":5},{"item_name":"revive","price":100,"cost":20},{"item_name":"tuxeball","price":50,"cost":10}]}},"connections":{"route5":{"conditions":[],"name":"Route 5"},"tunnel-b":{"conditions":[],"name":"Tunnel B"}},"encounters":[],"requiredEncounters":0,"trainers":[],"environment":null,"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 640 640\"\n>\n <image href=\"\/db\/maps\/Timber_Town.png\" \/>\n\n <rect data-location=\"healing_center\" x=\"96\" y=\"48\" width=\"80\" height=\"64\" stroke=\"red\" fill=\"transparent\" \/>\n <rect data-location=\"scoop_store\" x=\"191\" y=\"127\" width=\"82\" height=\"66\" stroke=\"red\" fill=\"transparent\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/_generated/areas/tunnel-b.json b/db/_generated/areas/tunnel-b.json
new file mode 100644
index 0000000..bdf3a76
--- /dev/null
+++ b/db/_generated/areas/tunnel-b.json
@@ -0,0 +1 @@
+{"name":"Tunnel B","encounter_slug":"default_encounter","encounters":[{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"eyenemy"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"dandicub"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"cardiling"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"budaye"},{"encounter_rate":0.5,"daytime":true,"exp_req_mod":1,"level_range":[2,4],"monster":"cataspike"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"elofly"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"aardorn"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"nut"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"grimachin"},{"encounter_rate":0.5,"daytime":false,"exp_req_mod":1,"level_range":[3,6],"monster":"spighter"}],"requiredEncounters":10,"trainers":[{"name":"Florist Iris","sprite":"florist.png","monsters":[{"slug":"tourbidi","level":18},{"slug":"tourbidi","level":18},{"slug":"tourbidi","level":18}]}],"environment_slug":"forest","connections":{"timber-town":{"conditions":[],"name":"Timber Town"},"route6":{"conditions":["encounters","trainers"],"name":"Route 6"}},"environment":{"slug":"forest","battle_graphics":{"island_back":"woodland_island.png","island_front":"woodland_island.png","background":"forest_background.png"},"battle_music":"music_battle_loop"},"map":"<svg\n version=\"1.1\"\n xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n viewbox=\"0 0 320 640\"\n>\n <image href=\"\/db\/maps\/Tunnel_Route_B.png\" \/>\n<\/svg>\n"} \ No newline at end of file
diff --git a/db/areas.php b/db/areas.php
index 185589e..d423a3e 100644
--- a/db/areas.php
+++ b/db/areas.php
@@ -8,10 +8,15 @@ foreach (scandir(__DIR__ . '/areas') as $file) {
$area = json_decode(file_get_contents($filePath), true);
$encounters = json_decode(@file_get_contents(dirname(__DIR__) . "/modules/tuxemon/mods/tuxemon/db/encounter/$area[encounter_slug].json") ?? '', true);
- $environment = json_decode(file_get_contents(dirname(__DIR__) . "/modules/tuxemon/mods/tuxemon/db/environment/$area[environment_slug].json"), true);
+ $environment = json_decode(@file_get_contents(dirname(__DIR__) . "/modules/tuxemon/mods/tuxemon/db/environment/$area[environment_slug].json") ?? '', true);
$map = @file_get_contents(__DIR__ . "/maps/$fileName.svg");
+ $area['encounters'] ??= [];
array_push($area['encounters'], ...$encounters['monsters'] ?? []);
+ $area['requiredEncounters'] ??= 0;
+
+ $area['trainers'] ??= [];
+
$area['environment'] = $environment;
$area['map'] = $map;
diff --git a/db/areas/candy-town.json b/db/areas/candy-town.json
new file mode 100644
index 0000000..59ddca0
--- /dev/null
+++ b/db/areas/candy-town.json
@@ -0,0 +1,26 @@
+{
+ "name": "Candy Town",
+
+ "locations": {
+ "hospital": {
+ "type": "healingCenter",
+ "price": 30
+ },
+
+ "scoop_store": {
+ "type": "shop",
+ "economy": "spyder_candy_scoop",
+ "items": []
+ }
+ },
+
+ "connections": {
+ "route6": {
+ "conditions": []
+ },
+
+ "sea-route-c": {
+ "conditions": []
+ }
+ }
+}
diff --git a/db/areas/city-park.json b/db/areas/city-park.json
index d0e5af3..865df3c 100644
--- a/db/areas/city-park.json
+++ b/db/areas/city-park.json
@@ -20,8 +20,7 @@
"slug": "shybulb",
"level": 8
}
- ],
- "inventory": []
+ ]
}
],
diff --git a/db/areas/flower-city.json b/db/areas/flower-city.json
new file mode 100644
index 0000000..1e7ebf9
--- /dev/null
+++ b/db/areas/flower-city.json
@@ -0,0 +1,42 @@
+{
+ "name": "Flower City",
+
+ "encounter_slug": "",
+
+ "encounters": [],
+
+ "requiredEncounters": 0,
+
+ "trainers": [],
+
+ "environment_slug": "interior",
+
+ "locations": {
+ "healing_center": {
+ "type": "healingCenter",
+ "price": 30
+ },
+
+ "scoop_store": {
+ "type": "shop",
+ "economy": "spyder_flower_scoop",
+ "items": []
+ }
+ },
+
+ "events": {},
+
+ "connections": {
+ "route4": {
+ "conditions": []
+ },
+
+ "route5": {
+ "conditions": []
+ },
+
+ "side-route-a": {
+ "conditions": []
+ }
+ }
+}
diff --git a/db/areas/lion-mountain.json b/db/areas/lion-mountain.json
new file mode 100644
index 0000000..fc804f8
--- /dev/null
+++ b/db/areas/lion-mountain.json
@@ -0,0 +1,34 @@
+{
+ "name": "Lion Mountain",
+
+ "encounter_slug": "lion_mountain",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Miner Jacques",
+ "sprite": "monk.png",
+ "monsters": [
+ {
+ "slug": "heronquak",
+ "level": 55
+ },
+ {
+ "slug": "jemuar",
+ "level": 60
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "snow",
+
+ "connections": {
+ "sphalian-town": {
+ "conditions": []
+ }
+ }
+}
diff --git a/db/areas/paper-town.json b/db/areas/paper-town.json
index 688dd7f..f3d3255 100644
--- a/db/areas/paper-town.json
+++ b/db/areas/paper-town.json
@@ -1,26 +1,18 @@
{
"name": "Paper Town",
- "encounter_slug": "",
+ "locations": {
+ "home": {
+ "type": "healingCenter",
+ "price": 0
+ },
- "encounters": [],
-
- "requiredEncounters": 0,
-
- "trainers": [
- {
- "name": "Rival",
- "monsters": [
- {
- "slug": "STARTER",
- "level": 2
- }
- ],
- "inventory": []
+ "scoop_store": {
+ "type": "shop",
+ "economy": "spyder_paper_mart",
+ "items": []
}
- ],
-
- "environment_slug": "interior",
+ },
"connections": {
"route1": {
@@ -28,6 +20,12 @@
"encounters",
"trainers"
]
+ },
+
+ "sea-route-c": {
+ "conditions": [
+ "area.sea-route-c"
+ ]
}
}
}
diff --git a/db/areas/route1.json b/db/areas/route1.json
index 5c49e04..0c9523f 100644
--- a/db/areas/route1.json
+++ b/db/areas/route1.json
@@ -10,7 +10,7 @@
"trainers": [
{
"name": "Bruder Mikki",
- "sprite": "dragonrider.png",
+ "sprite": "adventurer.png",
"monsters": [
{
"slug": "memnomnom",
diff --git a/db/areas/route2.json b/db/areas/route2.json
index b08e1af..5e56ad9 100644
--- a/db/areas/route2.json
+++ b/db/areas/route2.json
@@ -9,7 +9,8 @@
"trainers": [
{
- "name": "Rival",
+ "name": "Rival Billie",
+ "sprite": "fashionista.png",
"monsters": [
{
"slug": "cardiling",
@@ -23,8 +24,7 @@
"slug": "STARTER",
"level": 6
}
- ],
- "inventory": []
+ ]
}
],
diff --git a/db/areas/route3.json b/db/areas/route3.json
index ea59234..5dbcba3 100644
--- a/db/areas/route3.json
+++ b/db/areas/route3.json
@@ -29,6 +29,13 @@
"connections": {
"leather-town": {
"conditions": []
+ },
+
+ "route4": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
}
}
}
diff --git a/db/areas/route4.json b/db/areas/route4.json
new file mode 100644
index 0000000..a0c4a97
--- /dev/null
+++ b/db/areas/route4.json
@@ -0,0 +1,63 @@
+{
+ "name": "Route 4",
+
+ "encounter_slug": "route4",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Soldier Marshall",
+ "sprite": "soldier.png",
+ "monsters": [
+ {
+ "slug": "puparmor",
+ "level": 16
+ },
+ {
+ "slug": "puparmor",
+ "level": 16
+ }
+ ]
+ },
+ {
+ "name": "Rival Billie",
+ "sprite": "fashionista.png",
+ "monsters": [
+ {
+ "slug": "cardiwing",
+ "level": 16
+ },
+ {
+ "slug": "eyesore",
+ "level": 16
+ },
+ {
+ "slug": "viviphyta",
+ "level": 16
+ },
+ {
+ "slug": "STARTER",
+ "level": 18
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "forest",
+
+ "connections": {
+ "route3": {
+ "conditions": []
+ },
+
+ "flower-city": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
+ }
+ }
+}
diff --git a/db/areas/route5.json b/db/areas/route5.json
new file mode 100644
index 0000000..368ddbc
--- /dev/null
+++ b/db/areas/route5.json
@@ -0,0 +1,45 @@
+{
+ "name": "Route 5",
+
+ "encounter_slug": "route5",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Soldier Hunter",
+ "sprite": "soldier.png",
+ "monsters": [
+ {
+ "slug": "elofly",
+ "level": 16
+ },
+ {
+ "slug": "elofly",
+ "level": 16
+ },
+ {
+ "slug": "elowind",
+ "level": 18
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "forest",
+
+ "connections": {
+ "flower-city": {
+ "conditions": []
+ },
+
+ "timber-town": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
+ }
+ }
+}
diff --git a/db/areas/route6.json b/db/areas/route6.json
new file mode 100644
index 0000000..f30d050
--- /dev/null
+++ b/db/areas/route6.json
@@ -0,0 +1,45 @@
+{
+ "name": "Route 6",
+
+ "encounter_slug": "route6",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Florist Frances",
+ "sprite": "florist.png",
+ "monsters": [
+ {
+ "slug": "narcileaf",
+ "level": 30
+ },
+ {
+ "slug": "shybulb",
+ "level": 30
+ },
+ {
+ "slug": "shybulb",
+ "level": 30
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "forest",
+
+ "connections": {
+ "tunnel-b": {
+ "conditions": []
+ },
+
+ "candy-town": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
+ }
+ }
+}
diff --git a/db/areas/route7.json b/db/areas/route7.json
new file mode 100644
index 0000000..647227a
--- /dev/null
+++ b/db/areas/route7.json
@@ -0,0 +1,49 @@
+{
+ "name": "Route 7",
+
+ "encounter_slug": "route7",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Monk Statius",
+ "sprite": "monk.png",
+ "monsters": [
+ {
+ "slug": "aardart",
+ "level": 30
+ },
+ {
+ "slug": "pairagrim",
+ "level": 30
+ },
+ {
+ "slug": "bolt",
+ "level": 30
+ },
+ {
+ "slug": "fribbit",
+ "level": 30
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "forest",
+
+ "connections": {
+ "side-route-a": {
+ "conditions": []
+ },
+
+ "sphalian-town": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
+ }
+ }
+}
diff --git a/db/areas/sea-route-c.json b/db/areas/sea-route-c.json
new file mode 100644
index 0000000..34526f5
--- /dev/null
+++ b/db/areas/sea-route-c.json
@@ -0,0 +1,37 @@
+{
+ "name": "Sea Route C",
+
+ "encounter_slug": "default_encounter",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Dragonrider Nigel",
+ "sprite": "dragonrider.png",
+ "monsters": [
+ {
+ "slug": "agnigon",
+ "level": 36
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "sea",
+
+ "connections": {
+ "candy-town": {
+ "conditions": []
+ },
+
+ "paper-town": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
+ }
+ }
+}
diff --git a/db/areas/side-route-a.json b/db/areas/side-route-a.json
new file mode 100644
index 0000000..b83c51b
--- /dev/null
+++ b/db/areas/side-route-a.json
@@ -0,0 +1,49 @@
+{
+ "name": "Side Route A",
+
+ "encounter_slug": "default_encounter",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Rival Billie",
+ "sprite": "fashionista.png",
+ "monsters": [
+ {
+ "slug": "cardiwing",
+ "level": 17
+ },
+ {
+ "slug": "eyesore",
+ "level": 17
+ },
+ {
+ "slug": "viviphyta",
+ "level": 17
+ },
+ {
+ "slug": "STARTER",
+ "level": 20
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "forest",
+
+ "connections": {
+ "flower-city": {
+ "conditions": []
+ },
+
+ "route7": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
+ }
+ }
+}
diff --git a/db/areas/sphalian-town.json b/db/areas/sphalian-town.json
new file mode 100644
index 0000000..4c70aa5
--- /dev/null
+++ b/db/areas/sphalian-town.json
@@ -0,0 +1,20 @@
+{
+ "name": "Sphalian Town",
+
+ "locations": {
+ "healing_center": {
+ "type": "healingCenter",
+ "price": 40
+ }
+ },
+
+ "connections": {
+ "route7": {
+ "conditions": []
+ },
+
+ "lion-mountain": {
+ "conditions": []
+ }
+ }
+}
diff --git a/db/areas/timber-town.json b/db/areas/timber-town.json
new file mode 100644
index 0000000..bfb9ca7
--- /dev/null
+++ b/db/areas/timber-town.json
@@ -0,0 +1,26 @@
+{
+ "name": "Timber Town",
+
+ "locations": {
+ "healing_center": {
+ "type": "healingCenter",
+ "price": 20
+ },
+
+ "scoop_store": {
+ "type": "shop",
+ "economy": "spyder_timber_scoop",
+ "items": []
+ }
+ },
+
+ "connections": {
+ "route5": {
+ "conditions": []
+ },
+
+ "tunnel-b": {
+ "conditions": []
+ }
+ }
+}
diff --git a/db/areas/tunnel-b.json b/db/areas/tunnel-b.json
new file mode 100644
index 0000000..0e7954c
--- /dev/null
+++ b/db/areas/tunnel-b.json
@@ -0,0 +1,45 @@
+{
+ "name": "Tunnel B",
+
+ "encounter_slug": "default_encounter",
+
+ "encounters": [],
+
+ "requiredEncounters": 10,
+
+ "trainers": [
+ {
+ "name": "Florist Iris",
+ "sprite": "florist.png",
+ "monsters": [
+ {
+ "slug": "tourbidi",
+ "level": 18
+ },
+ {
+ "slug": "tourbidi",
+ "level": 18
+ },
+ {
+ "slug": "tourbidi",
+ "level": 18
+ }
+ ]
+ }
+ ],
+
+ "environment_slug": "forest",
+
+ "connections": {
+ "timber-town": {
+ "conditions": []
+ },
+
+ "route6": {
+ "conditions": [
+ "encounters",
+ "trainers"
+ ]
+ }
+ }
+}
diff --git a/db/maps/Buddha_mountain.png b/db/maps/Buddha_mountain.png
new file mode 100644
index 0000000..96fb617
--- /dev/null
+++ b/db/maps/Buddha_mountain.png
Binary files differ
diff --git a/db/maps/Candy_Town.png b/db/maps/Candy_Town.png
new file mode 100644
index 0000000..ad067cf
--- /dev/null
+++ b/db/maps/Candy_Town.png
Binary files differ
diff --git a/db/maps/City_park.png b/db/maps/City_park.png
new file mode 100644
index 0000000..01d67c0
--- /dev/null
+++ b/db/maps/City_park.png
Binary files differ
diff --git a/db/maps/Flower_City.png b/db/maps/Flower_City.png
new file mode 100644
index 0000000..2b9527a
--- /dev/null
+++ b/db/maps/Flower_City.png
Binary files differ
diff --git a/db/maps/Leather_Town.png b/db/maps/Leather_Town.png
new file mode 100644
index 0000000..0599799
--- /dev/null
+++ b/db/maps/Leather_Town.png
Binary files differ
diff --git a/db/maps/Paper_town.png b/db/maps/Paper_town.png
new file mode 100644
index 0000000..1eb12ef
--- /dev/null
+++ b/db/maps/Paper_town.png
Binary files differ
diff --git a/db/maps/Route_1.png b/db/maps/Route_1.png
new file mode 100644
index 0000000..b34e1b2
--- /dev/null
+++ b/db/maps/Route_1.png
Binary files differ
diff --git a/db/maps/Route_2.png b/db/maps/Route_2.png
new file mode 100644
index 0000000..ffe3c1b
--- /dev/null
+++ b/db/maps/Route_2.png
Binary files differ
diff --git a/db/maps/Route_3.png b/db/maps/Route_3.png
new file mode 100644
index 0000000..a772dbb
--- /dev/null
+++ b/db/maps/Route_3.png
Binary files differ
diff --git a/db/maps/Route_4.png b/db/maps/Route_4.png
new file mode 100644
index 0000000..13510c6
--- /dev/null
+++ b/db/maps/Route_4.png
Binary files differ
diff --git a/db/maps/Route_5.png b/db/maps/Route_5.png
new file mode 100644
index 0000000..793e2d4
--- /dev/null
+++ b/db/maps/Route_5.png
Binary files differ
diff --git a/db/maps/Route_6.png b/db/maps/Route_6.png
new file mode 100644
index 0000000..30082b5
--- /dev/null
+++ b/db/maps/Route_6.png
Binary files differ
diff --git a/db/maps/Route_7.png b/db/maps/Route_7.png
new file mode 100644
index 0000000..4600d3e
--- /dev/null
+++ b/db/maps/Route_7.png
Binary files differ
diff --git a/db/maps/Sea_Route_C.png b/db/maps/Sea_Route_C.png
new file mode 100644
index 0000000..4afeb1a
--- /dev/null
+++ b/db/maps/Sea_Route_C.png
Binary files differ
diff --git a/db/maps/Side_Route_A.png b/db/maps/Side_Route_A.png
new file mode 100644
index 0000000..3a13711
--- /dev/null
+++ b/db/maps/Side_Route_A.png
Binary files differ
diff --git a/db/maps/Sphalian_town.png b/db/maps/Sphalian_town.png
new file mode 100644
index 0000000..87c9de5
--- /dev/null
+++ b/db/maps/Sphalian_town.png
Binary files differ
diff --git a/db/maps/Timber_Town.png b/db/maps/Timber_Town.png
new file mode 100644
index 0000000..e81419f
--- /dev/null
+++ b/db/maps/Timber_Town.png
Binary files differ
diff --git a/db/maps/Tunnel_Route_B.png b/db/maps/Tunnel_Route_B.png
new file mode 100644
index 0000000..9b6cb0d
--- /dev/null
+++ b/db/maps/Tunnel_Route_B.png
Binary files differ
diff --git a/db/maps/Tunnel_Route_B_-_Below.png b/db/maps/Tunnel_Route_B_-_Below.png
new file mode 100644
index 0000000..84e3645
--- /dev/null
+++ b/db/maps/Tunnel_Route_B_-_Below.png
Binary files differ
diff --git a/db/maps/candy-town.svg b/db/maps/candy-town.svg
new file mode 100644
index 0000000..2dab5f3
--- /dev/null
+++ b/db/maps/candy-town.svg
@@ -0,0 +1,10 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/Candy_Town.png" />
+
+ <rect data-location="hospital" x="127" y="431" width="210" height="130" stroke="red" fill="transparent" />
+ <rect data-location="scoop_store" x="17" y="144" width="80" height="64" stroke="red" fill="transparent" />
+</svg>
diff --git a/db/maps/city-park.svg b/db/maps/city-park.svg
new file mode 100644
index 0000000..91a32cd
--- /dev/null
+++ b/db/maps/city-park.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/City_park.png" />
+</svg>
diff --git a/db/maps/flower-city.svg b/db/maps/flower-city.svg
new file mode 100644
index 0000000..dc4c4b2
--- /dev/null
+++ b/db/maps/flower-city.svg
@@ -0,0 +1,10 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/Flower_City.png" />
+
+ <rect data-location="healing_center" x="224" y="192" width="80" height="64" stroke="red" fill="transparent" />
+ <rect data-location="scoop_store" x="368" y="320" width="80" height="64" stroke="red" fill="transparent" />
+</svg>
diff --git a/db/maps/leather-town.svg b/db/maps/leather-town.svg
new file mode 100644
index 0000000..9607b32
--- /dev/null
+++ b/db/maps/leather-town.svg
@@ -0,0 +1,13 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/Leather_Town.png" />
+
+ <text x="323" y="174">Healing Center</text>
+ <rect data-location="healing_center" x="336" y="97" width="80" height="64" stroke="red" fill="transparent" />
+
+ <text x="226" y="174">Shop</text>
+ <rect data-location="leather_scoop" x="208" y="97" width="80" height="64" stroke="red" fill="transparent" />
+</svg>
diff --git a/db/maps/lion-mountain.svg b/db/maps/lion-mountain.svg
new file mode 100644
index 0000000..6367992
--- /dev/null
+++ b/db/maps/lion-mountain.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 720 1600"
+>
+ <image href="/db/maps/Buddha_mountain.png" />
+</svg>
diff --git a/db/maps/paper-town.svg b/db/maps/paper-town.svg
new file mode 100644
index 0000000..b0c929d
--- /dev/null
+++ b/db/maps/paper-town.svg
@@ -0,0 +1,10 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 320"
+>
+ <image href="/db/maps/Paper_town.png" />
+
+ <text x="294" y="221">Shop</text>
+ <rect data-location="scoop_store" x="272" y="144" width="80" height="64" stroke="red" fill="transparent" />
+</svg>
diff --git a/db/maps/route1.svg b/db/maps/route1.svg
new file mode 100644
index 0000000..b7f1647
--- /dev/null
+++ b/db/maps/route1.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 320"
+>
+ <image href="/db/maps/Route_1.png" />
+</svg>
diff --git a/db/maps/route2.svg b/db/maps/route2.svg
new file mode 100644
index 0000000..7471402
--- /dev/null
+++ b/db/maps/route2.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 320"
+>
+ <image href="/db/maps/Route_2.png" />
+</svg>
diff --git a/db/maps/route3.svg b/db/maps/route3.svg
new file mode 100644
index 0000000..c9f8196
--- /dev/null
+++ b/db/maps/route3.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/Route_3.png" />
+</svg>
diff --git a/db/maps/route4.svg b/db/maps/route4.svg
new file mode 100644
index 0000000..4f60aec
--- /dev/null
+++ b/db/maps/route4.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 320 640"
+>
+ <image href="/db/maps/Route_4.png" />
+</svg>
diff --git a/db/maps/route5.svg b/db/maps/route5.svg
new file mode 100644
index 0000000..3a776da
--- /dev/null
+++ b/db/maps/route5.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 320"
+>
+ <image href="/db/maps/Route_5.png" />
+</svg>
diff --git a/db/maps/route6.svg b/db/maps/route6.svg
new file mode 100644
index 0000000..84ce82d
--- /dev/null
+++ b/db/maps/route6.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 320"
+>
+ <image href="/db/maps/Route_6.png" />
+</svg>
diff --git a/db/maps/route7.svg b/db/maps/route7.svg
new file mode 100644
index 0000000..038bc9b
--- /dev/null
+++ b/db/maps/route7.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/Route_7.png" />
+</svg>
diff --git a/db/maps/sea-route-c.svg b/db/maps/sea-route-c.svg
new file mode 100644
index 0000000..aa93f1e
--- /dev/null
+++ b/db/maps/sea-route-c.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/Sea_Route_C.png" />
+</svg>
diff --git a/db/maps/side-route-a.svg b/db/maps/side-route-a.svg
new file mode 100644
index 0000000..48967fe
--- /dev/null
+++ b/db/maps/side-route-a.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 320 640"
+>
+ <image href="/db/maps/Side_Route_A.png" />
+</svg>
diff --git a/db/maps/sphalian-town.svg b/db/maps/sphalian-town.svg
new file mode 100644
index 0000000..4b9e51f
--- /dev/null
+++ b/db/maps/sphalian-town.svg
@@ -0,0 +1,9 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 368 400"
+>
+ <image href="/db/maps/Sphalian_town.png" />
+
+ <rect data-location="healing_center" x="240" y="48" width="80" height="64" stroke="red" fill="transparent" />
+</svg>
diff --git a/db/maps/timber-town.svg b/db/maps/timber-town.svg
new file mode 100644
index 0000000..5b76f85
--- /dev/null
+++ b/db/maps/timber-town.svg
@@ -0,0 +1,10 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 640 640"
+>
+ <image href="/db/maps/Timber_Town.png" />
+
+ <rect data-location="healing_center" x="96" y="48" width="80" height="64" stroke="red" fill="transparent" />
+ <rect data-location="scoop_store" x="191" y="127" width="82" height="66" stroke="red" fill="transparent" />
+</svg>
diff --git a/db/maps/tunnel-b-underground.svg b/db/maps/tunnel-b-underground.svg
new file mode 100644
index 0000000..af07768
--- /dev/null
+++ b/db/maps/tunnel-b-underground.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 320 640"
+>
+ <image href="/db/maps/Tunnel_Route_B_-_Below.png" />
+</svg>
diff --git a/db/maps/tunnel-b.svg b/db/maps/tunnel-b.svg
new file mode 100644
index 0000000..19bc73a
--- /dev/null
+++ b/db/maps/tunnel-b.svg
@@ -0,0 +1,7 @@
+<svg
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ viewbox="0 0 320 640"
+>
+ <image href="/db/maps/Tunnel_Route_B.png" />
+</svg>
diff --git a/index.html b/index.html
index 73e5f93..e9c7154 100644
--- a/index.html
+++ b/index.html
@@ -4,6 +4,9 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
+ <title>TuxemonClicker</title>
+ <link rel="icon" href="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/tuxemon.png">
+
<link rel="stylesheet" href="/resources/css/variables.css" />
<link rel="stylesheet" href="/resources/css/page.css" />
<link rel="stylesheet" href="/resources/css/battle.css" />
@@ -25,18 +28,20 @@
<div id="techniques"></div>
</div>
- <div id="scene__town" class="hidden"></div>
+ <div id="scene__town" class="hidden">
+ <div data-template-slot="map"></div>
+ </div>
<div id="log"></div>
<div id="status">
<div class="status__area">
- <span><img data-template-slot="showMap" src="/modules/tuxemon/mods/tuxemon/gfx/items/app_map.png" /> <span data-template-slot="area" title="Area"></span></span>
+ <span title="Area"><img data-template-slot="showMap" src="/modules/tuxemon/mods/tuxemon/gfx/items/app_map.png" /> <span data-template-slot="area"></span></span>
</div>
<div class="status__numbers">
- <span><i title="Money">&curren;</i> <span data-template-slot="money"></span></span>
- <span><img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/tuxemon.png" title="Monster Progress" /> <span data-template-slot="monsterProgress"></span></span>
- <span><img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/player.png" title="Trainer Progress" style="filter: invert(1);" /> <span data-template-slot="trainerProgress"></span></span>
+ <span title="Money">&curren; <span data-template-slot="money"></span></span>
+ <span title="Monster Progress"><img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/tuxemon.png" /> <span data-template-slot="monsterProgress"></span></span>
+ <span title="Trainer Progress"><img src="/modules/tuxemon/mods/tuxemon/gfx/ui/menu/player.png" style="filter: invert(1);" /> <span data-template-slot="trainerProgress"></span></span>
</div>
<div class="status__actions">
<button data-template-slot="nextTrainer" class="menu-button">&rArr; Next Trainer</button>
@@ -168,6 +173,25 @@
</div>
</template>
+ <template id="tpl___healing-center">
+ <div class="healing-center">
+ <div>Price for Full Heal per Tuxemon: <span data-template-slot="price"></span></div>
+ <button data-template-slot="heal" class="menu-button">Select to Heal</button>
+ </div>
+ </template>
+
+ <template id="tpl___shop">
+ <div class="shop"></div>
+ </template>
+
+ <template id="tpl___shop__item">
+ <div class="shop__item">
+ <img data-template-slot="sprite" src="" />
+ <span data-template-slot="name"></span>
+ <span data-template-slot="price"></span>
+ </div>
+ </template>
+
<template id="tpl___party">
<div class="party">
<div data-template-slot="monsters" class="party__monsters"></div>
@@ -188,6 +212,7 @@
Lv. <span data-template-slot="level"></span>
</div>
<div>
+ <span data-template-slot="statusEffect"></span>
HP <span data-template-slot="hpText"></span>
</div>
</div>
@@ -309,13 +334,13 @@
<select data-template-slot="language"></select>
</label>
- <br><br>
-
<label>
Currency
<select data-template-slot="currency"></select>
<div>Last Updated: <span data-template-slot="currency.lastUpdated"></span></div>
</label>
+
+ <button data-template-slot="highlight" class="menu-button">Highlight Interactive Elements</button>
</div>
</template>
diff --git a/resources/css/battle.css b/resources/css/battle.css
index 748dad1..681b2e6 100644
--- a/resources/css/battle.css
+++ b/resources/css/battle.css
@@ -7,7 +7,6 @@
flex-direction: column;
justify-content: space-between;
- background-image: url('/modules/tuxemon/mods/tuxemon/gfx/ui/combat/sea_background.png');
background-size: cover;
}
diff --git a/resources/css/menu.css b/resources/css/menu.css
index 8b0cb0b..82ba18e 100644
--- a/resources/css/menu.css
+++ b/resources/css/menu.css
@@ -112,15 +112,24 @@
.status__area > span > span {
margin: 0 0.5rem;
}
+.status__area img {
+ cursor: pointer;
+}
.status__numbers {
- display: flex;
- justify-content: space-between;
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
}
.status__numbers > span {
display: flex;
align-items: center;
}
+.status__numbers > span:nth-child(2) {
+ justify-content: center;
+}
+.status__numbers > span:nth-child(3) {
+ justify-content: flex-end;
+}
.status__numbers > span > span {
margin-left: 0.5rem;
}
@@ -325,3 +334,18 @@
font-size: 2rem;
width: 80vw;
}
+
+
+
+
+.menu__settings {
+ padding: 1rem;
+ display: grid;
+ grid-gap: 1rem;
+}
+
+.setting-highlight {
+ border: 2px solid yellow;
+ stroke: yellow;
+ stroke-width: 2px;
+}
diff --git a/resources/css/page.css b/resources/css/page.css
index 57c46a5..635a160 100644
--- a/resources/css/page.css
+++ b/resources/css/page.css
@@ -27,3 +27,13 @@ img {
.hidden {
display: none;
}
+
+svg {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+svg [data-location] {
+ cursor: pointer;
+}
diff --git a/resources/css/town.css b/resources/css/town.css
index b8be144..832c3d8 100644
--- a/resources/css/town.css
+++ b/resources/css/town.css
@@ -1,11 +1,25 @@
#scene__town {}
-#scene__town svg {
- width: 100%;
- height: 100%;
- display: block;
+
+
+
+.healing-center {
+ padding: 1rem;
+}
+
+
+
+
+.shop {
+ padding: 1rem;
+
+ display: grid;
+ grid-gap: 1rem;
}
-svg [data-location] {
+.shop__item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
cursor: pointer;
}
diff --git a/resources/js/classes/Area.js b/resources/js/classes/Area.js
index b0bd710..e8d7acc 100644
--- a/resources/js/classes/Area.js
+++ b/resources/js/classes/Area.js
@@ -35,6 +35,9 @@ class Area {
return DB.areas[this.slug].map;
}
+ /**
+ * @returns {Object[]}
+ */
get locations () {
return DB.areas[this.slug].locations;
}
diff --git a/resources/js/classes/Item.js b/resources/js/classes/Item.js
index 9da6a92..40089f2 100644
--- a/resources/js/classes/Item.js
+++ b/resources/js/classes/Item.js
@@ -20,7 +20,13 @@ class Item {
}
get sprite () {
- return DB.items[this.slug].sprite;
+ const sprite = DB.items[this.slug].sprite;
+
+ if (sprite) {
+ return '/modules/tuxemon/mods/tuxemon/' + sprite;
+ }
+
+ return '';
}
get conditions () {
diff --git a/resources/js/classes/Monster.js b/resources/js/classes/Monster.js
index 84ea5da..c058a75 100644
--- a/resources/js/classes/Monster.js
+++ b/resources/js/classes/Monster.js
@@ -62,6 +62,9 @@ class Monster {
}
}
+ /**
+ * @returns {string[]}
+ */
get types () {
return DB.monsters[this.slug].types;
}
@@ -74,6 +77,18 @@ class Monster {
return DB.monsters[this.slug].moveset;
}
+ get catch_rate () {
+ return DB.monsters[this.slug].catch_rate;
+ }
+
+ get upper_catch_resistance () {
+ return DB.monsters[this.slug].upper_catch_resistance;
+ }
+
+ get lower_catch_resistance () {
+ return DB.monsters[this.slug].lower_catch_resistance;
+ }
+
/**
* @returns {DB_Evolution[]}
*/
diff --git a/resources/js/classes/State.js b/resources/js/classes/State.js
index 085d1c7..1a65efe 100644
--- a/resources/js/classes/State.js
+++ b/resources/js/classes/State.js
@@ -26,6 +26,11 @@ class State {
currentArea = null;
/**
+ * @type {AreaSlug}
+ */
+ lastVisitedTown = '';
+
+ /**
* @type {number}
*/
turn = 0;
diff --git a/resources/js/classes/StatusEffect.js b/resources/js/classes/StatusEffect.js
index e1ae9c9..d161761 100644
--- a/resources/js/classes/StatusEffect.js
+++ b/resources/js/classes/StatusEffect.js
@@ -18,6 +18,9 @@ class StatusEffect {
else if (['charging'].includes(this.slug)) {
this.turnsLeft = 2;
}
+ else if (['faint'].includes(this.slug)) {
+ this.turnsLeft = Number.MAX_SAFE_INTEGER;
+ }
else if (this.category === 'positive') {
this.turnsLeft = Math.ceil(Math.random() * 6) + 4;
}
diff --git a/resources/js/definitions.js b/resources/js/definitions.js
index 338fdf7..d399a3c 100644
--- a/resources/js/definitions.js
+++ b/resources/js/definitions.js
@@ -1,6 +1,7 @@
/**
* @typedef {string} MonsterSlug
* @typedef {string} TechniqueSlug
+ * @typedef {string} AreaSlug
*/
//
diff --git a/resources/js/formula.js b/resources/js/formula.js
index 7223cb8..f28ce2d 100644
--- a/resources/js/formula.js
+++ b/resources/js/formula.js
@@ -95,13 +95,89 @@ function calculateAwardedExperience (opposingMonster, participants) {
/**
* @param {Monster} opposingMonster
*
- * @returns {number[]}
+ * @returns {number}
*/
function calculateAwardedMoney (opposingMonster) {
let money = opposingMonster.level * opposingMonster.moneyModifier;
- const baseDecimalDiff = 2 - DB.currencies.map[Memory.state.Settings.currency].decimals;
- money = money * Math.pow(10, baseDecimalDiff);
+ money = convertToCurrencyBase(money);
return money;
}
+
+/**
+ * @param {Monster} playerMonster
+ * @param {Monster} opposingMonster
+ * @param {Item} ball
+ *
+ * @returns {boolean}
+ */
+function checkCapture (playerMonster, opposingMonster, ball) {
+ const MAX_CATCH_RATE = 255;
+ const MAX_ATTEMPT_RATE = 65536;
+ const ATTEMPT_CONSTANT = 524325;
+
+ // status effect
+ let STATUS_MODIFER = 1.0;
+ if (opposingMonster.statusEffect && opposingMonster.statusEffect.category === 'negative') {
+ STATUS_MODIFER = 1.2;
+ }
+
+ // ball
+ let BALL_MODIFIER = 1.0;
+ if (ball.slug === 'tuxeball_wood') {
+ if (opposingMonster.types.includes(ElementType.wood)) {
+ BALL_MODIFIER = 1.5
+ } else {
+ BALL_MODIFIER = 0.2;
+ }
+ }
+ else if (ball.slug === 'tuxeball_fire') {
+ if (opposingMonster.types.includes(ElementType.fire)) {
+ BALL_MODIFIER = 1.5
+ } else {
+ BALL_MODIFIER = 0.2;
+ }
+ }
+ else if (ball.slug === 'tuxeball_earth') {
+ if (opposingMonster.types.includes(ElementType.earth)) {
+ BALL_MODIFIER = 1.5
+ } else {
+ BALL_MODIFIER = 0.2;
+ }
+ }
+ else if (ball.slug === 'tuxeball_metal') {
+ if (opposingMonster.types.includes(ElementType.metal)) {
+ BALL_MODIFIER = 1.5
+ } else {
+ BALL_MODIFIER = 0.2;
+ }
+ }
+ else if (ball.slug === 'tuxeball_water') {
+ if (opposingMonster.types.includes(ElementType.water)) {
+ BALL_MODIFIER = 1.5
+ } else {
+ BALL_MODIFIER = 0.2;
+ }
+ }
+
+ // calculate
+ const catchCheck = Math.max(
+ (3 * opposingMonster.hp - 2 * playerMonster.hp)
+ * opposingMonster.catch_rate
+ * STATUS_MODIFER
+ * BALL_MODIFIER
+ / (3 * opposingMonster.hp),
+ 1
+ );
+
+ let attemptCheck = ATTEMPT_CONSTANT / (
+ Math.sqrt(Math.sqrt(MAX_CATCH_RATE / catchCheck)) * 8
+ )
+
+ const catchResistance = Math.random() * (opposingMonster.upper_catch_resistance - opposingMonster.lower_catch_resistance) + opposingMonster.lower_catch_resistance;
+
+ attemptCheck = attemptCheck * catchResistance;
+
+ return Math.random() * MAX_ATTEMPT_RATE > Math.round(attemptCheck);
+}
diff --git a/resources/js/game.js b/resources/js/game.js
index 9370382..663e6b0 100644
--- a/resources/js/game.js
+++ b/resources/js/game.js
@@ -1,5 +1,6 @@
const Game = {
phases: {
+ preTurnBegin: [],
preTurn: [],
battle: {
preAction: {
@@ -16,6 +17,7 @@ const Game = {
},
},
postTurn: [],
+ postTurnEnd: [],
},
logMessages: [],
@@ -73,6 +75,11 @@ const Game = {
Game.logTurn('end');
+ for (const event of Game.phases.postTurnEnd) {
+ event();
+ }
+ Game.phases.postTurnEnd = [];
+
UI.progressTurn();
Game.isProgressingTurn = false;
},
@@ -133,29 +140,33 @@ const Game = {
}
Game.removeBattlePhaseEvents('action', 'opponent');
+ Memory.state.player.activeMonster.statusEffect = await fetchStatusEffect('faint');
+
// whole party defeated
if (!Memory.state.player.monsters.some((monster) => monster.hp > 0)) {
- if (Game.isBattleType('trainer')) {
- if (Memory.state.currentArea.encounters.length > 0) {
- await Game.encounterWildMonster();
- } else {
- await Game.encounterTrainer();
- }
- }
+ Game.isInBattle = false;
+ Memory.state.currentArea.monsterProgress = 0;
- else if (Game.isBattleType('monster')) {
- if (Memory.state.currentArea.monsterProgress < Memory.state.currentArea.requiredEncounters) {
- Memory.state.currentArea.monsterProgress = 0;
- UI.drawStatus();
- }
-
- await Game.encounterWildMonster();
- }
+ // go to last visited town
+ await Game.goToArea(Memory.state.lastVisitedTown);
// heal all monsters full
+ let totalHealingCenterPrice = 0;
for (const monster of Memory.state.player.monsters) {
monster.hp = monster.stats.hp;
+ monster.statusEffect = null;
+
+ // pay healing center
+ const healingCenterPrice = Object.values(Memory.state.currentArea.locations).find((location) => location.type === 'healingCenter').price;
+ totalHealingCenterPrice += healingCenterPrice;
}
+
+ Memory.state.money -= totalHealingCenterPrice;
+
+ Game.addPhaseEvent('postTurnEnd', () => {
+ Game.log(`Whited out!`);
+ Game.log(`Payed ${formatPrice(totalHealingCenterPrice)} for full recovery at ${Memory.state.currentArea.name}!`);
+ });
}
// party members still left
@@ -189,7 +200,7 @@ const Game = {
},
/**
- * @param {('preTurn' | 'postTurn')} phase
+ * @param {('preTurnBegin' | 'preTurn' | 'postTurn' | 'postTurnEnd')} phase
* @param {Function} event
*/
addPhaseEvent (phase, event) {
@@ -459,6 +470,14 @@ const Game = {
});
}
+ // confused
+ else if (monster.statusEffect.slug === 'confused') {
+ Game.addBattlePhaseEvent('preAction', monster, () => {
+ // TODO
+ logStatusIs();
+ });
+ }
+
// stuck
else if (monster.statusEffect.slug === 'stuck') {
for (const technique of monster.activeTechniques) {
@@ -533,25 +552,15 @@ const Game = {
});
},
- /**
- * @param {MouseEvent} event
- */
- async battleClick (event) {
- if (Game.isLoadingArea || Game.isProgressingTurn) {
- return;
- }
-
- Game.isInBattle = true;
- UI.battleClickEvent = event;
-
- // player
- await Game.tryUseTechnique(Memory.state.activeTechnique, Memory.state.player.activeMonster, Memory.state.opponent.activeMonster);
-
- // opponent
+ async opponentTryUseTechnique () {
if (!Game.opponentActionTimeout) {
let speedDifference = Memory.state.opponent.activeMonster.stats.speed - Memory.state.player.activeMonster.stats.speed;
if (speedDifference > 0) speedDifference = speedDifference / 2;
- else if (speedDifference < 0) speedDifference = speedDifference * 2;
+ else if (speedDifference < 0 && speedDifference > -100) speedDifference = speedDifference * 2;
+ let levelDifference = Memory.state.opponent.activeMonster.level - Memory.state.player.activeMonster.level;
+ if (levelDifference >= 5) levelDifference = levelDifference * 2;
+ else if (levelDifference < 0 && levelDifference > -10) levelDifference = 0;
+ else if (levelDifference <= -10) levelDifference = levelDifference / 10;
const opponentActiveMonster = Memory.state.opponent.activeMonster;
Game.opponentActionTimeout = setTimeout(async () => {
@@ -576,13 +585,31 @@ const Game = {
}
Game.opponentActionTimeout = null;
- }, Math.max(500, 2000 - (speedDifference * 10)));
+ }, Math.max(levelDifference < 10 ? 500 : 50, Math.min(2000 - (speedDifference * 10) - (levelDifference * 100), 3000)));
console.log(
'Opponent Attack Timeout',
Memory.state.opponent.activeMonster.stats.speed, Memory.state.player.activeMonster.stats.speed,
- 2000 - (speedDifference * 10)
+ 2000 - (speedDifference * 10) - (levelDifference * 100)
);
}
+ },
+
+ /**
+ * @param {MouseEvent} event
+ */
+ async battleClick (event) {
+ if (Game.isLoadingArea || Game.isProgressingTurn) {
+ return;
+ }
+
+ Game.isInBattle = true;
+ UI.battleClickEvent = event;
+
+ // player
+ await Game.tryUseTechnique(Memory.state.activeTechnique, Memory.state.player.activeMonster, Memory.state.opponent.activeMonster);
+
+ // opponent
+ await Game.opponentTryUseTechnique();
await Game.progressTurn();
},
@@ -669,7 +696,7 @@ const Game = {
}
const nextTrainer = Memory.state.currentArea.trainers[nextTrainerIdx];
- if (nextTrainer.name === 'Rival') {
+ if (nextTrainer.name.startsWith('Rival')) {
for (const idx in nextTrainer.monsters) {
if (nextTrainer.monsters[idx].slug === 'STARTER') {
nextTrainer.monsters[idx].slug = Memory.state.rivalMonster;
@@ -704,16 +731,12 @@ const Game = {
}
Memory.state.currentArea = await fetchArea(areaSlug);
- UI.drawArea();
if (Game.isTown(Memory.state.currentArea)) {
- UI.elements.sceneBattle.classList.add('hidden');
- UI.elements.sceneTown.classList.remove('hidden');
-
- UI.drawTown();
+ if (Object.values(Memory.state.currentArea.locations).some((location) => location.type === 'healingCenter')) {
+ Memory.state.lastVisitedTown = areaSlug;
+ }
} else {
- UI.elements.sceneTown.classList.add('hidden');
- UI.elements.sceneBattle.classList.remove('hidden');
if (Memory.state.currentArea.encounters.length > 0) {
await Game.encounterWildMonster();
} else if (Memory.state.currentArea.trainers.length > 0) {
@@ -721,8 +744,7 @@ const Game = {
}
}
- UI.drawStatus();
- UI.drawActiveBall();
+ UI.drawArea();
Game.isLoadingArea = false;
},
@@ -748,6 +770,10 @@ const Game = {
conditionIsApplicable = eval(`${monster.hp} ${itemCondition.comparator} ${value}`);
}
+ else if (itemCondition.what === 'status') {
+ conditionIsApplicable = monster.statusEffect && monster.statusEffect.slug === itemCondition.value.replace('status_', '');
+ }
+
else if (itemCondition.what === 'wild_monster') {
conditionIsApplicable = Game.isBattleType('monster');
}
@@ -784,6 +810,13 @@ const Game = {
UI.drawActiveMonster();
}
+ if (itemEffect.type === 'revive') {
+ monster.hp = itemEffect.amount;
+ monster.statusEffect = null;
+ item.quantity--;
+ UI.drawActiveMonster();
+ }
+
else if (itemEffect.type === 'capture') {
Memory.state.activeBall = item;
UI.drawActiveBall();
@@ -825,15 +858,41 @@ const Game = {
return;
}
- Game.clearCurrentTurn();
+ const playerMonster = Memory.state.player.activeMonster;
+ const opposingMonster = Memory.state.opponent.activeMonster;
+ const activeBall = Memory.state.activeBall;
- Memory.state.activeBall.quantity--;
- if (Memory.state.activeBall.quantity === 0) {
+ // remove ball
+ activeBall.quantity--;
+ if (activeBall.quantity === 0) {
Game.removeItemFromInventory(Memory.state.player.inventory, Memory.state.activeBall);
Memory.state.activeBall = null;
UI.drawActiveBall();
}
+ // attempt capture
+ Game.log('Attempting capture!');
+ let success = true;
+ let attempts = 1;
+ const maxAttempts = 4;
+ while (success && attempts <= maxAttempts) {
+ success = checkCapture(playerMonster, opposingMonster, activeBall);
+
+ if (!success) {
+ Game.log(`Escape attempt ${attempts}: succeeded!`);
+ Game.log(`${opposingMonster.name} broke free!`);
+
+ Game.opponentTryUseTechnique();
+ return; // can't catch
+ }
+
+ Game.log(`Escape attempt ${attempts}: failed!`);
+
+ attempts++;
+ }
+
+ Game.clearCurrentTurn();
+
const caughtMonster = new Monster(Memory.state.opponent.activeMonster.slug);
caughtMonster.initialize();
caughtMonster.level = Memory.state.opponent.activeMonster.level;
diff --git a/resources/js/helpers.js b/resources/js/helpers.js
index e9cb37d..adf0bb7 100644
--- a/resources/js/helpers.js
+++ b/resources/js/helpers.js
@@ -63,3 +63,23 @@ function randomString () {
function translate (msgid) {
return DB.translations[Memory.state.Settings.language][msgid];
}
+
+/**
+ * @param {number} amount
+ *
+ * @returns {number}
+ */
+function convertToCurrencyBase (amount) {
+ const baseDecimalDiff = 2 - DB.currencies.map[Memory.state.Settings.currency].decimals;
+
+ return amount * Math.pow(10, baseDecimalDiff);
+}
+
+/**
+ * @param {number} price
+ *
+ * @returns {number}
+ */
+function formatPrice (price) {
+ return `${price} ${DB.currencies.map[Memory.state.Settings.currency].symbol}`;
+}
diff --git a/resources/js/main.js b/resources/js/main.js
index 24eafec..6ea94e2 100644
--- a/resources/js/main.js
+++ b/resources/js/main.js
@@ -4,24 +4,36 @@
// Start Game
const possibleStarterMonsters = ['budaye', 'dollfin', 'grintot', 'ignibus', 'memnomnom'];
- Memory.state.player = new Trainer({
- monsters: [
- await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))]),
- ],
- inventory: [
- new InventoryItem(await fetchItem('tuxeball'), 5),
- new InventoryItem(await fetchItem('potion')),
- ]
- });
- await Memory.state.player.initialize();
+ const monsterSelection = UI.openStarterMonsterSelection(
+ await Promise.all(possibleStarterMonsters.map(async (monsterSlug) => await fetchMonster(monsterSlug)))
+ );
+ monsterSelection.addEventListener('starter:monster:selected', async (event) => {
+ if (!confirm(`Select ${event.detail.monster.name}?`)) {
+ return;
+ }
+
+ Memory.state.player = new Trainer({
+ monsters: [
+ event.detail.monster,
+ ],
+ inventory: [
+ new InventoryItem(await fetchItem('tuxeball'), 5),
+ new InventoryItem(await fetchItem('potion')),
+ ]
+ });
+ await Memory.state.player.initialize();
- Game.setActivePlayerMonster(Memory.state.player.monsters[0]);
- Memory.state.activeBall = Memory.state.player.inventory[0]; // tuxeball
+ Game.setActivePlayerMonster(Memory.state.player.monsters[0]);
+ Memory.state.activeBall = Memory.state.player.inventory[0]; // tuxeball
- Memory.state.rivalMonster = possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))];
+ possibleStarterMonsters.splice(possibleStarterMonsters.indexOf(event.detail.monster), 1);
+ Memory.state.rivalMonster = possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))];
- await Game.goToArea('paper-town');
+ await Game.goToArea('paper-town');
- UI.drawActiveMonster();
- UI.drawActiveTechniques();
+ UI.drawActiveMonster();
+ UI.drawActiveTechniques();
+
+ event.detail.popup.remove();
+ });
})();
diff --git a/resources/js/memory.js b/resources/js/memory.js
index 47cde88..40be4df 100644
--- a/resources/js/memory.js
+++ b/resources/js/memory.js
@@ -9,21 +9,39 @@ const Memory = {
* @returns {string}
*/
save () {
+ const prepareSaveData = (saveData) => {
+ const prepareMonster = (monster) => {
+ if (monster.statusEffect && monster.statusEffect.slug === 'lifeleech') {
+ monster.statusEffect = null;
+ }
+
+ return monster;
+ };
+
+ for (const idx in saveData.monsters) {
+ saveData.monsters[idx] = prepareMonster(saveData.monsters[idx]);
+ }
+ for (const idx in saveData.player.monsters) {
+ saveData.player.monsters[idx] = prepareMonster(saveData.player.monsters[idx]);
+ }
+ for (const idx in saveData.opponent.monsters) {
+ saveData.opponent.monsters[idx] = prepareMonster(saveData.opponent.monsters[idx]);
+ }
+
+ return JSON.parse(JSON.stringify(saveData));
+ };
+
const saveMonster = (monsterData, monsterState) => {
monsterData.level = monsterState.level;
monsterData.hp = monsterState.hp;
- if (monsterData.statusEffect && monsterData.statusEffect.slug === 'lifeleech') {
- monsterData.statusEffect = null;
- }
-
return monsterData;
};
/**
* @type {State}
*/
- const saveData = JSON.parse(JSON.stringify(Memory.state));
+ const saveData = prepareSaveData(Object.assign({}, Memory.state));
// monsters
for (const idx in saveData.monsters) {
@@ -163,6 +181,7 @@ const Memory = {
Memory.state.areaProgress[areaSlug] = await loadArea(areaData);
}
Memory.state.currentArea = await loadArea(loadedState.currentArea);
+ Memory.state.lastVisitedTown = loadedState.lastVisitedTown;
Memory.state.turn = loadedState.turn;
Memory.state.money = loadedState.money;
@@ -179,7 +198,7 @@ const Memory = {
Memory.state.activeTechnique = await loadTechnique(loadedState.activeTechnique);
Memory.state.activeBall = await loadInventoryItem(loadedState.activeBall);
- UI.drawArea(Memory.state.currentArea);
+ UI.drawArea();
UI.drawStatus();
UI.drawOpponentMonster();
UI.drawActiveMonster();
diff --git a/resources/js/ui.js b/resources/js/ui.js
index 3940574..6f38867 100644
--- a/resources/js/ui.js
+++ b/resources/js/ui.js
@@ -14,6 +14,10 @@ const Template = {
techniques: document.querySelector('#tpl___techniques'),
technique: document.querySelector('#tpl___technique'),
+ healingCenter: document.querySelector('#tpl___healing-center'),
+ shop: document.querySelector('#tpl___shop'),
+ shopItem: document.querySelector('#tpl___shop__item'),
+
party: document.querySelector('#tpl___party'),
partyMonster: document.querySelector('#tpl___party__monster'),
@@ -51,6 +55,7 @@ const UI = {
log: document.querySelector('#log'),
status: document.querySelector('#status'),
+ showMap: document.querySelector('#status [data-template-slot="showMap"]'),
nextTrainer: document.querySelector('#status [data-template-slot="nextTrainer"]'),
changeArea: document.querySelector('#status [data-template-slot="changeArea"]'),
@@ -274,6 +279,14 @@ const UI = {
return document.createElement('i');
}
+ if (statusEffect.slug === 'faint') {
+ const node = document.createElement('b');
+ node.innerHTML = 'X';
+ node.title = statusEffect.name;
+
+ return node;
+ }
+
const img = document.createElement('img');
img.src = `/modules/tuxemon/mods/tuxemon/gfx/ui/icons/status/icon_${statusEffect.slug}.png`;
img.title = statusEffect.name;
@@ -420,6 +433,10 @@ const UI = {
* @returns {void}
*/
drawActiveTechniques () {
+ if (!Memory.state.player) { // on starter selection screen only
+ return;
+ }
+
const activeTechniques = UI.createActiveTechniques(Memory.state.player.activeMonster);
activeTechniques.id = 'techniques';
@@ -495,8 +512,35 @@ const UI = {
UI.elements.log.classList.toggle('log--is-hidden');
},
+ openLog () {
+ UI.elements.log.classList.remove('log--is-hidden');
+ },
+
+ closeLog () {
+ UI.elements.log.classList.add('log--is-hidden');
+ },
+
drawArea () {
- UI.elements.battle.style.backgroundImage = `url(/modules/tuxemon/mods/tuxemon/gfx/ui/combat/${Memory.state.currentArea.environment.battle_graphics.background})`;
+ if (Game.isTown(Memory.state.currentArea)) {
+ UI.elements.sceneTown.querySelector('[data-template-slot="map"]').replaceChildren(UI.createMap());
+
+ UI.closeLog();
+
+ UI.elements.sceneBattle.classList.add('hidden');
+ UI.elements.sceneTown.classList.remove('hidden');
+ } else {
+ UI.elements.battle.style.backgroundImage = `url(/modules/tuxemon/mods/tuxemon/gfx/ui/combat/${Memory.state.currentArea.environment.battle_graphics.background})`;
+
+ UI.elements.sceneTown.classList.add('hidden');
+ UI.elements.sceneBattle.classList.remove('hidden');
+
+ UI.drawOpponentMonster();
+ UI.drawActiveMonster();
+ UI.drawActiveTechniques();
+ }
+
+ UI.drawStatus();
+ UI.drawActiveBall();
},
progressTurn () {
@@ -633,39 +677,120 @@ const UI = {
/* Town */
- async drawTown () {
+ drawTown () {},
+
+
+ /* Map */
+
+ /**
+ * @returns {HTMLElement}
+ */
+ createMap () {
+ const template = document.createElement('div');
const currentArea = Memory.state.currentArea;
- UI.elements.sceneTown.innerHTML = Memory.state.currentArea.map;
+ template.innerHTML = currentArea.map;
+ template.style.width = '100vw';
+ template.style.maxWidth = '750px';
+
+ if (currentArea.locations) {
+ for (const locationId of Object.keys(currentArea.locations)) {
+ const location = currentArea.locations[locationId];
+
+ template.querySelector(`[data-location="${locationId}"]`).addEventListener('click', () => {
+ if (location.type === 'healingCenter') {
+ UI.openHealingCenter(location);
+ }
+
+ else if (location.type === 'shop') {
+ UI.openShop(location);
+ }
+ });
+ }
+ }
+
+ return template;
+ },
- for (const locationId of Object.keys(currentArea.locations)) {
- const location = currentArea.locations[locationId];
+ /**
+ * @param {Object} healingCenter
+ */
+ openHealingCenter (healingCenter) {
+ const popup = UI.createPopup();
+ const template = UI.createTemplate(Template.healingCenter);
+
+ const price = convertToCurrencyBase(healingCenter.price);
+ template.querySelector('[data-template-slot="price"]').innerHTML = formatPrice(price);
- UI.elements.sceneTown.querySelector(`[data-location="${locationId}"]`).addEventListener('click', () => {
- if (location.type === 'healingCenter') {
- UI.openHealingCenter(location);
+ template.querySelector('[data-template-slot="heal"]').addEventListener('click', () => {
+ const applicableMonsters = Memory.state.player.monsters.filter((monster) => monster.hp < monster.stats.hp || monster.statusEffect);
+ if (applicableMonsters.length === 0) {
+ alert('No applicable monsters.');
+ return;
+ }
+
+ const monsterSelectionPopup = UI.createPopup();
+ const monsterSelection = UI.createMonsterSelection(applicableMonsters);
+
+ monsterSelection.addEventListener('monster:selected', (event) => {
+ if (Memory.state.money < price) {
+ alert(`Not enough ${DB.currencies.map[Memory.state.Settings.currency].symbol}.`);
+ return;
}
- else if (location.type === 'shop') {
- UI.openShop(location);
+ Memory.state.money -= price;
+ event.detail.monster.hp = event.detail.monster.stats.hp;
+ event.detail.monster.statusEffect = null;
+ event.detail.node.remove();
+
+ if (monsterSelection.children.length === 0) {
+ monsterSelectionPopup.remove();
}
+
+ UI.drawStatus();
});
- }
- },
- openHealingCenter (healingCenter) {},
+ monsterSelectionPopup.querySelector('.popup').appendChild(monsterSelection);
+ UI.drawPopup(monsterSelectionPopup);
+ });
+
+ popup.querySelector('.popup').appendChild(template);
+ UI.drawPopup(popup);
+ },
+ /**
+ * @param {Object} shop
+ */
async openShop (shop) {
const popup = UI.createPopup();
- const template = document.createElement('div');
+ const template = UI.createTemplate(Template.shop);
for (const itemData of shop.items) {
+ const price = convertToCurrencyBase(itemData.price);
const item = await fetchItem(itemData.item_name);
- const itemNode = document.createElement('div');
+ const itemNode = UI.createTemplate(Template.shopItem);
+
+ itemNode.querySelector('[data-template-slot="sprite"]').src = item.sprite;
+ itemNode.querySelector('[data-template-slot="name"]').innerHTML = item.name;
+ itemNode.querySelector('[data-template-slot="price"]').innerHTML = formatPrice(price);
+
+ itemNode.addEventListener('click', () => {
+ if (Memory.state.money < price) {
+ alert(`Not enough ${DB.currencies.map[Memory.state.Settings.currency].symbol}.`);
+ return;
+ }
+
+ Memory.state.money -= price;
- itemNode.innerHTML = `<img src="/modules/tuxemon/mods/tuxemon/${item.sprite}" />`;
- itemNode.innerHTML += `${item.name}`;
- itemNode.innerHTML += `${itemData.price} ${DB.currencies.map[Memory.state.Settings.currency].symbol}`;
+ const itemInInventory = Memory.state.player.inventory.find((inventoryItem) => inventoryItem.slug === item.slug);
+ if (itemInInventory) {
+ itemInInventory.quantity++;
+ } else {
+ Memory.state.player.inventory.push(new InventoryItem(item, 1));
+ }
+
+ UI.drawStatus();
+ });
template.appendChild(itemNode);
}
@@ -679,6 +804,7 @@ const UI = {
partySelectionMode: 'select',
inventorySelectionMode: 'use',
+ isHighlighting: false,
/**
@@ -727,6 +853,56 @@ const UI = {
return template;
},
+ /**
+ * @param {Monster[]} monsters
+ *
+ * @returns {HTMLElement}
+ */
+ createPartySelection (monsters) {
+ const party = UI.createTemplate(Template.party);
+ party.id = 'party';
+ for (const monsterIdx in monsters) {
+ const monster = monsters[monsterIdx];
+ const partyMonster = UI.createPartyMonster(monster);
+
+ partyMonster.addEventListener('click', async (event) => {
+ // bubble up to partyNode
+ let target = event.target;
+ while (target.parentNode.id !== party.id) {
+ target = target.parentNode;
+ }
+
+ party.dispatchEvent(new CustomEvent('party:monster:selected', {
+ detail: {
+ monster: monster,
+ mode: UI.partySelectionMode,
+ node: partyMonster,
+ },
+ }));
+ });
+
+ party.querySelector('[data-template-slot="monsters"]').appendChild(partyMonster);
+ }
+
+ const selectionModesNode = party.querySelector('[data-template-slot="modes"]');
+ const selectionModeNodes = selectionModesNode.querySelectorAll('[data-party-selection-mode]');
+ selectionModeNodes.forEach((node) => {
+ if (node.dataset.partySelectionMode === UI.partySelectionMode) {
+ node.setAttribute('selected', true);
+ }
+
+ node.addEventListener('click', () => {
+ selectionModesNode.querySelector(`[data-party-selection-mode="${UI.partySelectionMode}"]`).removeAttribute('selected');
+
+ UI.partySelectionMode = node.dataset.partySelectionMode;
+
+ node.setAttribute('selected', true);
+ });
+ });
+
+ return party;
+ },
+
drawStatus () {
const currentArea = Memory.state.currentArea;
@@ -739,15 +915,84 @@ const UI = {
UI.elements.status.querySelector('[data-template-slot="trainerProgress"]').textContent = `${currentArea.trainerProgress} / ${currentArea.trainers.length}`;
const nextTrainerButton = UI.elements.nextTrainer;
- if (
- Memory.state.opponent.type === 'monster' &&
- currentArea.monsterProgress >= currentArea.requiredEncounters &&
- currentArea.trainerProgress < currentArea.trainers.length
- ) {
- nextTrainerButton.disabled = false;
+ if (!Game.isTown(currentArea)) {
+ if (
+ Memory.state.opponent.type === 'monster' &&
+ currentArea.monsterProgress >= currentArea.requiredEncounters &&
+ currentArea.trainerProgress < currentArea.trainers.length &&
+ !Game.isInBattle
+ ) {
+ nextTrainerButton.disabled = false;
+ } else {
+ nextTrainerButton.disabled = true;
+ }
} else {
nextTrainerButton.disabled = true;
}
+
+ const changeAreaButton = UI.elements.changeArea;
+ if (!Game.isTown(currentArea)) {
+ if (
+ Game.isInBattle ||
+ (Memory.state.opponent && Memory.state.opponent.type === 'trainer')
+ ) {
+ changeAreaButton.disabled = true;
+ } else {
+ changeAreaButton.disabled = false;
+ }
+ } else {
+ changeAreaButton.disabled = false;
+ }
+ },
+
+ /**
+ * @param {Monster[]} monsters
+ */
+ openStarterMonsterSelection (monsters) {
+ const popup = UI.createPopup().cloneNode(true); // remove close event
+ const template = UI.createPartySelection(monsters);
+
+ const title = document.createElement('h1');
+ title.textContent = 'Select your Tuxemon!';
+ title.style.textAlign = 'center';
+ template.prepend(title);
+
+ template.addEventListener('party:monster:selected', (event) => {
+ const monster = event.detail.monster;
+
+ if (UI.partySelectionMode === 'select') {
+ template.dispatchEvent(new CustomEvent('starter:monster:selected', {
+ detail: {
+ monster: monster,
+ node: event.detail.node,
+ popup: popup,
+ },
+ }));
+ }
+ else if (UI.partySelectionMode === 'stats') {
+ UI.openStatsMenu(monster);
+ }
+ else if (UI.partySelectionMode === 'techniques') {
+ UI.openMovesetSelection(monster);
+ }
+ });
+
+ popup.querySelector('.popup').appendChild(template);
+ UI.drawPopup(popup);
+
+ return template;
+ },
+
+ openMap () {
+ if (Game.isInBattle || Game.isTown(Memory.state.currentArea)) {
+ return;
+ }
+
+ const popup = UI.createPopup();
+ const template = UI.createMap();
+
+ popup.querySelector('.popup').appendChild(template);
+ UI.drawPopup(popup);
},
openAreaSelection () {
@@ -771,6 +1016,10 @@ const UI = {
canGo = canGo && currentArea.trainerProgress >= currentArea.trainers.length;
}
+ else if (condition.startsWith('area.')) {
+ canGo = Memory.state.areaProgress.hasOwnProperty(condition.replace('area.', ''));
+ }
+
else if (condition.startsWith('event.')) {
canGo = false;
}
@@ -798,60 +1047,25 @@ const UI = {
openPartyMenu () {
const popup = UI.createPopup();
+ const template = UI.createPartySelection(Memory.state.player.monsters);
- const party = UI.createTemplate(Template.party);
- party.id = 'party';
- for (const monsterIdx in Memory.state.player.monsters) {
- const monster = Memory.state.player.monsters[monsterIdx];
- const partyMonster = UI.createPartyMonster(monster);
-
- partyMonster.addEventListener('click', async (event) => {
- // bubble up to partyNode
- let target = event.target;
- while (target.parentNode.id !== party.id) {
- target = target.parentNode;
- }
-
- if (UI.partySelectionMode === 'select') {
- Game.setActivePlayerMonster(monster);
-
- popup.remove();
- }
- else if (UI.partySelectionMode === 'stats') {
- UI.openStatsMenu(monster);
- }
- else if (UI.partySelectionMode === 'techniques') {
- UI.openMovesetSelection(monster);
- }
-
- UI.events.dispatchEvent(new CustomEvent('party:monsterSelected', {
- detail: {
- monster: monster,
- mode: UI.partySelectionMode,
- },
- }));
- });
+ template.addEventListener('party:monster:selected', (event) => {
+ const monster = event.detail.monster;
- party.querySelector('[data-template-slot="monsters"]').appendChild(partyMonster);
- }
+ if (UI.partySelectionMode === 'select') {
+ Game.setActivePlayerMonster(monster);
- const selectionModesNode = party.querySelector('[data-template-slot="modes"]');
- const selectionModeNodes = selectionModesNode.querySelectorAll('[data-party-selection-mode]');
- selectionModeNodes.forEach((node) => {
- if (node.dataset.partySelectionMode === UI.partySelectionMode) {
- node.setAttribute('selected', true);
+ popup.remove();
+ }
+ else if (UI.partySelectionMode === 'stats') {
+ UI.openStatsMenu(monster);
+ }
+ else if (UI.partySelectionMode === 'techniques') {
+ UI.openMovesetSelection(monster);
}
-
- node.addEventListener('click', () => {
- selectionModesNode.querySelector(`[data-party-selection-mode="${UI.partySelectionMode}"]`).removeAttribute('selected');
-
- UI.partySelectionMode = node.dataset.partySelectionMode;
-
- node.setAttribute('selected', true);
- });
});
- popup.querySelector('.popup').appendChild(party);
+ popup.querySelector('.popup').appendChild(template);
UI.drawPopup(popup);
},
@@ -1067,13 +1281,44 @@ const UI = {
const exchangedMoney = baseRateMoney * newCurrency.rate;
Memory.state.money = Number(exchangedMoney.toFixed(newCurrency.decimals));
- UI.drawTown();
+ UI.drawArea();
UI.drawStatus();
});
template.querySelector('[data-template-slot="currency.lastUpdated"]').textContent = DB.currencies.last_updated;
+ // Highlight
+
+ template.querySelector('[data-template-slot="highlight"]').addEventListener('click', () => {
+ UI.isHighlighting = !UI.isHighlighting;
+
+ const elements = [
+ UI.elements.battleOpponent,
+ UI.elements.battlePlayer.querySelector('[data-template-slot="sprite"]'),
+ UI.elements.techniques,
+ ...UI.elements.sceneTown.querySelectorAll('[data-location]'),
+ UI.elements.showMap,
+ UI.elements.nextTrainer,
+ UI.elements.changeArea,
+ UI.elements.menuParty,
+ UI.elements.menuCatch,
+ UI.elements.menuInventory,
+ UI.elements.menuLog,
+ UI.elements.menuJournal,
+ UI.elements.menuSettings,
+ ];
+
+ for (const element of elements) {
+ if (UI.isHighlighting) {
+ element.classList.add('setting-highlight');
+ } else {
+ element.classList.remove('setting-highlight');
+ }
+ }
+ });
+
+
popup.querySelector('.popup').appendChild(template);
UI.drawPopup(popup);
},
@@ -1093,6 +1338,7 @@ const UI = {
partyMonster.querySelector('[data-template-slot="name"]').textContent = monster.name;
partyMonster.querySelector('[data-template-slot="gender"]').innerHTML = UI.createGenderIcon(monster.gender).outerHTML;
partyMonster.querySelector('[data-template-slot="level"]').textContent = monster.level;
+ partyMonster.querySelector('[data-template-slot="statusEffect"]').innerHTML = UI.createStatusEffectIcon(monster.statusEffect).outerHTML;
partyMonster.querySelector('[data-template-slot="hpText"]').textContent = `${monster.hp} / ${monster.stats.hp}`;
return partyMonster;
@@ -1235,7 +1481,7 @@ const UI = {
inventoryItemNode.title = item.description;
inventoryItemNode.dataset.inventoryItem = item.slug;
- inventoryItemNode.querySelector('[data-template-slot="sprite"]').src = `/modules/tuxemon/mods/tuxemon/${item.sprite}`;
+ inventoryItemNode.querySelector('[data-template-slot="sprite"]').src = item.sprite;
inventoryItemNode.querySelector('[data-template-slot="name"]').textContent = item.name;
inventoryItemNode.querySelector('[data-template-slot="quantity"]').textContent = item.quantity;
@@ -1245,6 +1491,10 @@ const UI = {
UI.openItemMonsterSelection(item);
}
+ else if (item.category === 'revive') {
+ UI.openItemMonsterSelection(item);
+ }
+
else if (item.category === 'capture') {
Game.useItem(item);
}
@@ -1389,6 +1639,7 @@ const UI = {
};
// UI element click bindings
+UI.elements.showMap.addEventListener('click', UI.openMap);
UI.elements.changeArea.addEventListener('click', UI.openAreaSelection);
UI.elements.menuParty.addEventListener('click', UI.openPartyMenu);
UI.elements.menuInventory.addEventListener('click', UI.openInventoryMenu);