diff --git a/Tutorials/TurnBasedRPG/Project.xml b/Tutorials/TurnBasedRPG/Project.xml
index 381421f9f..5a54627d5 100644
--- a/Tutorials/TurnBasedRPG/Project.xml
+++ b/Tutorials/TurnBasedRPG/Project.xml
@@ -62,6 +62,7 @@
 	
 	
 	
+	
 	
 
 	
diff --git a/Tutorials/TurnBasedRPG/assets/data/room-001.json b/Tutorials/TurnBasedRPG/assets/data/room-001.json
index b99e25a37..bcd4969e2 100644
--- a/Tutorials/TurnBasedRPG/assets/data/room-001.json
+++ b/Tutorials/TurnBasedRPG/assets/data/room-001.json
@@ -1,5 +1,5 @@
 {
-  "ogmoVersion": "3.3.0",
+  "ogmoVersion": "3.4.0",
   "width": 640,
   "height": 480,
   "offsetX": 0,
@@ -16,33 +16,35 @@
       "gridCellsY": 30,
       "entities": [
         {"name": "player", "id": 0, "_eid": "40117707", "x": 128, "y": 144, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 1, "_eid": "40304284", "x": 320, "y": 96, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 2, "_eid": "40304284", "x": 352, "y": 80, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 3, "_eid": "40304284", "x": 192, "y": 144, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 4, "_eid": "40304284", "x": 224, "y": 144, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 5, "_eid": "40304284", "x": 256, "y": 144, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 6, "_eid": "40304284", "x": 288, "y": 144, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 7, "_eid": "40304284", "x": 304, "y": 160, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 8, "_eid": "40304284", "x": 304, "y": 192, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 9, "_eid": "40304284", "x": 304, "y": 224, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 10, "_eid": "40304284", "x": 304, "y": 256, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 11, "_eid": "40304284", "x": 304, "y": 304, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 12, "_eid": "40304284", "x": 320, "y": 288, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 13, "_eid": "40304284", "x": 336, "y": 304, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 14, "_eid": "40304284", "x": 288, "y": 288, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 15, "_eid": "40304284", "x": 272, "y": 304, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 16, "_eid": "40304284", "x": 256, "y": 224, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 17, "_eid": "40304284", "x": 256, "y": 192, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 18, "_eid": "40304284", "x": 208, "y": 192, "originX": 0, "originY": 0},
-        {"name": "coin", "id": 19, "_eid": "40304284", "x": 208, "y": 224, "originX": 0, "originY": 0},
-        {"name": "enemy", "id": 20, "_eid": "40444291", "x": 304, "y": 112, "originX": 0, "originY": 0},
-        {"name": "enemy", "id": 21, "_eid": "40444291", "x": 272, "y": 208, "originX": 0, "originY": 0},
-        {"name": "boss", "id": 22, "_eid": "40444462", "x": 304, "y": 288, "originX": 0, "originY": 0}
+        {"name": "coin", "id": 1, "_eid": "40304284", "x": 416, "y": 48, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 2, "_eid": "40304284", "x": 496, "y": 48, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 3, "_eid": "40304284", "x": 192, "y": 136, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 4, "_eid": "40304284", "x": 240, "y": 136, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 5, "_eid": "40304284", "x": 288, "y": 136, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 6, "_eid": "40304284", "x": 336, "y": 136, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 7, "_eid": "40304284", "x": 392, "y": 176, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 8, "_eid": "40304284", "x": 392, "y": 224, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 9, "_eid": "40304284", "x": 392, "y": 272, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 10, "_eid": "40304284", "x": 392, "y": 320, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 11, "_eid": "40304284", "x": 336, "y": 400, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 12, "_eid": "40304284", "x": 368, "y": 368, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 13, "_eid": "40304284", "x": 400, "y": 400, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 14, "_eid": "40304284", "x": 304, "y": 368, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 15, "_eid": "40304284", "x": 272, "y": 400, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 16, "_eid": "40304284", "x": 304, "y": 256, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 17, "_eid": "40304284", "x": 304, "y": 224, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 18, "_eid": "40304284", "x": 240, "y": 224, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 19, "_eid": "40304284", "x": 240, "y": 256, "originX": 0, "originY": 0},
+        {"name": "enemy", "id": 20, "_eid": "40444291", "x": 464, "y": 64, "originX": 0, "originY": 0},
+        {"name": "enemy", "id": 21, "_eid": "40444291", "x": 272, "y": 240, "originX": 0, "originY": 0},
+        {"name": "boss", "id": 22, "_eid": "40444462", "x": 336, "y": 384, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 23, "_eid": "40304284", "x": 496, "y": 80, "originX": 0, "originY": 0},
+        {"name": "coin", "id": 24, "_eid": "40304284", "x": 416, "y": 80, "originX": 0, "originY": 0}
       ]
     },
     {
       "name": "walls",
-      "_eid": "40116503",
+      "_eid": "02788814",
       "offsetX": 0,
       "offsetY": 0,
       "gridCellWidth": 16,
@@ -50,7 +52,7 @@
       "gridCellsX": 40,
       "gridCellsY": 30,
       "tileset": "tiles",
-      "data": [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 1, 1, 1, 1, 1, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
+      "data": [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 16, 16, 16, 16, 16, 21, 28, 28, 22, 28, 22, 22, 28, 22, 22, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 4, 9, 9, 9, 9, 9, 9, 9, 5, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 16, 16, 21, 7, 3, 1, 1, 1, 2, 1, 1, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, 18, 19, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 3, 1, 1, 1, 1, 1, 1, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 28, 28, 28, 28, 22, 22, 28, 23, 16, 16, 16, 20, 16, 16, 16, 16, 16, 16, 24, 21, 7, 1, 1, 1, 2, 1, 1, 1, 2, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 4, 5, 9, 9, 9, 9, 9, 34, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 35, 7, 1, 1, 1, 1, 3, 1, 1, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 1, 1, 1, 32, 28, 28, 28, 22, 28, 28, 28, 28, 22, 28, 28, 33, 7, 1, 30, 26, 26, 26, 26, 26, 26, 27, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 1, 1, 1, 9, 9, 5, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 8, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 12, 13, 13, 13, 13, 13, 14, 30, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 10, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 2, 1, 1, 23, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 21, 11, 1, 23, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 2, 1, 1, 23, 21, 28, 28, 28, 28, 28, 28, 28, 22, 22, 34, 35, 7, 1, 23, 16, 16, 16, 16, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 3, 1, 1, 1, 1, 1, 23, 21, 4, 9, 9, 9, 9, 9, 9, 9, 9, 32, 33, 7, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 25, 26, 26, 26, 26, 26, 26, 26, 27, 21, 7, 1, 1, 1, 2, 1, 1, 1, 1, 9, 9, 8, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 21, 7, 2, 1, 1, 1, 1, 1, 1, 1, 30, 31, 10, 1, 23, 16, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 16, 16, 16, 21, 7, 1, 1, 1, 1, 1, 1, 2, 1, 23, 21, 12, 14, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 21, 12, 14, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 17, 18, 18, 18, 18, 18, 18, 18, 35, 12, 14, 34, 19, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 22, 22, 22, 22, 29, 28, 28, 33, 12, 14, 32, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 21, 22, 22, 29, 28, 28, 28, 28, 33, 12, 14, 32, 23, 16, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 4, 9, 5, 9, 9, 9, 9, 9, 8, 1, 9, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 23, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 20, 16, 16, 16, 16, 16, 16, 21, 11, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 21, 7, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 23, 20, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 16, 21, 7, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
       "exportMode": 0,
       "arrayMode": 0
     }
diff --git a/Tutorials/TurnBasedRPG/assets/data/turnBasedRPG.ogmo b/Tutorials/TurnBasedRPG/assets/data/turnBasedRPG.ogmo
index ee52ccbe6..b01766817 100644
--- a/Tutorials/TurnBasedRPG/assets/data/turnBasedRPG.ogmo
+++ b/Tutorials/TurnBasedRPG/assets/data/turnBasedRPG.ogmo
@@ -1,6 +1,6 @@
 {
   "name": "HaxeFlixel Tutorial",
-  "ogmoVersion": "3.3.0",
+  "ogmoVersion": "3.4.0",
   "levelPaths": ["."],
   "backgroundColor": "#282c34ff",
   "gridColor": "#3c4049cc",
@@ -29,7 +29,7 @@
       "definition": "tile",
       "name": "walls",
       "gridSize": {"x": 16, "y": 16},
-      "exportID": "40116503",
+      "exportID": "02788814",
       "exportMode": 0,
       "arrayMode": 0,
       "defaultTileset": "tiles"
@@ -54,7 +54,7 @@
           {"x": 1, "y": 1}
         ]
       },
-      "color": "#ff0000ff",
+      "color": "#00e3ffff",
       "tileX": false,
       "tileY": false,
       "tileSize": {"x": 16, "y": 16},
@@ -162,7 +162,7 @@
           {"x": 1, "y": 1}
         ]
       },
-      "color": "#00e3ffff",
+      "color": "#ff0000ff",
       "tileX": false,
       "tileY": false,
       "tileSize": {"x": 16, "y": 16},
@@ -182,6 +182,6 @@
     }
   ],
   "tilesets": [
-    {"label": "tiles", "path": "../images/tiles.png", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODRBQjNDODZENDhCMTFFMzk5N0RDMzAxQTc5NEM5RkMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODRBQjNDODdENDhCMTFFMzk5N0RDMzAxQTc5NEM5RkMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4NEFCM0M4NEQ0OEIxMUUzOTk3REMzMDFBNzk0QzlGQyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4NEFCM0M4NUQ0OEIxMUUzOTk3REMzMDFBNzk0QzlGQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrOLMN8AAACUSURBVHja7JWhDoAgEEBPRrERbVYCX+If8Z8Eqo2IyYi7wOYYTncYuI3XD94bDKaUErSwWZtWY0izu3NAnc3zEhpBAVyIQvAeWuKPEEAAQ7I8IjjLswso5RHZq2iNUp7lFRoBI2AEQN+vEL5A+EM/MSvV7wm8ySNnjH0GfJGvRQhu8mWE/GPz2g/5hUVrkvw94hJgALO2Vr1s43dIAAAAAElFTkSuQmCC", "tileWidth": 16, "tileHeight": 16, "tileSeparationX": 0, "tileSeparationY": 0}
+    {"label": "tiles", "path": "../images/tiles.png", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAACQCAMAAACPiZ3DAAAABGdBTUEAALGOfPtRkwAAAFRQTFRFAAAAqrfM/udh/q40/smc5O35af/UdeP/0XbQAJnbWmmIm0yj/3BtJZVq4Zpl6EU3Q+Gz////98KCz4JUvWxKUmB8wMvcJitEdjs26qVsi5u0PyYxhk+Z/gAAABx0Uk5TAP///////////////////////////////////6l3rVEAAAMZSURBVGiB7ZdbcusgDED90R0QkwTvf5+XNxKSLMBJm2mvpmMHDAch9KDb1slzUvr5bwB8fb0CYFCXtTY+wmtpC7MAvLwxKxoYA3+a0FwGgH0s2iDPNCYqsg7Isg4ID6/CFMBcBRANprZQPTpbz6STmdOACYuFLXQAo0i/gDHYqX4bIPeh1jiAGjjmolFA6zH1IQIMBaCVzRNkoRVA3sAMgJk/p8GPA07mvweAvn8eoA+mHviZADJABYBLB1vOxfgYBJwK2cIKIBCKDsuAqxq0hLMGaDr82BbGAayjzdiAc3XYXgRMGFHV4H63bljs/Y7G+3YBIEqPtGACA3Dh1hUfRVADtl0AgK8uAvYJCQDcfpkN5gC2tTe0J02KDVxrf7YNqG8ggFVtQH0D+YH7PBsM2OIkFpgIYGzxllh4XJT/gL8COIpMAw5RxgB1eHZ8nnAKcCiWWnkZBlgeYCcBew7+fQUQJxZAySLfCjiOPqWkXgZAfSUNTYsXcU4CMC4yC3jcbg8R0MwgA25eGMCQDXyHCLBg+eQDBHAkgLgFfIwLRlQBaaJ0jLoNxGwBATkuBUCyXviTAa74wymA00D3gwI43cIFG4z4gZBx5/zgfYDns6bi+A/GKcDfhgiACgG0EPA24gBdXeCjUdBgJK2XRf3qNJhm6oIHoB3UykTqAqlMaZ7vJYDej7PwACrHzgP2wershb9toiFbsL2t1r9JqLg092GLp08MCCVk2wy40QVGAaJUwONBUwolsYBqAw0gaSDfwAYB12Tjj7pJnw/69gbznQPpu6TzNp6VBsBeC+8D5J6I2gIA1oE+nOHbAzQbqIC281oAuzrQ5wPYTluQ9Y+xz/en9ysAug3OvxI/yMdXzZHTeBzV/C800hdyjOX82zECQAjHkh4kQP/uC0lLDxmg26ACunyQAbofdBr0AP0Y3w0Ir91xuw++SquxBMD5ILYnAUj79GMU4HYJsLtxAGuDQUCJ8OwHwBEmbED1/26A5geweuEL34AfxNTOyqAflCxM60JMabofKGld9wMVoPmBUhf0Y1TS+lXAP2vxLx2XEutMAAAAAElFTkSuQmCC", "tileWidth": 16, "tileHeight": 16, "tileSeparationX": 0, "tileSeparationY": 0, "tileMarginX": 0, "tileMarginY": 0}
   ]
 }
\ No newline at end of file
diff --git a/Tutorials/TurnBasedRPG/assets/images/bar_empty.png b/Tutorials/TurnBasedRPG/assets/images/bar_empty.png
new file mode 100644
index 000000000..baec0777c
Binary files /dev/null and b/Tutorials/TurnBasedRPG/assets/images/bar_empty.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/bar_filled.png b/Tutorials/TurnBasedRPG/assets/images/bar_filled.png
new file mode 100644
index 000000000..20ca1b36b
Binary files /dev/null and b/Tutorials/TurnBasedRPG/assets/images/bar_filled.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/boss.png b/Tutorials/TurnBasedRPG/assets/images/boss.png
index 5e346d49f..5e529eeee 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/boss.png and b/Tutorials/TurnBasedRPG/assets/images/boss.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/button.png b/Tutorials/TurnBasedRPG/assets/images/button.png
index 32f436866..61d6e6de2 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/button.png and b/Tutorials/TurnBasedRPG/assets/images/button.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/coin.png b/Tutorials/TurnBasedRPG/assets/images/coin.png
index 2a5820aa8..5ea4008ab 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/coin.png and b/Tutorials/TurnBasedRPG/assets/images/coin.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/enemy.png b/Tutorials/TurnBasedRPG/assets/images/enemy.png
index 7672375f2..07544008f 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/enemy.png and b/Tutorials/TurnBasedRPG/assets/images/enemy.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/font.png b/Tutorials/TurnBasedRPG/assets/images/font.png
new file mode 100644
index 000000000..84e2038b8
Binary files /dev/null and b/Tutorials/TurnBasedRPG/assets/images/font.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/health.png b/Tutorials/TurnBasedRPG/assets/images/health.png
index d39bd4ae8..7a549c929 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/health.png and b/Tutorials/TurnBasedRPG/assets/images/health.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/player.png b/Tutorials/TurnBasedRPG/assets/images/player.png
index 6bb1e9add..e786ae632 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/player.png and b/Tutorials/TurnBasedRPG/assets/images/player.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/pointer.png b/Tutorials/TurnBasedRPG/assets/images/pointer.png
index 7e0cc1ca4..38d46edf4 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/pointer.png and b/Tutorials/TurnBasedRPG/assets/images/pointer.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/small_button.png b/Tutorials/TurnBasedRPG/assets/images/small_button.png
new file mode 100644
index 000000000..bd39131e9
Binary files /dev/null and b/Tutorials/TurnBasedRPG/assets/images/small_button.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/tiles.png b/Tutorials/TurnBasedRPG/assets/images/tiles.png
index f16b622f5..429add584 100644
Binary files a/Tutorials/TurnBasedRPG/assets/images/tiles.png and b/Tutorials/TurnBasedRPG/assets/images/tiles.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/ui_section.png b/Tutorials/TurnBasedRPG/assets/images/ui_section.png
new file mode 100644
index 000000000..d69ffec7d
Binary files /dev/null and b/Tutorials/TurnBasedRPG/assets/images/ui_section.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/images/uiback.png b/Tutorials/TurnBasedRPG/assets/images/uiback.png
new file mode 100644
index 000000000..864106cf0
Binary files /dev/null and b/Tutorials/TurnBasedRPG/assets/images/uiback.png differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/coin.wav b/Tutorials/TurnBasedRPG/assets/sounds/coin.wav
index 5396aeeec..b92bdabdf 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/coin.wav and b/Tutorials/TurnBasedRPG/assets/sounds/coin.wav differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/combat.wav b/Tutorials/TurnBasedRPG/assets/sounds/combat.wav
index ec0cc74e4..135ff3d8a 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/combat.wav and b/Tutorials/TurnBasedRPG/assets/sounds/combat.wav differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/fled.wav b/Tutorials/TurnBasedRPG/assets/sounds/fled.wav
index ccd9497a3..862d8d3fe 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/fled.wav and b/Tutorials/TurnBasedRPG/assets/sounds/fled.wav differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/hurt.wav b/Tutorials/TurnBasedRPG/assets/sounds/hurt.wav
index 54491328d..5c09f1165 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/hurt.wav and b/Tutorials/TurnBasedRPG/assets/sounds/hurt.wav differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/lose.wav b/Tutorials/TurnBasedRPG/assets/sounds/lose.wav
index c543d5ec9..9d83705e5 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/lose.wav and b/Tutorials/TurnBasedRPG/assets/sounds/lose.wav differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/miss.wav b/Tutorials/TurnBasedRPG/assets/sounds/miss.wav
index 55a36b623..95d0a2da1 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/miss.wav and b/Tutorials/TurnBasedRPG/assets/sounds/miss.wav differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/select.wav b/Tutorials/TurnBasedRPG/assets/sounds/select.wav
index 6b7a9c9d7..73ec4f5f4 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/select.wav and b/Tutorials/TurnBasedRPG/assets/sounds/select.wav differ
diff --git a/Tutorials/TurnBasedRPG/assets/sounds/win.wav b/Tutorials/TurnBasedRPG/assets/sounds/win.wav
index a58220ca7..f62b62a77 100644
Binary files a/Tutorials/TurnBasedRPG/assets/sounds/win.wav and b/Tutorials/TurnBasedRPG/assets/sounds/win.wav differ
diff --git a/Tutorials/TurnBasedRPG/source/CombatHUD.hx b/Tutorials/TurnBasedRPG/source/CombatHUD.hx
deleted file mode 100644
index 07eb8b5a2..000000000
--- a/Tutorials/TurnBasedRPG/source/CombatHUD.hx
+++ /dev/null
@@ -1,507 +0,0 @@
-package;
-
-import flash.filters.ColorMatrixFilter;
-import flash.geom.Matrix;
-import flash.geom.Point;
-import flixel.FlxG;
-import flixel.FlxSprite;
-import flixel.addons.effects.chainable.FlxEffectSprite;
-import flixel.addons.effects.chainable.FlxWaveEffect;
-import flixel.group.FlxGroup.FlxTypedGroup;
-import flixel.sound.FlxSound;
-import flixel.text.FlxText;
-import flixel.tweens.FlxEase;
-import flixel.tweens.FlxTween;
-import flixel.ui.FlxBar;
-import flixel.util.FlxColor;
-
-using flixel.util.FlxSpriteUtil;
-
-/**
- * This enum is used to set the valid values for our outcome variable.
- * Outcome can only ever be one of these 4 values and we can check for these values easily once combat is concluded.
- */
-enum Outcome
-{
-	NONE;
-	ESCAPE;
-	VICTORY;
-	DEFEAT;
-}
-
-enum Choice
-{
-	FIGHT;
-	FLEE;
-}
-
-class CombatHUD extends FlxTypedGroup
-{
-	// These public variables will be used after combat has finished to help tell us what happened.
-	public var enemy:Enemy; // we will pass the enemySprite that the playerSprite touched to initialize combat, and this will let us also know which enemySprite to kill, etc.
-	public var playerHealth(default, null):Int; // when combat has finished, we will need to know how much remaining health the playerSprite has
-	public var outcome(default, null):Outcome; // when combat has finished, we will need to know if the playerSprite killed the enemySprite or fled
-
-	// These are the sprites that we will use to show the combat hud interface
-	var background:FlxSprite; // this is the background sprite
-	var playerSprite:Player; // this is a sprite of the playerSprite
-	var enemySprite:Enemy; // this is a sprite of the enemySprite
-
-	// These variables will be used to track the enemySprite's health
-	var enemyHealth:Int;
-	var enemyMaxHealth:Int;
-	var enemyHealthBar:FlxBar; // This FlxBar will show us the enemySprite's current/max health
-
-	var playerHealthCounter:FlxText; // this will show the playerSprite's current/max health
-
-	var damages:Array; // This array will contain 2 FlxText objects which will appear to show damage dealt (or misses)
-
-	var pointer:FlxSprite; // This will be the pointer to show which option (Fight or Flee) the user is pointing to.
-	var selected:Choice; // this will track which option is selected
-	var choices:Map; // this map will contain the FlxTexts for our 2 options: Fight and Flee
-
-	var results:FlxText; // this text will show the outcome of the battle for the playerSprite.
-
-	var alpha:Float = 0; // we will use this to fade in and out our combat hud
-	var wait:Bool = true; // this flag will be set to true when don't want the playerSprite to be able to do anything (between turns)
-
-	var fledSound:FlxSound;
-	var hurtSound:FlxSound;
-	var loseSound:FlxSound;
-	var missSound:FlxSound;
-	var selectSound:FlxSound;
-	var winSound:FlxSound;
-	var combatSound:FlxSound;
-
-	var screen:FlxSprite;
-
-	public function new()
-	{
-		super();
-
-		screen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT);
-		var waveEffect = new FlxWaveEffect(FlxWaveMode.ALL, 4, -1, 4);
-		var waveSprite = new FlxEffectSprite(screen, [waveEffect]);
-		add(waveSprite);
-
-		// first, create our background. Make a black square, then draw borders onto it in white. Add it to our group.
-		background = new FlxSprite().makeGraphic(120, 120, FlxColor.WHITE);
-		background.drawRect(1, 1, 118, 44, FlxColor.BLACK);
-		background.drawRect(1, 46, 118, 73, FlxColor.BLACK);
-		background.screenCenter();
-		add(background);
-
-		// next, make a 'dummy' playerSprite that looks like our playerSprite (but can't move) and add it.
-		playerSprite = new Player(background.x + 36, background.y + 16);
-		playerSprite.animation.frameIndex = 3;
-		playerSprite.active = false;
-		playerSprite.facing = RIGHT;
-		add(playerSprite);
-
-		// do the same thing for an enemySprite. We'll just use enemySprite type REGULAR for now and change it later.
-		enemySprite = new Enemy(background.x + 76, background.y + 16, REGULAR);
-		enemySprite.animation.frameIndex = 3;
-		enemySprite.active = false;
-		enemySprite.facing = LEFT;
-		add(enemySprite);
-
-		// setup the playerSprite's health display and add it to the group.
-		playerHealthCounter = new FlxText(0, playerSprite.y + playerSprite.height + 2, 0, "3 / 3", 8);
-		playerHealthCounter.alignment = CENTER;
-		playerHealthCounter.x = playerSprite.x + 4 - (playerHealthCounter.width / 2);
-		add(playerHealthCounter);
-
-		// create and add a FlxBar to show the enemySprite's health. We'll make it Red and Yellow.
-		enemyHealthBar = new FlxBar(enemySprite.x - 6, playerHealthCounter.y, LEFT_TO_RIGHT, 20, 10);
-		enemyHealthBar.createFilledBar(0xffdc143c, FlxColor.YELLOW, true, FlxColor.YELLOW);
-		add(enemyHealthBar);
-
-		// create our choices and add them to the group.
-		choices = new Map();
-		choices[FIGHT] = new FlxText(background.x + 30, background.y + 48, 85, "FIGHT", 22);
-		choices[FLEE] = new FlxText(background.x + 30, choices[FIGHT].y + choices[FIGHT].height + 8, 85, "FLEE", 22);
-		add(choices[FIGHT]);
-		add(choices[FLEE]);
-
-		pointer = new FlxSprite(background.x + 10, choices[FIGHT].y + (choices[FIGHT].height / 2) - 8, AssetPaths.pointer__png);
-		pointer.visible = false;
-		add(pointer);
-
-		// create our damage texts. We'll make them be white text with a red shadow (so they stand out).
-		damages = new Array();
-		damages.push(new FlxText(0, 0, 40));
-		damages.push(new FlxText(0, 0, 40));
-		for (d in damages)
-		{
-			d.color = FlxColor.WHITE;
-			d.setBorderStyle(SHADOW, FlxColor.RED);
-			d.alignment = CENTER;
-			d.visible = false;
-			add(d);
-		}
-
-		// create our results text object. We'll position it, but make it hidden for now.
-		results = new FlxText(background.x + 2, background.y + 9, 116, "", 18);
-		results.alignment = CENTER;
-		results.color = FlxColor.YELLOW;
-		results.setBorderStyle(SHADOW, FlxColor.GRAY);
-		results.visible = false;
-		add(results);
-
-		// like we did in our HUD class, we need to set the scrollFactor on each of our children objects to 0,0. We also set alpha to 0 (so we can fade this in)
-		forEach(function(sprite:FlxSprite)
-		{
-			sprite.scrollFactor.set();
-			sprite.alpha = 0;
-		});
-
-		// mark this object as not active and not visible so update and draw don't get called on it until we're ready to show it.
-		active = false;
-		visible = false;
-
-		fledSound = FlxG.sound.load(AssetPaths.fled__wav);
-		hurtSound = FlxG.sound.load(AssetPaths.hurt__wav);
-		loseSound = FlxG.sound.load(AssetPaths.lose__wav);
-		missSound = FlxG.sound.load(AssetPaths.miss__wav);
-		selectSound = FlxG.sound.load(AssetPaths.select__wav);
-		winSound = FlxG.sound.load(AssetPaths.win__wav);
-		combatSound = FlxG.sound.load(AssetPaths.combat__wav);
-	}
-
-	/**
-	 * This function will be called from PlayState when we want to start combat. It will setup the screen and make sure everything is ready.
-	 * @param	playerHealth	The amount of health the playerSprite is starting with
-	 * @param	enemy			This links back to the Enemy we are fighting with so we can get it's health and type (to change our sprite).
-	 */
-	public function initCombat(playerHealth:Int, enemy:Enemy)
-	{
-		screen.drawFrame();
-		var screenPixels = screen.framePixels;
-
-		if (FlxG.renderBlit)
-			screenPixels.copyPixels(FlxG.camera.buffer, FlxG.camera.buffer.rect, new Point());
-		else
-			screenPixels.draw(FlxG.camera.canvas, new Matrix(1, 0, 0, 1, 0, 0));
-
-		var rc:Float = 1 / 3;
-		var gc:Float = 1 / 2;
-		var bc:Float = 1 / 6;
-		screenPixels.applyFilter(screenPixels, screenPixels.rect, new Point(),
-			new ColorMatrixFilter([rc, gc, bc, 0, 0, rc, gc, bc, 0, 0, rc, gc, bc, 0, 0, 0, 0, 0, 1, 0]));
-
-		combatSound.play();
-		this.playerHealth = playerHealth; // we set our playerHealth variable to the value that was passed to us
-		this.enemy = enemy; // set our enemySprite object to the one passed to us
-
-		updatePlayerHealth();
-
-		// setup our enemySprite
-		enemyMaxHealth = enemyHealth = if (enemy.type == REGULAR) 2 else 4; // each enemySprite will have health based on their type
-		enemyHealthBar.value = 100; // the enemySprite's health bar starts at 100%
-		enemySprite.changeType(enemy.type); // change our enemySprite's image to match their type.
-
-		// make sure we initialize all of these before we start so nothing looks 'wrong' the second time we get
-		wait = true;
-		results.text = "";
-		pointer.visible = false;
-		results.visible = false;
-		outcome = NONE;
-		selected = FIGHT;
-		movePointer();
-
-		visible = true; // make our hud visible (so draw gets called on it) - note, it's not active, yet!
-
-		// do a numeric tween to fade in our combat hud when the tween is finished, call finishFadeIn
-		FlxTween.num(0, 1, .66, {ease: FlxEase.circOut, onComplete: finishFadeIn}, updateAlpha);
-	}
-
-	/**
-	 * This function is called by our Tween to fade in/out all the items in our hud.
-	 */
-	function updateAlpha(alpha:Float)
-	{
-		this.alpha = alpha;
-		forEach(function(sprite) sprite.alpha = alpha);
-	}
-
-	/**
-	 * When we've finished fading in, we set our hud to active (so it gets updates), and allow the playerSprite to interact. We show our pointer, too.
-	 */
-	function finishFadeIn(_)
-	{
-		active = true;
-		wait = false;
-		pointer.visible = true;
-		selectSound.play();
-	}
-
-	/**
-	 * After we fade our hud out, we set it to not be active or visible (no update and no draw)
-	 */
-	function finishFadeOut(_)
-	{
-		active = false;
-		visible = false;
-	}
-
-	/**
-	 * This function is called to change the Player's health text on the screen.
-	 */
-	function updatePlayerHealth()
-	{
-		playerHealthCounter.text = playerHealth + " / 3";
-		playerHealthCounter.x = playerSprite.x + 4 - (playerHealthCounter.width / 2);
-	}
-
-	override public function update(elapsed:Float)
-	{
-		if (!wait) // if we're waiting, don't do any of this.
-		{
-			updateKeyboardInput();
-			updateTouchInput();
-		}
-		super.update(elapsed);
-	}
-
-	function updateKeyboardInput()
-	{
-		#if FLX_KEYBOARD
-		// setup some simple flags to see which keys are pressed.
-		var up:Bool = false;
-		var down:Bool = false;
-		var fire:Bool = false;
-
-		// check to see any keys are pressed and set the cooresponding flags.
-		if (FlxG.keys.anyJustReleased([SPACE, X, ENTER]))
-		{
-			fire = true;
-		}
-		else if (FlxG.keys.anyJustReleased([W, UP]))
-		{
-			up = true;
-		}
-		else if (FlxG.keys.anyJustReleased([S, DOWN]))
-		{
-			down = true;
-		}
-
-		// based on which flags are set, do the specified action
-		if (fire)
-		{
-			selectSound.play();
-			makeChoice(); // when the playerSprite chooses either option, we call this function to process their selection
-		}
-		else if (up || down)
-		{
-			// if the playerSprite presses up or down, we move the cursor up or down (with wrapping)
-			selected = if (selected == FIGHT) FLEE else FIGHT;
-			selectSound.play();
-			movePointer();
-		}
-		#end
-	}
-
-	function updateTouchInput()
-	{
-		#if FLX_TOUCH
-		for (touch in FlxG.touches.justReleased())
-		{
-			for (choice in choices.keys())
-			{
-				var text = choices[choice];
-				if (touch.overlaps(text))
-				{
-					selectSound.play();
-					selected = choice;
-					movePointer();
-					makeChoice();
-					return;
-				}
-			}
-		}
-		#end
-	}
-
-	/**
-	 * Call this function to place the pointer next to the currently selected choice
-	 */
-	function movePointer()
-	{
-		pointer.y = choices[selected].y + (choices[selected].height / 2) - 8;
-	}
-
-	/**
-	 * This function will process the choice the playerSprite picked
-	 */
-	function makeChoice()
-	{
-		pointer.visible = false; // hide our pointer
-		switch (selected) // check which item was selected when the playerSprite picked it
-		{
-			case FIGHT:
-				// if FIGHT was picked...
-				// ...the playerSprite attacks the enemySprite first
-				// they have an 85% chance to hit the enemySprite
-				if (FlxG.random.bool(85))
-				{
-					// if they hit, deal 1 damage to the enemySprite, and setup our damage indicator
-					damages[1].text = "1";
-					FlxTween.tween(enemySprite, {x: enemySprite.x + 4}, 0.1, {
-						onComplete: function(_)
-						{
-							FlxTween.tween(enemySprite, {x: enemySprite.x - 4}, 0.1);
-						}
-					});
-					hurtSound.play();
-					enemyHealth--;
-					enemyHealthBar.value = (enemyHealth / enemyMaxHealth) * 100; // change the enemySprite's health bar
-				}
-				else
-				{
-					// change our damage text to show that we missed!
-					damages[1].text = "MISS!";
-					missSound.play();
-				}
-
-				// position the damage text over the enemySprite, and set it's alpha to 0 but it's visible to true (so that it gets draw called on it)
-				damages[1].x = enemySprite.x + 2 - (damages[1].width / 2);
-				damages[1].y = enemySprite.y + 4 - (damages[1].height / 2);
-				damages[1].alpha = 0;
-				damages[1].visible = true;
-
-				// if the enemySprite is still alive, it will swing back!
-				if (enemyHealth > 0)
-				{
-					enemyAttack();
-				}
-
-				// setup 2 tweens to allow the damage indicators to fade in and float up from the sprites
-				FlxTween.num(damages[0].y, damages[0].y - 12, 1, {ease: FlxEase.circOut}, updateDamageY);
-				FlxTween.num(0, 1, .2, {ease: FlxEase.circInOut, onComplete: doneDamageIn}, updateDamageAlpha);
-
-			case FLEE:
-				// if the playerSprite chose to FLEE, we'll give them a 50/50 chance to escape
-				if (FlxG.random.bool(50))
-				{
-					// if they succeed, we show the 'escaped' message and trigger it to fade in
-					outcome = ESCAPE;
-					results.text = "ESCAPED!";
-					fledSound.play();
-					results.visible = true;
-					results.alpha = 0;
-					FlxTween.tween(results, {alpha: 1}, .66, {ease: FlxEase.circInOut, onComplete: doneResultsIn});
-				}
-				else
-				{
-					// if they fail to escape, the enemySprite will get a free-swing
-					enemyAttack();
-					FlxTween.num(damages[0].y, damages[0].y - 12, 1, {ease: FlxEase.circOut}, updateDamageY);
-					FlxTween.num(0, 1, .2, {ease: FlxEase.circInOut, onComplete: doneDamageIn}, updateDamageAlpha);
-				}
-		}
-
-		// regardless of what happens, we need to set our 'wait' flag so that we can show what happened before moving on
-		wait = true;
-	}
-
-	/**
-	 * This function is called anytime we want the enemySprite to swing at the playerSprite
-	 */
-	function enemyAttack()
-	{
-		// first, lets see if the enemySprite hits or not. We'll give him a 30% chance to hit
-		if (FlxG.random.bool(30))
-		{
-			// if we hit, flash the screen white, and deal one damage to the playerSprite - then update the playerSprite's health
-			FlxG.camera.flash(FlxColor.WHITE, .2);
-			FlxG.camera.shake(0.01, 0.2);
-			hurtSound.play();
-			damages[0].text = "1";
-			playerHealth--;
-			updatePlayerHealth();
-		}
-		else
-		{
-			// if the enemySprite misses, show it on the screen
-			damages[0].text = "MISS!";
-			missSound.play();
-		}
-
-		// setup the combat text to show up over the playerSprite and fade in/raise up
-		damages[0].x = playerSprite.x + 2 - (damages[0].width / 2);
-		damages[0].y = playerSprite.y + 4 - (damages[0].height / 2);
-		damages[0].alpha = 0;
-		damages[0].visible = true;
-	}
-
-	/**
-	 * This function is called from our Tweens to move the damage displays up on the screen
-	 */
-	function updateDamageY(damageY:Float)
-	{
-		damages[0].y = damages[1].y = damageY;
-	}
-
-	/**
-	 * This function is called from our Tweens to fade in/out the damage text
-	 */
-	function updateDamageAlpha(damageAlpha:Float)
-	{
-		damages[0].alpha = damages[1].alpha = damageAlpha;
-	}
-
-	/**
-	 * This function is called when our damage texts have finished fading in - it will trigger them to start fading out again, after a short delay
-	 */
-	function doneDamageIn(_)
-	{
-		FlxTween.num(1, 0, .66, {ease: FlxEase.circInOut, startDelay: 1, onComplete: doneDamageOut}, updateDamageAlpha);
-	}
-
-	/**
-	 * This function is triggered when our results text has finished fading in. If we're not defeated, we will fade out the entire hud after a short delay
-	 */
-	function doneResultsIn(_)
-	{
-		FlxTween.num(1, 0, .66, {ease: FlxEase.circOut, onComplete: finishFadeOut, startDelay: 1}, updateAlpha);
-	}
-
-	/**
-	 * This function is triggered when the damage texts have finished fading out again. They will clear and reset them for next time.
-	 * It will also check to see what we're supposed to do next - if the enemySprite is dead, we trigger victory, if the playerSprite is dead we trigger defeat, otherwise we reset for the next round.
-	 */
-	function doneDamageOut(_)
-	{
-		damages[0].visible = false;
-		damages[1].visible = false;
-		damages[0].text = "";
-		damages[1].text = "";
-
-		if (playerHealth <= 0)
-		{
-			// if the playerSprite's health is 0, we show the defeat message on the screen and fade it in
-			outcome = DEFEAT;
-			loseSound.play();
-			results.text = "DEFEAT!";
-			results.visible = true;
-			results.alpha = 0;
-			FlxTween.tween(results, {alpha: 1}, 0.66, {ease: FlxEase.circInOut, onComplete: doneResultsIn});
-		}
-		else if (enemyHealth <= 0)
-		{
-			// if the enemySprite's health is 0, we show the victory message
-			outcome = VICTORY;
-			winSound.play();
-			results.text = "VICTORY!";
-			results.visible = true;
-			results.alpha = 0;
-			FlxTween.tween(results, {alpha: 1}, 0.66, {ease: FlxEase.circInOut, onComplete: doneResultsIn});
-		}
-		else
-		{
-			// both are still alive, so we reset and have the playerSprite pick their next action
-			wait = false;
-			pointer.visible = true;
-		}
-	}
-}
diff --git a/Tutorials/TurnBasedRPG/source/GameOverState.hx b/Tutorials/TurnBasedRPG/source/GameOverState.hx
deleted file mode 100644
index f824af731..000000000
--- a/Tutorials/TurnBasedRPG/source/GameOverState.hx
+++ /dev/null
@@ -1,100 +0,0 @@
-package;
-
-import flixel.FlxG;
-import flixel.FlxSprite;
-import flixel.FlxState;
-import flixel.text.FlxText;
-import flixel.ui.FlxButton;
-import flixel.util.FlxAxes;
-import flixel.util.FlxColor;
-
-class GameOverState extends FlxState
-{
-	var titleText:FlxText; // the title text
-	var messageText:FlxText; // the final score message text
-	var scoreIcon:FlxSprite; // sprite for a coin icon
-	var scoreText:FlxText; // text of the score
-	var highscoreText:FlxText; // text to show the highscore
-	var mainMenuButton:FlxButton; // button to go to main menu
-
-	/**
-	 * Called from PlayState, this will set our win and score variables
-	 * @param	win		true if the player beat the boss, false if they died
-	 * @param	score	the number of coins collected
-	 */
-	public function new(win:Bool, score:Int)
-	{
-		super();
-
-		#if FLX_MOUSE
-		FlxG.mouse.visible = true;
-		#end
-
-		// create and add each of our items
-
-		titleText = new FlxText(0, 20, 0, if (win) "You Win!" else "Game Over!", 22);
-		titleText.alignment = CENTER;
-		titleText.screenCenter(FlxAxes.X);
-		add(titleText);
-
-		messageText = new FlxText(0, (FlxG.height / 2) - 18, 0, "Final Score:", 8);
-		messageText.alignment = CENTER;
-		messageText.screenCenter(FlxAxes.X);
-		add(messageText);
-
-		scoreIcon = new FlxSprite((FlxG.width / 2) - 8, 0, AssetPaths.coin__png);
-		scoreIcon.screenCenter(FlxAxes.Y);
-		add(scoreIcon);
-
-		scoreText = new FlxText((FlxG.width / 2), 0, 0, Std.string(score), 8);
-		scoreText.screenCenter(FlxAxes.Y);
-		add(scoreText);
-
-		// we want to see what the highscore is
-		var highscore = checkHighscore(score);
-
-		highscoreText = new FlxText(0, (FlxG.height / 2) + 10, 0, "Highscore: " + highscore, 8);
-		highscoreText.alignment = CENTER;
-		highscoreText.screenCenter(FlxAxes.Y);
-		add(highscoreText);
-
-		mainMenuButton = new FlxButton(0, FlxG.height - 32, "Main Menu", switchToMainMenu);
-		mainMenuButton.screenCenter(FlxAxes.X);
-		mainMenuButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
-		add(mainMenuButton);
-
-		FlxG.camera.fade(FlxColor.BLACK, 0.33, true);
-	}
-
-	/**
-	 * This function will compare the new score with the saved highscore.
-	 * If the new score is higher, it will save it as the new highscore, otherwise, it will return the saved highscore.
-	 * @param	score	The new score
-	 * @return	the highscore
-	 */
-	function checkHighscore(score:Int):Int
-	{
-		var highscore:Int = score;
-		if (FlxG.save.data.highscore != null && FlxG.save.data.highscore > highscore)
-		{
-			highscore = FlxG.save.data.highscore;
-		}
-		else
-		{
-			// data is less or there is no data; save current score
-			FlxG.save.data.highscore = highscore;
-		}
-		return highscore;
-	}
-
-	/**
-	 * When the user hits the main menu button, it should fade out and then take them back to the MenuState
-	 */
-	function switchToMainMenu():Void
-	{
-		FlxG.camera.fade(FlxColor.BLACK, 0.33, false, function()
-		{
-			FlxG.switchState(MenuState.new);
-		});
-	}
-}
diff --git a/Tutorials/TurnBasedRPG/source/HUD.hx b/Tutorials/TurnBasedRPG/source/HUD.hx
deleted file mode 100644
index 09db963d2..000000000
--- a/Tutorials/TurnBasedRPG/source/HUD.hx
+++ /dev/null
@@ -1,46 +0,0 @@
-package;
-
-import flixel.FlxG;
-import flixel.FlxSprite;
-import flixel.group.FlxGroup;
-import flixel.text.FlxText;
-import flixel.util.FlxColor;
-
-using flixel.util.FlxSpriteUtil;
-
-class HUD extends FlxTypedGroup
-{
-	var background:FlxSprite;
-	var healthCounter:FlxText;
-	var moneyCounter:FlxText;
-	var healthIcon:FlxSprite;
-	var moneyIcon:FlxSprite;
-
-	public function new()
-	{
-		super();
-		background = new FlxSprite().makeGraphic(FlxG.width, 20, FlxColor.BLACK);
-		background.drawRect(0, 19, FlxG.width, 1, FlxColor.WHITE);
-		healthCounter = new FlxText(16, 2, 0, "3 / 3", 8);
-		healthCounter.setBorderStyle(SHADOW, FlxColor.GRAY, 1, 1);
-		moneyCounter = new FlxText(0, 2, 0, "0", 8);
-		moneyCounter.setBorderStyle(SHADOW, FlxColor.GRAY, 1, 1);
-		healthIcon = new FlxSprite(4, healthCounter.y + (healthCounter.height / 2) - 4, AssetPaths.health__png);
-		moneyIcon = new FlxSprite(FlxG.width - 12, moneyCounter.y + (moneyCounter.height / 2) - 4, AssetPaths.coin__png);
-		moneyCounter.alignment = RIGHT;
-		moneyCounter.x = moneyIcon.x - moneyCounter.width - 4;
-		add(background);
-		add(healthIcon);
-		add(moneyIcon);
-		add(healthCounter);
-		add(moneyCounter);
-		forEach(function(sprite) sprite.scrollFactor.set(0, 0));
-	}
-
-	public function updateHUD(health:Int, money:Int)
-	{
-		healthCounter.text = health + " / 3";
-		moneyCounter.text = Std.string(money);
-		moneyCounter.x = moneyIcon.x - moneyCounter.width - 4;
-	}
-}
diff --git a/Tutorials/TurnBasedRPG/source/Main.hx b/Tutorials/TurnBasedRPG/source/Main.hx
index 44c432989..ad37778ca 100644
--- a/Tutorials/TurnBasedRPG/source/Main.hx
+++ b/Tutorials/TurnBasedRPG/source/Main.hx
@@ -4,6 +4,7 @@ import flixel.FlxG;
 import flixel.FlxGame;
 import flixel.util.FlxSave;
 import openfl.display.Sprite;
+import states.MenuState;
 
 class Main extends Sprite
 {
diff --git a/Tutorials/TurnBasedRPG/source/MenuState.hx b/Tutorials/TurnBasedRPG/source/MenuState.hx
deleted file mode 100644
index 29a71272d..000000000
--- a/Tutorials/TurnBasedRPG/source/MenuState.hx
+++ /dev/null
@@ -1,78 +0,0 @@
-package;
-
-import flixel.FlxG;
-import flixel.FlxState;
-import flixel.text.FlxText;
-import flixel.ui.FlxButton;
-import flixel.util.FlxColor;
-
-class MenuState extends FlxState
-{
-	var titleText:FlxText;
-	var playButton:FlxButton;
-	var optionsButton:FlxButton;
-	#if desktop
-	var exitButton:FlxButton;
-	#end
-
-	override public function create()
-	{
-		titleText = new FlxText(20, 0, 0, "HaxeFlixel\nTutorial\nGame", 22);
-		titleText.alignment = CENTER;
-		titleText.screenCenter(X);
-		add(titleText);
-
-		playButton = new FlxButton(0, 0, "Play", clickPlay);
-		playButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
-		playButton.x = (FlxG.width / 2) - 10 - playButton.width;
-		playButton.y = FlxG.height - playButton.height - 10;
-		add(playButton);
-
-		optionsButton = new FlxButton(0, 0, "Options", clickOptions);
-		optionsButton.x = (FlxG.width / 2) + 10;
-		optionsButton.y = FlxG.height - optionsButton.height - 10;
-		add(optionsButton);
-
-		#if desktop
-		exitButton = new FlxButton(FlxG.width - 28, 8, "X", clickExit);
-		exitButton.loadGraphic(AssetPaths.button__png, true, 20, 20);
-		add(exitButton);
-		#end
-
-		if (FlxG.sound.music == null) // don't restart the music if it's already playing
-		{
-			#if flash
-			FlxG.sound.playMusic(AssetPaths.HaxeFlixel_Tutorial_Game__mp3, 1, true);
-			#else
-			FlxG.sound.playMusic(AssetPaths.HaxeFlixel_Tutorial_Game__ogg, 1, true);
-			#end
-		}
-
-		FlxG.camera.fade(FlxColor.BLACK, 0.33, true);
-
-		super.create();
-	}
-
-	function clickPlay()
-	{
-		FlxG.camera.fade(FlxColor.BLACK, 0.33, false, function()
-		{
-			FlxG.switchState(PlayState.new);
-		});
-	}
-
-	function clickOptions()
-	{
-		FlxG.camera.fade(FlxColor.BLACK, 0.33, false, function()
-		{
-			FlxG.switchState(OptionsState.new);
-		});
-	}
-
-	#if desktop
-	function clickExit()
-	{
-		Sys.exit(0);
-	}
-	#end
-}
diff --git a/Tutorials/TurnBasedRPG/source/Coin.hx b/Tutorials/TurnBasedRPG/source/objects/Coin.hx
similarity index 74%
rename from Tutorials/TurnBasedRPG/source/Coin.hx
rename to Tutorials/TurnBasedRPG/source/objects/Coin.hx
index 70e2994e4..4f8d39dad 100644
--- a/Tutorials/TurnBasedRPG/source/Coin.hx
+++ b/Tutorials/TurnBasedRPG/source/objects/Coin.hx
@@ -1,4 +1,4 @@
-package;
+package objects;
 
 import flixel.FlxSprite;
 import flixel.tweens.FlxEase;
@@ -9,7 +9,9 @@ class Coin extends FlxSprite
 	public function new(x:Float, y:Float)
 	{
 		super(x, y);
-		loadGraphic(AssetPaths.coin__png, false, 8, 8);
+		loadGraphic(AssetPaths.coin__png, true, 12, 12);
+		animation.add("idle", [0, 1], 4);
+		animation.play("idle");
 	}
 
 	override function kill()
diff --git a/Tutorials/TurnBasedRPG/source/Enemy.hx b/Tutorials/TurnBasedRPG/source/objects/Enemy.hx
similarity index 70%
rename from Tutorials/TurnBasedRPG/source/Enemy.hx
rename to Tutorials/TurnBasedRPG/source/objects/Enemy.hx
index db26ef240..0e5ea9bd6 100644
--- a/Tutorials/TurnBasedRPG/source/Enemy.hx
+++ b/Tutorials/TurnBasedRPG/source/objects/Enemy.hx
@@ -1,9 +1,10 @@
-package;
+package objects;
 
 import flixel.FlxG;
 import flixel.FlxSprite;
 import flixel.math.FlxPoint;
 import flixel.math.FlxVelocity;
+import flixel.tile.FlxTilemap;
 import flixel.sound.FlxSound;
 
 using flixel.util.FlxSpriteUtil;
@@ -16,8 +17,8 @@ enum EnemyType
 
 class Enemy extends FlxSprite
 {
-	static inline var WALK_SPEED:Float = 40;
-	static inline var CHASE_SPEED:Float = 70;
+	static inline var WALK_SPEED:Float = 50;
+	static inline var CHASE_SPEED:Float = 90;
 
 	var brain:FSM;
 	var idleTimer:Float;
@@ -27,15 +28,19 @@ class Enemy extends FlxSprite
 	public var type(default, null):EnemyType;
 	public var seesPlayer:Bool;
 	public var playerPosition:FlxPoint;
+	public var maxHP:Int;
+	public var hp:Int;
 
 	public function new(x:Float, y:Float, type:EnemyType)
 	{
 		super(x, y);
-		this.type = type;
-		var graphic = if (type == BOSS) AssetPaths.boss__png else AssetPaths.enemy__png;
-		loadGraphic(graphic, true, 16, 16);
-		setFacingFlip(LEFT, false, false);
-		setFacingFlip(RIGHT, true, false);
+		
+		changeType(type);
+		maxHP = type == REGULAR ? 2 : 4;
+		hp = maxHP;
+		
+		setFacingFlip(LEFT, true, false);
+		setFacingFlip(RIGHT, false, false);
 		animation.add("d_idle", [0]);
 		animation.add("lr_idle", [3]);
 		animation.add("u_idle", [6]);
@@ -43,9 +48,8 @@ class Enemy extends FlxSprite
 		animation.add("lr_walk", [3, 4, 3, 5], 6);
 		animation.add("u_walk", [6, 7, 6, 8], 6);
 		drag.x = drag.y = 10;
-		setSize(8, 8);
-		offset.x = 4;
-		offset.y = 8;
+		setSize(12, 12);
+		offset.set(6, 12);
 
 		brain = new FSM(idle);
 		idleTimer = 0;
@@ -55,11 +59,18 @@ class Enemy extends FlxSprite
 		stepSound = FlxG.sound.load(AssetPaths.step__wav, 0.4);
 		stepSound.proximity(x, y, FlxG.camera.target, FlxG.width * 0.6);
 	}
+	
+	public function hurt(damage:Int)
+	{
+		hp -= damage;
+	}
 
-	override public function update(elapsed:Float)
+	override function update(elapsed:Float)
 	{
 		if (this.isFlickering())
+		{
 			return;
+		}
 
 		var action = "idle";
 		if (velocity.x != 0 || velocity.y != 0)
@@ -67,37 +78,31 @@ class Enemy extends FlxSprite
 			action = "walk";
 			if (Math.abs(velocity.x) > Math.abs(velocity.y))
 			{
-				if (velocity.x < 0)
-					facing = LEFT;
-				else
-					facing = RIGHT;
+				facing = (velocity.x < 0) ? LEFT : RIGHT;
 			}
 			else
 			{
-				if (velocity.y < 0)
-					facing = UP;
-				else
-					facing = DOWN;
+				facing = (velocity.y < 0) ? UP : DOWN;
 			}
-
-			stepSound.setPosition(x + frameWidth / 2, y + height);
+			
+			stepSound.setPosition(x + width / 2, y + height);
 			stepSound.play();
 		}
-
+		
 		switch (facing)
 		{
 			case LEFT, RIGHT:
 				animation.play("lr_" + action);
-
+				
 			case UP:
 				animation.play("u_" + action);
-
+				
 			case DOWN:
 				animation.play("d_" + action);
-
+				
 			case _:
 		}
-
+		
 		brain.update(elapsed);
 		super.update(elapsed);
 	}
@@ -125,7 +130,9 @@ class Enemy extends FlxSprite
 			idleTimer = FlxG.random.int(1, 4);
 		}
 		else
+		{
 			idleTimer -= elapsed;
+		}
 	}
 
 	function chase(elapsed:Float)
@@ -139,6 +146,14 @@ class Enemy extends FlxSprite
 			FlxVelocity.moveTowardsPoint(this, playerPosition, CHASE_SPEED);
 		}
 	}
+	
+	public function checkVision(player:Player, walls:FlxTilemap)
+	{
+		// Store the player position
+		player.getMidpoint(playerPosition);
+		// Cast a ray from here to the player and see if a wall is blocking
+		seesPlayer = walls.ray(getMidpoint(FlxPoint.weak()), playerPosition);
+	}
 
 	public function changeType(type:EnemyType)
 	{
@@ -146,7 +161,7 @@ class Enemy extends FlxSprite
 		{
 			this.type = type;
 			var graphic = if (type == BOSS) AssetPaths.boss__png else AssetPaths.enemy__png;
-			loadGraphic(graphic, true, 16, 16);
+			loadGraphic(graphic, true, 24, 24);
 		}
 	}
 }
diff --git a/Tutorials/TurnBasedRPG/source/FSM.hx b/Tutorials/TurnBasedRPG/source/objects/FSM.hx
similarity index 91%
rename from Tutorials/TurnBasedRPG/source/FSM.hx
rename to Tutorials/TurnBasedRPG/source/objects/FSM.hx
index 9a7c4f116..8a6dd49c4 100644
--- a/Tutorials/TurnBasedRPG/source/FSM.hx
+++ b/Tutorials/TurnBasedRPG/source/objects/FSM.hx
@@ -1,3 +1,5 @@
+package objects;
+
 class FSM
 {
 	public var activeState:Float->Void;
diff --git a/Tutorials/TurnBasedRPG/source/Player.hx b/Tutorials/TurnBasedRPG/source/objects/Player.hx
similarity index 56%
rename from Tutorials/TurnBasedRPG/source/Player.hx
rename to Tutorials/TurnBasedRPG/source/objects/Player.hx
index 32a3186e6..5ed1a8c8e 100644
--- a/Tutorials/TurnBasedRPG/source/Player.hx
+++ b/Tutorials/TurnBasedRPG/source/objects/Player.hx
@@ -1,4 +1,4 @@
-package;
+package objects;
 
 import flixel.FlxG;
 import flixel.FlxSprite;
@@ -7,16 +7,21 @@ import flixel.sound.FlxSound;
 
 class Player extends FlxSprite
 {
-	static inline var SPEED:Float = 100;
+	static inline var SPEED:Float = 110;
+	/** Reaches top speed in 0.15 seconds */
+	static inline var ACCEL:Float = SPEED / 0.15;
 
-	var stepSound:FlxSound;
+	public final maxHP:Int = 3;
+	public var hp:Int;
 
 	public function new(x:Float = 0, y:Float = 0)
 	{
+		hp = maxHP;
 		super(x, y);
-		loadGraphic(AssetPaths.player__png, true, 16, 16);
-		setFacingFlip(LEFT, false, false);
-		setFacingFlip(RIGHT, true, false);
+		
+		loadGraphic(AssetPaths.player__png, true, 24, 24);
+		setFacingFlip(LEFT, true, false);
+		setFacingFlip(RIGHT, false, false);
 		animation.add("d_idle", [0]);
 		animation.add("lr_idle", [3]);
 		animation.add("u_idle", [6]);
@@ -25,20 +30,44 @@ class Player extends FlxSprite
 		animation.add("u_walk", [6, 7, 6, 8], 6);
 
 		drag.x = drag.y = 800;
-		setSize(8, 8);
-		offset.set(4, 8);
-
-		stepSound = FlxG.sound.load(AssetPaths.step__wav);
+		maxVelocity.x = maxVelocity.y = SPEED;
+		setSize(12, 12);
+		offset.set(6, 12);
+	}
+	
+	public function hurt(damage:Int)
+	{
+		hp -= damage;
 	}
 
 	override function update(elapsed:Float)
 	{
-		updateMovement();
 		super.update(elapsed);
+		
+		updateMovement();
 	}
 
 	function updateMovement()
 	{
+		var action = "idle";
+		// check if the player is moving, and not walking into walls
+		if (velocity.x != 0 || velocity.y != 0)
+		{
+			// FlxG.sound.play(AssetPaths.step__wav)
+			action = "walk";
+		}
+
+		switch (facing)
+		{
+			case LEFT, RIGHT:
+				animation.play("lr_" + action);
+			case UP:
+				animation.play("u_" + action);
+			case DOWN:
+				animation.play("d_" + action);
+			case _:
+		}
+		
 		var up:Bool = false;
 		var down:Bool = false;
 		var left:Bool = false;
@@ -58,65 +87,48 @@ class Player extends FlxSprite
 		left = left || virtualPad.buttonLeft.pressed;
 		right = right || virtualPad.buttonRight.pressed;
 		#end
-
+		
+		// Cancel out opposing directions
 		if (up && down)
+		{
 			up = down = false;
+		}
+		
 		if (left && right)
+		{
 			left = right = false;
-
-		if (up || down || left || right)
+		}
+		
+		acceleration.set(0, 0);
+		if (right)
 		{
-			var newAngle:Float = 0;
-			if (up)
-			{
-				newAngle = -90;
-				if (left)
-					newAngle -= 45;
-				else if (right)
-					newAngle += 45;
-				facing = UP;
-			}
-			else if (down)
-			{
-				newAngle = 90;
-				if (left)
-					newAngle += 45;
-				else if (right)
-					newAngle -= 45;
-				facing = DOWN;
-			}
-			else if (left)
-			{
-				newAngle = 180;
-				facing = LEFT;
-			}
-			else if (right)
-			{
-				newAngle = 0;
-				facing = RIGHT;
-			}
-
-			// determine our velocity based on angle and speed
-			velocity.setPolarDegrees(SPEED, newAngle);
+			facing = RIGHT;
+			acceleration.x = ACCEL;
 		}
-
-		var action = "idle";
-		// check if the player is moving, and not walking into walls
-		if ((velocity.x != 0 || velocity.y != 0) && touching == NONE)
+		else if (left)
 		{
-			stepSound.play();
-			action = "walk";
+			facing = LEFT;
+			acceleration.x = -ACCEL;
 		}
-
-		switch (facing)
+		
+		if (down)
 		{
-			case LEFT, RIGHT:
-				animation.play("lr_" + action);
-			case UP:
-				animation.play("u_" + action);
-			case DOWN:
-				animation.play("d_" + action);
-			case _:
+			facing = DOWN;
+			acceleration.y = ACCEL;
+		}
+		else if (up)
+		{
+			facing = UP;
+			acceleration.y = -ACCEL;
+		}
+		
+		// Prevent faster speeds on diagonal movement
+		var magnitude = velocity.length;
+		if (magnitude > SPEED)
+		{
+			// Reduce speed to SPEED but maintain direction
+			velocity.x *= SPEED / magnitude;
+			velocity.y *= SPEED / magnitude;
 		}
 	}
 }
diff --git a/Tutorials/TurnBasedRPG/source/states/GameOverState.hx b/Tutorials/TurnBasedRPG/source/states/GameOverState.hx
new file mode 100644
index 000000000..d5758c50f
--- /dev/null
+++ b/Tutorials/TurnBasedRPG/source/states/GameOverState.hx
@@ -0,0 +1,106 @@
+package states;
+
+import flixel.util.FlxTimer;
+import flixel.tweens.FlxEase;
+import flixel.tweens.FlxTween;
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.FlxState;
+import flixel.text.FlxBitmapText;
+import flixel.text.FlxText;
+import flixel.ui.FlxButton;
+import flixel.util.FlxAxes;
+import flixel.util.FlxColor;
+import ui.LargeText;
+
+class GameOverState extends FlxState
+{
+	/**
+	 * Called from PlayState, this will set our win and score variables
+	 * @param  win    Whether the player beat the boss, or died
+	 * @param  score  The number of coins collected
+	 */
+	public function new(win:Bool, score:Int)
+	{
+		super();
+		
+		#if FLX_MOUSE
+		FlxG.mouse.visible = true;
+		#end
+		
+		// create and add each of our items
+		
+		var titleText = new LargeText(0, 20, if (win) "You Win!" else "Game Over!");
+		titleText.screenCenter(FlxAxes.X);
+		add(titleText);
+
+		var messageText = new FlxText(0, (FlxG.height / 2) - 18, 0, "Final Score: 0", 8);
+		messageText.screenCenter(FlxAxes.X);
+		add(messageText);
+		
+		// Fade the camera from black
+		FlxG.camera.fade(FlxColor.BLACK, 0.33, true);
+		
+		// Count up the points for dramatic effect
+		FlxTween.num(0, score, 1.0, // from 0 to score in 1.0 second
+			{
+				startDelay: 0.33,// wait for the fade to complete
+				ease: FlxEase.circOut,
+				onComplete: function (tween:FlxTween)
+				{
+					// Wait 1 second and then show the highscore
+					new FlxTimer().start(0.5, (_)->showHighscore(score));
+				}
+			},
+			function updateText(tweenedScore)
+			{
+				messageText.text = "Final Score: " + Math.floor(tweenedScore);
+			}
+		);
+	}
+	
+	function showHighscore(score:Int)
+	{
+		// Get previous highscore
+		var highscore = 0;
+		if (FlxG.save.data.highscore != null)
+		{
+			highscore = FlxG.save.data.highscore;
+		}
+		
+		var highscoreText = new FlxText(0, (FlxG.height / 2) + 10, 0, "Highscore: " + highscore, 8);
+		add(highscoreText);
+		
+		// New high score
+		if (score > highscore)
+		{
+			FlxG.save.data.highscore = score;
+			highscoreText.text = "New Highscore!";
+		}
+		
+		highscoreText.screenCenter(FlxAxes.XY);
+		
+		// Wait a second then show the 
+		new FlxTimer().start(1.0, (_)->showButton());
+	}
+	
+	function showButton()
+	{
+		var mainMenuButton = new FlxButton(0, FlxG.height - 32, "Main Menu", switchToMainMenu);
+		mainMenuButton.loadGraphic(AssetPaths.button__png, true, 80, 20);
+		mainMenuButton.screenCenter(FlxAxes.X);
+		mainMenuButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
+		add(mainMenuButton);
+	}
+
+	/**
+	 * When the user hits the main menu button, it should fade out and then take them back to the MenuState
+	 */
+	function switchToMainMenu():Void
+	{
+		FlxG.camera.fade(FlxColor.BLACK, 0.33, false, function()
+		{
+			FlxG.switchState(MenuState.new);
+		});
+	}
+}
diff --git a/Tutorials/TurnBasedRPG/source/states/MenuState.hx b/Tutorials/TurnBasedRPG/source/states/MenuState.hx
new file mode 100644
index 000000000..ea92059e3
--- /dev/null
+++ b/Tutorials/TurnBasedRPG/source/states/MenuState.hx
@@ -0,0 +1,111 @@
+package states;
+
+import ui.OptionsSubState;
+import ui.LargeText;
+import flixel.FlxG;
+import flixel.FlxState;
+import flixel.text.FlxBitmapText;
+import flixel.text.FlxText;
+import flixel.ui.FlxButton;
+import flixel.util.FlxColor;
+import flixel.addons.editors.ogmo.FlxOgmo3Loader;
+
+class MenuState extends FlxState
+{
+	override public function create()
+	{
+		// NOTE: differs from tutorial!
+		var map = new FlxOgmo3Loader(AssetPaths.turnBasedRPG__ogmo, AssetPaths.room_001__json);
+		var walls = map.loadTilemap(AssetPaths.tiles__png, "walls");
+		walls.x -= 123;
+		walls.y -= 10;
+		add(walls);
+		
+		// Use FlxBitmapText for crisper edges on large text
+		var titleText = new LargeText(0, 16, "DUNGEON\nCRAWLER");
+		titleText.alignment = CENTER;
+		titleText.setBorderStyle(OUTLINE, 0xFF3f2631);
+		titleText.screenCenter(X);
+		add(titleText);
+		
+		// TUTORIAL VERSION:
+		// var titleText = new FlxText(0, 16, 0, "DUNGEON\nCRAWLER", 32);
+		// titleText.alignment = CENTER;
+		// titleText.screenCenter(X);
+		// add(titleText);
+
+		var playButton = new FlxButton(0, 0, "Play", clickPlay);
+		playButton.loadGraphic(AssetPaths.button__png, true, 80, 20);
+		playButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
+		playButton.x = (FlxG.width / 2) - 10 - playButton.width;
+		playButton.y = FlxG.height - playButton.height - 10;
+		add(playButton);
+
+		var optionsButton = new FlxButton(0, 0, "Options", clickOptions);
+		optionsButton.loadGraphic(AssetPaths.button__png, true, 80, 20);
+		optionsButton.x = (FlxG.width / 2) + 10;
+		optionsButton.y = FlxG.height - optionsButton.height - 10;
+		add(optionsButton);
+
+		#if desktop
+		var exitButton = new FlxButton(FlxG.width - 28, 8, "X", clickExit);
+		exitButton.loadGraphic(AssetPaths.small_button__png, true, 20, 20);
+		add(exitButton);
+		#end
+
+		if (FlxG.sound.music == null) // don't restart the music if it's already playing
+		{
+			initSound();
+		}
+
+		FlxG.camera.fade(FlxColor.BLACK, 0.33, true);
+
+		super.create();
+	}
+	
+	function initSound()
+	{
+		var volumes:{ music:Float, sound:Float } = null;
+		if (FlxG.save.data.volumes != null)
+		{
+			volumes = FlxG.save.data.volumes;
+		}
+		else
+		{
+			volumes = { music:0.5, sound:1.0 };
+		}
+		
+		FlxG.sound.defaultMusicGroup.volume = volumes.music;
+		FlxG.sound.defaultSoundGroup.volume = volumes.sound;
+		
+		#if flash
+		FlxG.sound.playMusic(AssetPaths.HaxeFlixel_Tutorial_Game__mp3, 1.0, true);
+		#else
+		FlxG.sound.playMusic(AssetPaths.HaxeFlixel_Tutorial_Game__ogg, 1.0, true);
+		#end
+	}
+
+	function clickPlay()
+	{
+		FlxG.camera.fade(FlxColor.BLACK, 0.33, false, function()
+		{
+			FlxG.switchState(PlayState.new);
+		});
+	}
+
+	function clickOptions()
+	{
+		openSubState(new OptionsSubState());
+		// FlxG.camera.fade(FlxColor.BLACK, 0.33, false, function()
+		// {
+		// 	FlxG.switchState(OptionsState.new);
+		// });
+	}
+
+	#if desktop
+	function clickExit()
+	{
+		Sys.exit(0);
+	}
+	#end
+}
diff --git a/Tutorials/TurnBasedRPG/source/OptionsState.hx b/Tutorials/TurnBasedRPG/source/states/OptionsState.hx
similarity index 76%
rename from Tutorials/TurnBasedRPG/source/OptionsState.hx
rename to Tutorials/TurnBasedRPG/source/states/OptionsState.hx
index 4a567f211..7c1d2e0b2 100644
--- a/Tutorials/TurnBasedRPG/source/OptionsState.hx
+++ b/Tutorials/TurnBasedRPG/source/states/OptionsState.hx
@@ -1,8 +1,9 @@
-package;
+package states;
 
 import flixel.FlxG;
 import flixel.FlxState;
 import flixel.text.FlxText;
+import flixel.text.FlxBitmapText;
 import flixel.ui.FlxBar;
 import flixel.ui.FlxButton;
 import flixel.util.FlxAxes;
@@ -11,49 +12,41 @@ import flixel.util.FlxColor;
 class OptionsState extends FlxState
 {
 	// define our screen elements
-	var titleText:FlxText;
 	var volumeBar:FlxBar;
-	var volumeText:FlxText;
 	var volumeAmountText:FlxText;
-	var volumeDownButton:FlxButton;
-	var volumeUpButton:FlxButton;
-	var clearDataButton:FlxButton;
-	var backButton:FlxButton;
 	#if desktop
 	var fullscreenButton:FlxButton;
 	#end
-
+	
 	override public function create():Void
 	{
 		// setup and add our objects to the screen
-		titleText = new FlxText(0, 20, 0, "Options", 22);
-		titleText.alignment = CENTER;
-		titleText.screenCenter(FlxAxes.X);
+		var titleText = new LargeText(0, 32, "Options");
+		titleText.screenCenter(X);
 		add(titleText);
-
-		volumeText = new FlxText(0, titleText.y + titleText.height + 10, 0, "Volume", 8);
+		
+		var volumeText = new FlxText(0, titleText.y + titleText.height + 10, 0, "Volume", 8);
 		volumeText.alignment = CENTER;
-		volumeText.screenCenter(FlxAxes.X);
+		volumeText.screenCenter(X);
 		add(volumeText);
-
+		
 		// the volume buttons will be smaller than 'default' buttons
-		volumeDownButton = new FlxButton(8, volumeText.y + volumeText.height + 2, "-",
-			clickVolumeDown);
-		volumeDownButton.loadGraphic(AssetPaths.button__png, true, 20, 20);
+		var volumeDownButton = new FlxButton(8, volumeText.y + volumeText.height + 2, "-", clickVolumeDown);
+		volumeDownButton.loadGraphic(AssetPaths.small_button__png, true, 20, 20);
 		volumeDownButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
 		add(volumeDownButton);
-
-		volumeUpButton = new FlxButton(FlxG.width - 28, volumeDownButton.y, "+", clickVolumeUp);
-		volumeUpButton.loadGraphic(AssetPaths.button__png, true, 20, 20);
+		
+		var volumeUpButton = new FlxButton(FlxG.width - 28, volumeDownButton.y, "+", clickVolumeUp);
+		volumeUpButton.loadGraphic(AssetPaths.small_button__png, true, 20, 20);
 		volumeUpButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
 		add(volumeUpButton);
-
+		
 		volumeBar = new FlxBar(volumeDownButton.x + volumeDownButton.width + 4,
 			volumeDownButton.y, LEFT_TO_RIGHT, Std.int(FlxG.width - 64),
 			Std.int(volumeUpButton.height));
 		volumeBar.createFilledBar(0xff464646, FlxColor.WHITE, true, FlxColor.WHITE);
 		add(volumeBar);
-
+		
 		volumeAmountText = new FlxText(0, 0, 200, (FlxG.sound.volume * 100) + "%", 8);
 		volumeAmountText.alignment = CENTER;
 		volumeAmountText.borderStyle = FlxTextBorderStyle.OUTLINE;
@@ -61,23 +54,25 @@ class OptionsState extends FlxState
 		volumeAmountText.y = volumeBar.y + (volumeBar.height / 2) - (volumeAmountText.height / 2);
 		volumeAmountText.screenCenter(FlxAxes.X);
 		add(volumeAmountText);
-
+		
 		#if desktop
 		fullscreenButton = new FlxButton(0, volumeBar.y + volumeBar.height + 8,
 			FlxG.fullscreen ? "FULLSCREEN" : "WINDOWED", clickFullscreen);
+		fullscreenButton.loadGraphic(AssetPaths.button__png, true, 80, 20);
 		fullscreenButton.screenCenter(FlxAxes.X);
 		add(fullscreenButton);
 		#end
-
-		clearDataButton = new FlxButton((FlxG.width / 2) - 90, FlxG.height - 28, "Clear Data",
-			clickClearData);
+		
+		var clearDataButton = new FlxButton((FlxG.width / 2) - 90, FlxG.height - 28, "Clear Data", clickClearData);
+		clearDataButton.loadGraphic(AssetPaths.button__png, true, 80, 20);
 		clearDataButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
 		add(clearDataButton);
-
-		backButton = new FlxButton((FlxG.width / 2) + 10, FlxG.height - 28, "Back", clickBack);
+		
+		var backButton = new FlxButton((FlxG.width / 2) + 10, FlxG.height - 28, "Back", clickBack);
+		backButton.loadGraphic(AssetPaths.button__png, true, 80, 20);
 		backButton.onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
 		add(backButton);
-
+		
 		// update our bar to show the current volume level
 		updateVolume();
 
diff --git a/Tutorials/TurnBasedRPG/source/PlayState.hx b/Tutorials/TurnBasedRPG/source/states/PlayState.hx
similarity index 52%
rename from Tutorials/TurnBasedRPG/source/PlayState.hx
rename to Tutorials/TurnBasedRPG/source/states/PlayState.hx
index b2a51962b..9b6d0dcaf 100644
--- a/Tutorials/TurnBasedRPG/source/PlayState.hx
+++ b/Tutorials/TurnBasedRPG/source/states/PlayState.hx
@@ -1,101 +1,91 @@
-package;
+package states;
 
 import flixel.FlxG;
 import flixel.FlxState;
 import flixel.addons.editors.ogmo.FlxOgmo3Loader;
-import flixel.group.FlxGroup.FlxTypedGroup;
+import flixel.group.FlxGroup;
 import flixel.sound.FlxSound;
 import flixel.tile.FlxTilemap;
 import flixel.util.FlxColor;
 #if mobile
 import flixel.ui.FlxVirtualPad;
 #end
+import objects.Coin;
+import objects.Enemy;
+import objects.Player;
+import ui.CombatSubState;
+import ui.HUD;
 
 using flixel.util.FlxSpriteUtil;
 
 class PlayState extends FlxState
 {
 	var player:Player;
-	var map:FlxOgmo3Loader;
 	var walls:FlxTilemap;
 	var coins:FlxTypedGroup;
 	var enemies:FlxTypedGroup;
-
+	
 	var hud:HUD;
 	var money:Int = 0;
-	var health:Int = 3;
-
-	var inCombat:Bool = false;
-	var combatHud:CombatHUD;
-
-	var ending:Bool;
-	var won:Bool;
-
-	var coinSound:FlxSound;
-
+	
 	#if mobile
 	public static var virtualPad:FlxVirtualPad;
 	#end
-
+	
 	override public function create()
 	{
 		#if FLX_MOUSE
 		FlxG.mouse.visible = false;
 		#end
-
-		map = new FlxOgmo3Loader(AssetPaths.turnBasedRPG__ogmo, AssetPaths.room_001__json);
+		
+		var map = new FlxOgmo3Loader(AssetPaths.turnBasedRPG__ogmo, AssetPaths.room_001__json);
 		walls = map.loadTilemap(AssetPaths.tiles__png, "walls");
 		walls.follow();
-		walls.setTileProperties(1, NONE);
-		walls.setTileProperties(2, ANY);
+		walls.setTileProperties(0, NONE, null, null, 16);
+		walls.setTileProperties(16, ANY, null, null, 20);
 		add(walls);
-
+		
 		coins = new FlxTypedGroup();
 		add(coins);
-
+		
 		enemies = new FlxTypedGroup();
 		add(enemies);
-
+		
 		player = new Player();
 		map.loadEntities(placeEntities, "entities");
 		add(player);
-
+		
 		FlxG.camera.follow(player, TOPDOWN, 1);
-
+		
 		hud = new HUD();
 		add(hud);
-
-		combatHud = new CombatHUD();
-		add(combatHud);
-
-		coinSound = FlxG.sound.load(AssetPaths.coin__wav);
-
+		
 		#if mobile
 		virtualPad = new FlxVirtualPad(FULL, NONE);
 		add(virtualPad);
 		#end
-
+		
 		FlxG.camera.fade(FlxColor.BLACK, 0.33, true);
-
+		
 		super.create();
 	}
-
+	
 	function placeEntities(entity:EntityData)
 	{
 		var x = entity.x;
 		var y = entity.y;
-
+		
 		switch (entity.name)
 		{
 			case "player":
 				player.setPosition(x, y);
-
+				
 			case "coin":
 				coins.add(new Coin(x + 4, y + 4));
-
+				
 			case "enemy":
 				enemies.add(new Enemy(x + 4, y, REGULAR));
-
+				
 			case "boss":
 				enemies.add(new Enemy(x + 4, y, BOSS));
 		}
@@ -104,62 +94,12 @@ class PlayState extends FlxState
 	override public function update(elapsed:Float)
 	{
 		super.update(elapsed);
-
-		if (ending)
-		{
-			return;
-		}
-
-		if (inCombat)
-		{
-			if (!combatHud.visible)
-			{
-				health = combatHud.playerHealth;
-				hud.updateHUD(health, money);
-				if (combatHud.outcome == DEFEAT)
-				{
-					ending = true;
-					FlxG.camera.fade(FlxColor.BLACK, 0.33, false, doneFadeOut);
-				}
-				else
-				{
-					if (combatHud.outcome == VICTORY)
-					{
-						combatHud.enemy.kill();
-						if (combatHud.enemy.type == BOSS)
-						{
-							won = true;
-							ending = true;
-							FlxG.camera.fade(FlxColor.BLACK, 0.33, false, doneFadeOut);
-						}
-					}
-					else
-					{
-						combatHud.enemy.flicker();
-					}
-					inCombat = false;
-					player.active = true;
-					enemies.active = true;
-
-					#if mobile
-					virtualPad.visible = true;
-					#end
-				}
-			}
-		}
-		else
-		{
-			FlxG.collide(player, walls);
-			FlxG.overlap(player, coins, playerTouchCoin);
-			FlxG.collide(enemies, walls);
-			enemies.forEachAlive(checkEnemyVision);
-			FlxG.overlap(player, enemies, playerTouchEnemy);
-		}
-	}
-
-	function doneFadeOut()
-	{
-		FlxG.switchState(() -> new GameOverState(won, money));
+		
+		FlxG.collide(player, walls);
+		FlxG.overlap(player, coins, playerTouchCoin);
+		FlxG.collide(enemies, walls);
+		enemies.forEachAlive(checkEnemyVision);
+		FlxG.overlap(player, enemies, playerTouchEnemy);
 	}
 
 	function playerTouchCoin(player:Player, coin:Coin)
@@ -168,22 +108,14 @@ class PlayState extends FlxState
 		{
 			coin.kill();
 			money++;
-			hud.updateHUD(health, money);
-			coinSound.play(true);
+			hud.updateMoney(money);
+			FlxG.sound.play(AssetPaths.coin__wav);
 		}
 	}
 
 	function checkEnemyVision(enemy:Enemy)
 	{
-		if (walls.ray(enemy.getMidpoint(), player.getMidpoint()))
-		{
-			enemy.seesPlayer = true;
-			enemy.playerPosition = player.getMidpoint();
-		}
-		else
-		{
-			enemy.seesPlayer = false;
-		}
+		enemy.checkVision(player, walls);
 	}
 
 	function playerTouchEnemy(player:Player, enemy:Enemy)
@@ -196,13 +128,40 @@ class PlayState extends FlxState
 
 	function startCombat(enemy:Enemy)
 	{
-		inCombat = true;
-		player.active = false;
-		enemies.active = false;
-		combatHud.initCombat(health, enemy);
-
 		#if mobile
 		virtualPad.visible = false;
 		#end
+		
+		FlxG.sound.play(AssetPaths.combat__wav);
+		openSubState(new CombatSubState(player, enemy, (outcome)->handleCombatOutcome(outcome, enemy)));
+	}
+	
+	function handleCombatOutcome(outcome:CombatOutcome, enemy:Enemy)
+	{
+		hud.updateHealth(player.hp);
+		switch(outcome)
+		{
+			case VICTORY:
+				enemy.kill();
+				if (enemy.type == BOSS)
+				{
+					fadeToGameOver(true);
+				}
+			case ESCAPED:
+				enemy.flicker();
+			case DEFEAT:
+				player.alive = false;
+				
+				fadeToGameOver(false);
+		}
+	}
+	
+	function fadeToGameOver(won:Bool)
+	{
+		function onComplete()
+		{
+			FlxG.switchState(()->new GameOverState(won, money));
+		}
+		FlxG.camera.fade(FlxColor.BLACK, 0.33, false, onComplete);
 	}
 }
diff --git a/Tutorials/TurnBasedRPG/source/ui/CombatSubState.hx b/Tutorials/TurnBasedRPG/source/ui/CombatSubState.hx
new file mode 100644
index 000000000..c144676bf
--- /dev/null
+++ b/Tutorials/TurnBasedRPG/source/ui/CombatSubState.hx
@@ -0,0 +1,528 @@
+package ui;
+
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.group.FlxGroup;
+import flixel.group.FlxSpriteGroup;
+import flixel.math.FlxRect;
+import flixel.text.FlxText;
+import flixel.tweens.FlxEase;
+import flixel.tweens.FlxTween;
+import flixel.ui.FlxBar;
+import flixel.util.FlxAxes;
+import flixel.util.FlxColor;
+import flixel.util.FlxTimer;
+import flixel.util.FlxDirectionFlags;
+import flixel.addons.display.FlxSliceSprite;
+import flixel.addons.effects.chainable.FlxEffectSprite;
+import flixel.addons.effects.chainable.FlxWaveEffect;
+
+import objects.Player;
+import objects.Enemy;
+
+import openfl.filters.ColorMatrixFilter;
+import openfl.geom.Matrix;
+import openfl.geom.Point;
+
+enum CombatOutcome
+{
+	ESCAPED;
+	VICTORY;
+	DEFEAT;
+}
+
+private enum Choice
+{
+	FIGHT;
+	FLEE;
+}
+
+/**
+ * A SubState that pauses the main game to show the combat UI
+ */
+class CombatSubState extends flixel.FlxSubState
+{
+	/** The UI handling the actual battling */
+	var ui:CombatUI;
+	
+	public function new (player:Player, enemy:Enemy, callback:(CombatOutcome)->Void)
+	{
+		super();
+		
+		// Adds a neat wave effect to the game, this helps separate the game from the UI
+		var waveEffect = new CombatWaveEffect(4, 4);
+		add(waveEffect);
+		
+		/** Called when the UI determines an outcome of the battle */
+		function outcomeReceived(outcome:CombatOutcome)
+		{
+			// disable the UI, tween it away, then pass the outcome to the callback
+			ui.active = false;
+			FlxTween.tween(ui, { y:FlxG.height }, 0.5, { ease: FlxEase.backIn, onComplete: 
+				function (_)
+				{
+					// send the result to the play state and close
+					callback(outcome);
+					close();
+				}
+			});
+			waveEffect.fadeOut(0.5);
+		}
+		
+		// Create the UI
+		ui = new CombatUI(player, enemy, outcomeReceived);
+		ui.screenCenter();
+		// ignore camera scroll to maintain screen position
+		ui.scrollFactor.set(0, 0);
+		add(ui);
+		
+		// store target y and then hide UI below the screen
+		var targetY = ui.y;
+		ui.y = FlxG.height;
+		
+		// disable the UI, tween it up from the bottom, then enable it
+		ui.active = false;
+		function onTweenComplete(tween:FlxTween)
+		{
+			ui.active = true;
+		}
+		FlxG.camera.flash(FlxColor.WHITE, .2);
+		FlxTween.tween(ui, { y:targetY }, 0.5,
+			{
+				startDelay: 0.2,// wait for flash
+				ease: FlxEase.backOut,
+				onComplete: onTweenComplete
+			}
+		);
+		waveEffect.fadeIn(1.0);
+	}
+}
+
+/**
+ * The UI that controls combat, extends `FlxSpriteGroup` which changes its members
+ * when certain properties are changed, namely `x`, `y`, `alpha` and `scrollFactor`.
+ */
+class CombatUI extends FlxSpriteGroup
+{
+	/** A function that handles the outcome of this match */
+	var callback:(CombatOutcome)->Void;
+	
+	/** A reference to the player sprite in the main game */
+	var player:Player;
+	/** A sprite representing our hero */
+	var playerIcon:FlxSprite;
+	/** Used to show the result of attacks */
+	var playerDmg:FlxText;
+	/** Displays the player's health */
+	var playerHealthBar:FlxBar;
+	
+	/** A reference to our current opponent in the main game */
+	var enemy:Enemy;
+	/** A sprite representing our enemy */
+	var enemyIcon:FlxSprite;
+	/** Used to show the result of attacks */
+	var enemyDmg:FlxText;
+	/** Displays the enemy's health */
+	var enemyHealthBar:FlxBar;
+	
+	/** A group of texts that show damage or missed attacks */
+	var attackResults:FlxTypedGroup;
+	
+	/** Shows the currently selected option (FIGHT or FLEE). */
+	var pointer:FlxSprite; 
+	/** Tracks which option is selected */
+	var selected:Choice = FIGHT; 
+	/** Contains text displaying our 2 options: FIGHT and FLEE */
+	var choices:Map; 
+	
+	public function new (player:Player, enemy:Enemy, callback:(CombatOutcome)->Void)
+	{
+		this.callback = callback;
+		this.player = player;
+		this.enemy = enemy;
+		super();
+		
+		// Make a background to visially separate the ui from the game underneath
+		var bg = new FlxSliceSprite(AssetPaths.uiback__png, new FlxRect(16, 16, 16, 16), 128, 128);
+		// Stretch the sections rather than tiling for better performance
+		bg.stretchTop
+			= bg.stretchRight
+			= bg.stretchLeft
+			= bg.stretchBottom
+			= bg.stretchCenter
+			= true;
+		add(bg);
+		
+		final border = 6;
+		var section = new FlxSliceSprite(AssetPaths.ui_section__png, new FlxRect(16, 16, 16, 16), bg.width - border * 2, 72);
+		section.x = border;
+		section.y = bg.height - section.height - border;
+		// Stretch the sections rather than tiling for better performance
+		section.stretchTop
+			= bg.stretchRight
+			= bg.stretchLeft
+			= bg.stretchBottom
+			= section.stretchCenter
+			= true;
+		add(section);
+		
+		// Make a 'dummy' player and health bar
+		playerIcon = createAvatar(24, 8, player, player.maxHP);
+		playerIcon.facing = RIGHT;
+		
+		// Make a 'dummy' enemy and health bar
+		enemyIcon = createAvatar(80, 8, enemy, enemy.maxHP);
+		enemyIcon.facing = LEFT;
+		
+		attackResults = new FlxTypedGroup();
+		for (i in 0...2)
+		{
+			var text = new FlxText(0, 0);
+			// Use same color as the pointer
+			text.color = 0xFFe5e5e5;
+			text.setBorderStyle(SHADOW, FlxColor.RED);
+			attackResults.add(text);
+			add(text);
+			text.kill();
+		}
+		
+		// create our choices and add them to the group.
+		choices = new Map();
+		choices[FIGHT] = new LargeText(40, 64, "FIGHT", 2);
+		choices[FLEE] = new LargeText(40, choices[FIGHT].y + choices[FIGHT].height + 10, "FLEE", 2);
+		add(choices[FIGHT]);
+		add(choices[FLEE]);
+		
+		pointer = new FlxSprite(16, choices[FIGHT].y + (choices[FIGHT].height / 2) - 8, AssetPaths.pointer__png);
+		add(pointer);
+	}
+	
+	/**
+	 * Creates and adds an Icon and health bar for the target fighter
+	 */
+	function createAvatar(x:Float, y:Float, target:FlxSprite, maxHealth:Float)
+	{
+		// Create an "dummy" icon of the target
+		var icon = new FlxSprite(x, y);
+		// Use the target's graphic to have the same frames
+		icon.loadGraphicFromSprite(target);
+		icon.animation.play("lr_idle");
+		
+		icon.setFacingFlip(LEFT, true, false);
+		icon.setFacingFlip(RIGHT, false, false);
+		add(icon);
+		
+		// create a health bar
+		var bar = new FlxBar(0, icon.y + icon.height + 2, LEFT_TO_RIGHT, 30, 10);
+		bar.createImageBar("assets/images/bar_empty.png", "assets/images/bar_filled.png", 0x0, 0x0);
+		bar.setRange(0, maxHealth);
+		// tracks the target's health automatically
+		bar.parent = target;
+		bar.parentVariable = "hp";
+		bar.update(0);//redraw
+		// yellow bar, yellow border, red underneath
+		centerOn(bar, icon, X);
+		add(bar);
+		
+		return icon;
+	}
+	
+	override function update(elapsed:Float)
+	{
+		super.update(elapsed);
+		
+		// Only check input between turns when the pointer is showing
+		if (pointer.exists)
+		{
+			#if FLX_KEYBOARD
+			updateKeyboardInput();
+			#end
+			
+			#if FLX_TOUCH
+			updateTouchInput();
+			#end
+		}
+	}
+	
+	/**
+	 * Call this function to place the pointer next to the currently selected choice
+	 */
+	inline function hilightChoice(choice:Choice)
+	{
+		selected = choice;
+		centerOn(pointer, choices[selected], Y);
+	}
+	
+	#if FLX_KEYBOARD
+	function updateKeyboardInput()
+	{
+		// Create helper func with shorter name
+		var justPressed = FlxG.keys.anyJustPressed;
+		var justReleased = FlxG.keys.anyJustReleased;
+		if (justPressed([SPACE, X, ENTER]))
+		{
+			FlxG.sound.play(AssetPaths.select__wav);
+			onChoose(selected);
+		}
+		// if the playerSprite presses up or down (but not both)
+		else if (justReleased([W, UP]) != justReleased([S, DOWN]))
+		{
+			FlxG.sound.play(AssetPaths.select__wav);
+			// Move the cursor up or down (with wrapping)
+			hilightChoice(selected == FIGHT ? FLEE : FIGHT);
+		}
+	}
+	#end
+
+	#if FLX_TOUCH
+	function updateTouchInput()
+	{
+		for (touch in FlxG.touches.justReleased())
+		{
+			for (choice in choices.keys())
+			{
+				var text = choices[choice];
+				if (touch.overlaps(text))
+				{
+					FlxG.sound.play(AssetPaths.select__wav);
+					hilightChoice(choice);
+					onChoose(choice);
+					return;
+				}
+			}
+		}
+	}
+	#end
+	
+	function onChoose(selected:Choice)
+	{
+		// kill texts from last round
+		attackResults.killMembers();
+		// hide our pointer
+		pointer.exists = false;
+		// check which item was selected when the playerSprite picked it
+		switch (selected)
+		{
+			case FIGHT:
+				playerAttack();
+				
+			case FLEE:
+				// 50% chance to flee
+				if (FlxG.random.bool(50))
+				{
+					// Success
+					FlxG.sound.play(AssetPaths.fled__wav);
+					showOutcome(ESCAPED);
+				}
+				else
+				{
+					// Failure, enemy gets a free attack
+					enemyAttack();
+				}
+		}
+	}
+	
+	function showAttackResult(icon:FlxSprite, msg:String, ?onComplete:()->Void)
+	{
+		// recycle dead text
+		var text = attackResults.recycle();
+		text.text = msg;
+		centerOn(text, icon, XY);
+		// text.alpha = 0;
+		
+		var callback = onComplete != null ? (_)->onComplete() : null;
+		FlxTween.tween(text, { alpha: 2 }, 0.5, { ease: FlxEase.circOut, onComplete: callback });
+		FlxTween.tween(text, { y: text.y - 12 }, 1, { ease: FlxEase.circOut, onComplete:(_)->text.kill() });
+	}
+	
+	function playerAttack()
+	{
+		// Move the player towards the enemy then back
+		FlxTween.tween(playerIcon, {x: playerIcon.x + 12}, 0.1)
+			.then(FlxTween.tween(playerIcon, {x: playerIcon.x}, 0.1));
+	
+		var result:String;
+		// 85% chance for player to hit
+		if (FlxG.random.bool(85))
+		{
+			// Success
+			FlxG.sound.play(AssetPaths.hurt__wav);
+			// Move the enemy, then move it back
+			FlxTween.tween(enemyIcon, {x: enemyIcon.x + 4}, 0.1, {startDelay: 0.1})
+				.then(FlxTween.tween(enemyIcon, {x: enemyIcon.x}, 0.1));
+			
+			// Deal 1 damage to the enemy
+			enemy.hurt(1);
+			result = "1";
+		}
+		else
+		{
+			// We missed
+			FlxG.sound.play(AssetPaths.miss__wav);
+			result = "MISS!";
+		}
+		
+		// Show the attack result then it's the enemy's turn
+		if (enemy.hp > 0)
+		{
+			showAttackResult(enemyIcon, result, enemyAttack);
+		}
+		else
+		{
+			showAttackResult(enemyIcon, result, roundEnd);
+		}
+	}
+	
+	function enemyAttack()
+	{
+		// Move the enemy towards the player, then move it back
+		FlxTween.tween(enemyIcon, {x: enemyIcon.x - 12}, 0.1)
+			.then(FlxTween.tween(enemyIcon, {x: enemyIcon.x}, 0.1));
+		
+		var result:String;
+		// 30% chance to hit
+		if (FlxG.random.bool(30))
+		{
+			// Flash the screen white
+			FlxG.camera.flash(FlxColor.WHITE, .2);
+			FlxG.camera.shake(0.01, 0.2);
+			
+			// Move the enemy, then move it back
+			FlxTween.tween(playerIcon, {x: playerIcon.x - 4}, 0.1, {startDelay: 0.1})
+				.then(FlxTween.tween(playerIcon, {x: playerIcon.x}, 0.1));
+			// Deal 1 damage
+			player.hurt(1);
+			FlxG.sound.play(AssetPaths.hurt__wav);
+			result = "1";
+		}
+		else
+		{
+			// Missed
+			FlxG.sound.play(AssetPaths.miss__wav);
+			result = "MISS!";
+		}
+		
+		showAttackResult(playerIcon, result, roundEnd);
+	}
+	
+	function roundEnd()
+	{
+		if (player.hp <= 0)
+		{
+			showOutcome(DEFEAT);
+		}
+		else if (enemy.hp <= 0)
+		{
+			showOutcome(VICTORY);
+		}
+		else
+		{
+			// Enables UI for net turn
+			pointer.exists = true;
+		}
+	}
+	
+	function showOutcome(outcome:CombatOutcome)
+	{
+		var text = new LargeText(0, 0, outcome.getName(), 3);
+		text.color = FlxColor.YELLOW;
+		text.setBorderStyle(SHADOW, FlxColor.GRAY);
+		// Adding a sprite to a sprite group will change the x/y by the group's, so add first
+		add(text);
+		text.screenCenter();
+		
+		// Store desired y, then tween to it
+		var targetY = text.y;
+		text.y = -text.height;
+		FlxTween.tween(text, { y: targetY }, 1, { ease: FlxEase.backOut, onComplete:
+			function (_)
+			{
+				// Hold it there for a sec, then start the outro
+				FlxTimer.wait(1.0, ()->callback(outcome));
+			}
+		});
+	}
+	
+	/**
+	 * Centers the first sprite's so it's center x mathes the center x of the target
+	 */
+	static inline function centerOn(sprite:FlxSprite, target:FlxSprite, axes:FlxAxes = XY)
+	{
+		if (axes.x)
+		{
+			sprite.x = target.x + (target.width - sprite.width) / 2;
+		}
+		
+		if (axes.y)
+		{
+			sprite.y = target.y + (target.height - sprite.height) / 2;
+		}
+	}
+}
+
+/**
+ * Helper class that handles the wave effect
+ */
+private class CombatWaveEffect extends FlxEffectSprite
+{
+	/**
+	 * [Description]
+	 * @param  strength  How strong you want the effect
+	 * @param  speed     How fast you want the effect to move, higher values = faster
+	 */
+	public function new(strength = 10, speed = 3.0)
+	{
+		var screen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.TRANSPARENT);
+		super(screen, [new FlxWaveEffect(FlxWaveMode.ALL, strength, -1, speed)]);
+		scrollFactor.set(0, 0);
+		
+		// Draw the camera to a bitmap
+		screen.drawFrame();
+		var screenPixels = screen.framePixels;
+		if (FlxG.renderBlit)
+			screenPixels.copyPixels(FlxG.camera.buffer, FlxG.camera.buffer.rect, new Point());
+		else
+			screenPixels.draw(FlxG.camera.canvas, new Matrix(1, 0, 0, 1, 0, 0));
+		
+		// Apply desaturating color matrix
+		var rc:Float = 1 / 3;
+		var gc:Float = 1 / 2;
+		var bc:Float = 1 / 6;
+		screenPixels.applyFilter(screenPixels, screenPixels.rect, new Point(),
+			new ColorMatrixFilter([rc, gc, bc, 0, 0, rc, gc, bc, 0, 0, rc, gc, bc, 0, 0, 0, 0, 0, 1, 0]));
+	}
+	
+	/**
+	 * Tweens the `alpha` and `strength` from 0
+	 * @param   time  The duration of the fade
+	 */
+	public function fadeIn(time:Float)
+	{
+		var waveEffect:FlxWaveEffect = cast this.effects[0];
+		var strength = waveEffect.strength;
+		// Fade effect in
+		// alpha = 0;
+		FlxTween.num(0.0, 1.0, time, (n)->
+		{
+			alpha = n;
+			waveEffect.strength = Math.floor(strength * FlxEase.circOut(Math.max(n - 0.5, 0) * 2));
+		});
+	}
+	
+	/**
+	 * Tweens the `alpha` and `strength` to 0
+	 * @param   time  The duration of the fade
+	 */
+	public function fadeOut(time:Float)
+	{
+		var waveEffect:FlxWaveEffect = cast this.effects[0];
+		var strength = waveEffect.strength;
+		// Fade effect in
+		// alpha = 1.0;
+		FlxTween.num(1.0, 0.0, time, (n)->
+		{
+			alpha = n;
+			waveEffect.strength = Math.floor(strength * FlxEase.circOut(Math.max(n - 0.5, 0) * 2));
+		});
+	}
+}
\ No newline at end of file
diff --git a/Tutorials/TurnBasedRPG/source/ui/HUD.hx b/Tutorials/TurnBasedRPG/source/ui/HUD.hx
new file mode 100644
index 000000000..b240d19a1
--- /dev/null
+++ b/Tutorials/TurnBasedRPG/source/ui/HUD.hx
@@ -0,0 +1,52 @@
+package ui;
+
+import flixel.FlxG;
+import flixel.FlxSprite;
+import flixel.group.FlxGroup.FlxTypedGroup;
+import flixel.text.FlxText;
+import flixel.util.FlxColor;
+import objects.Coin;
+
+using flixel.util.FlxSpriteUtil;
+
+class HUD extends FlxTypedGroup
+{
+	var healthCounter:FlxText;
+	var moneyCounter:FlxText;
+
+	public function new()
+	{
+		super();
+		
+		var healthIcon = new FlxSprite(4, 4, AssetPaths.health__png);
+		healthCounter = new FlxText(0, 0, 0, "3 / 3", 8);
+		healthCounter.setBorderStyle(SHADOW, FlxColor.GRAY, 1, 1);
+		healthCounter.x = healthIcon.x + healthIcon.width;
+		healthCounter.y = healthIcon.y + (healthIcon.height - healthCounter.height) / 2;
+		add(healthIcon);
+		add(healthCounter);
+		
+		var moneyIcon = new Coin(0, 4);
+		moneyIcon.solid = false;
+		moneyCounter = new FlxText(0, 0, 0, "00", 8);
+		moneyCounter.setBorderStyle(SHADOW, FlxColor.GRAY, 1, 1);
+		moneyIcon.x = FlxG.width - 4 - moneyIcon.width - moneyCounter.width;
+		moneyCounter.x = moneyIcon.x + moneyIcon.width;
+		moneyCounter.y = moneyIcon.y + (moneyIcon.height - moneyCounter.height) / 2;
+		moneyCounter.text = "0";
+		add(moneyIcon);
+		add(moneyCounter);
+		
+		forEach(function(sprite) sprite.scrollFactor.set(0, 0));
+	}
+
+	public function updateHealth(health:Int)
+	{
+		healthCounter.text = health + " / 3";
+	}
+	
+	public function updateMoney(money:Int)
+	{
+		moneyCounter.text = Std.string(money);
+	}
+}
diff --git a/Tutorials/TurnBasedRPG/source/ui/LargeText.hx b/Tutorials/TurnBasedRPG/source/ui/LargeText.hx
new file mode 100644
index 000000000..dd3f87120
--- /dev/null
+++ b/Tutorials/TurnBasedRPG/source/ui/LargeText.hx
@@ -0,0 +1,64 @@
+package ui;
+
+import flixel.FlxG;
+import flixel.math.FlxPoint;
+import flixel.math.FlxRect;
+import flixel.text.FlxBitmapFont;
+import flixel.text.FlxBitmapText;
+
+/**
+ * Note: This is not in the tutorial, the tutorial uses FlxText everywhere, which is easier to teach.
+ * Feel free to use this in your games
+ * Font created by Rick Hoppmann: https://tinyworlds.itch.io/free-pixel-font-thaleah
+ */
+@:forward
+abstract LargeText(FlxBitmapText) to FlxBitmapText
+{
+	static function getDefaultFont()
+	{
+		final graphic = FlxG.bitmap.add(AssetPaths.font__png);
+		final font = FlxBitmapFont.findFont(graphic.imageFrame.frame);
+		if (font != null)
+		{
+			return font;
+		}
+		
+		final chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!";
+		final widths = ['I'.code=>3, '!'.code=>3, 'T'.code=>7];
+		final defaultWidth = 8;
+		final defaultHeight = 8;
+		final font = FlxBitmapFont.fromMonospace(graphic, "", new FlxPoint(defaultWidth, defaultHeight));
+		
+		var x:Int = 0;
+		for (i in 0...chars.length)
+		{
+			final charCode = chars.charCodeAt(i);
+			final width = if (widths.exists(charCode)) widths[charCode] else defaultWidth;
+			final frame = FlxRect.get(x, 0, width, defaultHeight);
+			@:privateAccess
+			font.addCharFrame(charCode, frame, FlxPoint.weak(), width);
+			x += width;
+		}
+		return font;
+	}
+	
+	public function new (x = 0.0, y = 0.0, text = "", scale = 4)
+	{
+		this = new FlxBitmapText(x, y, text, getDefaultFont());
+		this.text = text;
+		this.autoUpperCase = true;
+		setScale(scale);
+	}
+	
+	function setScale(scale:Int)
+	{
+		this.scale.set(scale, scale);
+		this.updateHitbox();
+	}
+	
+	inline public function setBorderStyle(style, color = 0x0, size = 1, quality = 1)
+	{
+		this.setBorderStyle(style, color, size, quality);
+		this.updateHitbox();
+	}
+}
\ No newline at end of file
diff --git a/Tutorials/TurnBasedRPG/source/ui/OptionsSubState.hx b/Tutorials/TurnBasedRPG/source/ui/OptionsSubState.hx
new file mode 100644
index 000000000..a17242f2f
--- /dev/null
+++ b/Tutorials/TurnBasedRPG/source/ui/OptionsSubState.hx
@@ -0,0 +1,272 @@
+package ui;
+
+import flixel.FlxSprite;
+import flixel.FlxG;
+import flixel.addons.display.FlxSliceSprite;
+import flixel.group.FlxGroup;
+import flixel.group.FlxSpriteGroup;
+import flixel.math.FlxRect;
+import flixel.text.FlxText;
+import flixel.ui.FlxBar;
+import flixel.ui.FlxButton;
+import flixel.util.FlxColor;
+
+/**
+ * A SubState that pauses the main menu to show the options UI
+ */
+class OptionsSubState extends flixel.FlxSubState
+{
+	public function new ()
+	{
+		super();
+		
+		// black-out the main menu
+		var back = new FlxSprite();
+		back.makeGraphic(1, 1, 0x80000000);
+		back.setGraphicSize(FlxG.width, FlxG.height);
+		back.updateHitbox();
+		add(back);
+		
+		// add the UI
+		var ui = new OptionsUI(()->close());
+		add(ui);
+	}
+}
+
+/**
+ * The UI that controls options, extends `FlxSpriteGroup` which changes its members
+ * when certain properties are changed, namely `x`, `y`, `alpha` and `scrollFactor`.
+ */
+class OptionsUI extends FlxGroup
+{
+	
+	// define our screen elements
+	var musicBar:VolumeBar;
+	var soundBar:VolumeBar;
+	var fullscreenButton:FlxButton;
+	
+	public function new (onClose:()->Void)
+	{
+		super();
+		
+		if (FlxG.save.data.volumes == null)
+		{
+			initSave();
+		}
+		
+		// Make a background to visially separate the ui from the game underneath
+		var bg = new FlxSliceSprite(AssetPaths.uiback__png, new FlxRect(16, 16, 16, 16), 200, 160);
+		// Stretch the sections rather than tiling them for better performance
+		bg.stretchTop
+			= bg.stretchRight
+			= bg.stretchLeft
+			= bg.stretchBottom
+			= bg.stretchCenter
+			= true;
+		bg.screenCenter(XY);
+		add(bg);
+		
+		// place the group in the screen center, this will move the bg, and anything added later
+		
+		var titleText = new LargeText(bg.x, bg.y + 4, "Options", 2);
+		titleText.screenCenter(X);
+		add(titleText);
+		
+		var gap = 8;
+		var barX = bg.x + gap;
+		var barY = titleText.y + titleText.height + gap;
+		var barWidth = bg.width - gap * 2;
+		
+		musicBar = new VolumeBar(barX, barY, barWidth, "Music", FlxG.sound.defaultMusicGroup.volume, updateMusic);
+		add(musicBar);
+		barY += musicBar.height + gap;
+		
+		soundBar = new VolumeBar(barX, barY, barWidth, "Sound", FlxG.sound.defaultSoundGroup.volume, updateSound);
+		add(soundBar);
+		// barY += soundBar.height + gap;
+		
+		fullscreenButton = new Button(0, soundBar.y + soundBar.height + 8, FlxG.fullscreen ? "Windowed" : "Fullscreen", clickFullscreen);
+		fullscreenButton.screenCenter(X);
+		add(fullscreenButton);
+		
+		var clearDataButton = new Button(bg.x + 10, 0, "Clear Data", clickClearData);
+		clearDataButton.y = bg.y + bg.height - clearDataButton.height - 10;
+		add(clearDataButton);
+		
+		var backButton = new Button(0, clearDataButton.y, "Back", onClose);
+		backButton.x = bg.x + bg.width - backButton.width - 10;
+		add(backButton);
+	}
+	
+	function clickFullscreen()
+	{
+		fullscreenButton.text = FlxG.fullscreen ? "Windowed" : "Fullscreen";
+		FlxG.fullscreen = !FlxG.fullscreen;
+		FlxG.save.data.fullscreen = FlxG.fullscreen;
+		FlxG.save.flush();
+	}
+	
+	function initSave()
+	{
+		FlxG.save.data.volumes =
+		{
+			music: FlxG.sound.defaultMusicGroup.volume,
+			sound: FlxG.sound.defaultSoundGroup.volume
+		};
+		FlxG.sound.muted = false;
+		FlxG.sound.volume = 1.0;
+		FlxG.save.data.fullscreen = FlxG.fullscreen;
+		FlxG.save.flush();
+		trace(FlxG.save.data);
+	}
+	
+	/**
+	 * The user wants to clear the saved data - we just call erase on our save object and then reset the volume to .5
+	 */
+	function clickClearData()
+	{
+		FlxG.save.erase();
+		initSave();
+		musicBar.setVolume(0.5, true);
+		soundBar.setVolume(1.0, true);
+	}
+	
+	/**
+	 * Whenever we want to show the value of volume, we call this to change the bar and the amount text
+	 */
+	function updateMusic(volume:Float)
+	{
+		FlxG.sound.defaultMusicGroup.volume = volume;
+		FlxG.save.data.volumes.music = volume;
+		FlxG.save.flush();
+	}
+	
+	/**
+	 * Whenever we want to show the value of volume, we call this to change the bar and the amount text
+	 */
+	function updateSound(volume:Float)
+	{
+		FlxG.sound.defaultSoundGroup.volume = volume;
+		FlxG.save.data.volumes.sound = volume;
+		FlxG.save.flush();
+	}
+}
+
+class VolumeBar extends FlxSpriteGroup
+{
+	var bar:FlxBar;
+	var amountText:FlxText;
+	var label:String;
+	var onChange:(amount:Float)->Void;
+	
+	/**
+	 * 
+	 * @param   x 
+	 * @param   y 
+	 * @param   width 
+	 * @param   label 
+	 * @param   onChange 
+	 * @return  
+	 */
+	public function new (x:Float, y:Float, width:Float, label:String, volume:Float, onChange:(amount:Float)->Void)
+	{
+		this.label = label;
+		this.onChange = onChange;
+		super();
+		
+		// var label = new FlxText(0, 0, 0, label, 8);
+		// label.x = (width - label.width) / 2;
+		// add(label);
+		
+		// the volume buttons will be smaller than 'default' buttons
+		var downButton = new SmallButton(0, 0, "-", clickDown);
+		add(downButton);
+		
+		var upButton = new SmallButton(0, downButton.y, "+", clickUp);
+		upButton.x = width - upButton.width;
+		add(upButton);
+		
+		var barWidth = Std.int(width - (4 + upButton.width) * 2);
+		var barHeight = Std.int(upButton.height);
+		bar = new FlxBar(downButton.x + downButton.width + 4, downButton.y, LEFT_TO_RIGHT, barWidth, barHeight);
+		bar.createFilledBar(0xff464646, FlxColor.WHITE, true, FlxColor.WHITE);
+		add(bar);
+		
+		amountText = new FlxText(0, 0, 200, "100%", 8);
+		amountText.alignment = CENTER;
+		amountText.setBorderStyle(OUTLINE, 0xff464646);
+		amountText.x = bar.x + (bar.width - amountText.width) / 2;
+		amountText.y = bar.y + (bar.height - amountText.height) / 2;
+		add(amountText);
+		
+		//
+		this.x = x;
+		this.y = y;
+		setVolume(volume);
+	}
+	
+	function clickDown()
+	{
+		setVolumeHelper(bar.value - 10);
+	}
+	
+	function clickUp()
+	{
+		setVolumeHelper(bar.value + 10);
+	}
+	
+	function setVolumeHelper(volume:Float, dispatch = true)
+	{
+		bar.value = Math.round(volume); // Note: bar.value is automatically clamped between 0 and 100
+		amountText.text = label + " " + bar.value + "%";
+		
+		if (dispatch)
+		{
+			onChange(bar.value / 100);
+		}
+	}
+	
+	public function setVolume(volume:Float, dispatch = false)
+	{
+		setVolumeHelper(volume * 100, dispatch);
+	}
+	
+	override function destroy()
+	{
+		// remove references but do not destroy
+		bar = null;
+		amountText = null;
+		onChange = null;
+		
+		// this will actually destroy them
+		super.destroy();
+	}
+}
+
+/**
+ * Helper class for creating an 80x20 button with a custom graphic
+ */
+class Button extends FlxButton
+{
+	public function new (x, y, label, onClick)
+	{
+		super(x, y, label, onClick);
+		
+		loadGraphic(AssetPaths.button__png, true, 80, 20);
+		onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
+	}
+}
+
+/**
+ * Helper class for creating small 20x20 buttons
+ */
+class SmallButton extends FlxButton
+{
+	public function new (x, y, label, onClick)
+	{
+		super(x, y, label, onClick);
+		
+		loadGraphic(AssetPaths.small_button__png, true, 20, 20);
+		onUp.sound = FlxG.sound.load(AssetPaths.select__wav);
+	}
+}
\ No newline at end of file