changeset 212:ddfe0a211169

rpg: experiment with map teleport
author David Demelier <markand@malikania.fr>
date Mon, 16 Nov 2020 14:26:13 +0100
parents adcbb7ccfdee
children 8d4b9003f8d5
files examples/assets/maps/simple.json examples/assets/maps/village.json examples/assets/maps/village.png examples/example-map/CMakeLists.txt examples/example-map/main.c libadventure/adventure/molko.c libadventure/adventure/state/mainmenu.c libadventure/adventure/state/splashscreen.c libcore/core/action.c libcore/core/action.h librpg/CMakeLists.txt librpg/rpg/map-file.c librpg/rpg/map-file.h librpg/rpg/map.c librpg/rpg/map.h librpg/rpg/teleport.c librpg/rpg/teleport.h tools/map/map.c
diffstat 18 files changed, 624 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- a/examples/assets/maps/simple.json	Sun Nov 15 21:47:49 2020 +0100
+++ b/examples/assets/maps/simple.json	Mon Nov 16 14:26:13 2020 +0100
@@ -32,9 +32,37 @@
          "width":100,
          "x":0,
          "y":0
+        }, 
+        {
+         "draworder":"topdown",
+         "id":5,
+         "name":"actions",
+         "objects":[
+                {
+                 "height":32,
+                 "id":1,
+                 "name":"",
+                 "properties":[
+                        {
+                         "name":"exec",
+                         "type":"string",
+                         "value":"teleport:village"
+                        }],
+                 "rotation":0,
+                 "type":"",
+                 "visible":true,
+                 "width":67,
+                 "x":1372,
+                 "y":1300
+                }],
+         "opacity":1,
+         "type":"objectgroup",
+         "visible":true,
+         "x":0,
+         "y":0
         }],
- "nextlayerid":5,
- "nextobjectid":1,
+ "nextlayerid":6,
+ "nextobjectid":2,
  "orientation":"orthogonal",
  "properties":[
         {
@@ -45,7 +73,7 @@
         {
          "name":"origin-y",
          "type":"int",
-         "value":1344
+         "value":1350
         }, 
         {
          "name":"title",
@@ -473,7 +501,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -499,7 +526,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -525,7 +551,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -551,7 +576,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -577,7 +601,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -603,7 +626,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -629,7 +651,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -655,7 +676,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -681,7 +701,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -707,7 +726,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -733,7 +751,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -759,7 +776,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -785,7 +801,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -811,7 +826,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -837,7 +851,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -863,7 +876,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -889,7 +901,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -915,7 +926,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -941,7 +951,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -967,7 +976,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -993,7 +1001,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1019,7 +1026,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1045,7 +1051,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1071,7 +1076,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1097,7 +1101,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1123,7 +1126,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1149,7 +1151,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1175,7 +1176,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1201,7 +1201,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1227,7 +1226,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1253,7 +1251,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1279,7 +1276,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1355,7 +1351,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1381,7 +1376,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1407,7 +1401,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1433,7 +1426,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1459,7 +1451,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1485,7 +1476,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1511,7 +1501,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1537,7 +1526,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1613,7 +1601,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1639,7 +1626,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1665,7 +1651,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1691,7 +1676,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1717,7 +1701,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1743,7 +1726,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
@@ -1769,7 +1751,6 @@
                  "objectgroup":
                     {
                      "draworder":"index",
-                     "id":2,
                      "name":"",
                      "objects":[
                             {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/assets/maps/village.json	Mon Nov 16 14:26:13 2020 +0100
@@ -0,0 +1,104 @@
+{ "compressionlevel":-1,
+ "editorsettings":
+    {
+     "export":
+        {
+         "target":"."
+        }
+    },
+ "height":40,
+ "infinite":false,
+ "layers":[
+        {
+         "data":[241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 40, 41, 41, 41, 12, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 70, 71, 72, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241],
+         "height":40,
+         "id":1,
+         "name":"background",
+         "opacity":1,
+         "type":"tilelayer",
+         "visible":true,
+         "width":40,
+         "x":0,
+         "y":0
+        }, 
+        {
+         "data":[0, 0, 0, 0, 0, 0, 0, 53, 50, 43, 44, 45, 46, 47, 50, 50, 50, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 50, 50, 50, 50, 50, 50, 51, 80, 73, 74, 75, 76, 77, 139, 140, 141, 49, 50, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 80, 80, 80, 81, 80, 103, 104, 104, 104, 105, 169, 170, 171, 79, 80, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 80, 80, 80, 80, 80, 80, 81, 110, 133, 134, 134, 134, 135, 199, 200, 201, 79, 80, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 110, 110, 110, 110, 110, 110, 111, 0, 163, 164, 164, 164, 165, 0, 0, 0, 109, 110, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 194, 194, 194, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+         "height":40,
+         "id":2,
+         "name":"foreground",
+         "opacity":1,
+         "type":"tilelayer",
+         "visible":true,
+         "width":40,
+         "x":0,
+         "y":0
+        }, 
+        {
+         "draworder":"topdown",
+         "id":3,
+         "name":"actions",
+         "objects":[
+                {
+                 "height":32,
+                 "id":1,
+                 "name":"",
+                 "properties":[
+                        {
+                         "name":"exec",
+                         "type":"string",
+                         "value":"teleport:world"
+                        }],
+                 "rotation":0,
+                 "type":"",
+                 "visible":true,
+                 "width":96,
+                 "x":320,
+                 "y":608
+                }],
+         "opacity":1,
+         "type":"objectgroup",
+         "visible":true,
+         "x":0,
+         "y":0
+        }],
+ "nextlayerid":4,
+ "nextobjectid":2,
+ "orientation":"orthogonal",
+ "properties":[
+        {
+         "name":"origin-x",
+         "type":"int",
+         "value":352
+        }, 
+        {
+         "name":"origin-y",
+         "type":"int",
+         "value":544
+        }, 
+        {
+         "name":"title",
+         "type":"string",
+         "value":"Village useless"
+        }],
+ "renderorder":"right-down",
+ "tiledversion":"2020.10.30",
+ "tileheight":32,
+ "tilesets":[
+        {
+         "columns":30,
+         "firstgid":1,
+         "image":"village.png",
+         "imageheight":512,
+         "imagewidth":960,
+         "margin":0,
+         "name":"village",
+         "spacing":0,
+         "tilecount":480,
+         "tileheight":32,
+         "tilewidth":32
+        }],
+ "tilewidth":32,
+ "type":"map",
+ "version":1.4,
+ "width":40
+}
\ No newline at end of file
Binary file examples/assets/maps/village.png has changed
--- a/examples/example-map/CMakeLists.txt	Sun Nov 15 21:47:49 2020 +0100
+++ b/examples/example-map/CMakeLists.txt	Mon Nov 16 14:26:13 2020 +0100
@@ -23,18 +23,30 @@
 	OUTPUT
 		${example-map_BINARY_DIR}/simple.map
 		${example-map_BINARY_DIR}/simple.png
+		${example-map_BINARY_DIR}/village.map
+		${example-map_BINARY_DIR}/village.png
 	DEPENDS
 		$<TARGET_FILE:map>
 		${examples_SOURCE_DIR}/assets/maps/simple.json
 		${examples_SOURCE_DIR}/assets/maps/simple.png
+		${examples_SOURCE_DIR}/assets/maps/village.json
+		${examples_SOURCE_DIR}/assets/maps/village.png
 	COMMAND
 		$<TARGET_FILE:map>
 			< ${examples_SOURCE_DIR}/assets/maps/simple.json
 			> ${example-map_BINARY_DIR}/simple.map
 	COMMAND
+		$<TARGET_FILE:map>
+			< ${examples_SOURCE_DIR}/assets/maps/village.json
+			> ${example-map_BINARY_DIR}/village.map
+	COMMAND
 		${CMAKE_COMMAND} -E copy
 			${examples_SOURCE_DIR}/assets/maps/simple.png
 			${example-map_BINARY_DIR}/simple.png
+	COMMAND
+		${CMAKE_COMMAND} -E copy
+			${examples_SOURCE_DIR}/assets/maps/village.png
+			${example-map_BINARY_DIR}/village.png
 )
 
 molko_define_executable(
--- a/examples/example-map/main.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/examples/example-map/main.c	Mon Nov 16 14:26:13 2020 +0100
@@ -16,10 +16,16 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <core/alloc.h>
 #include <core/core.h>
 #include <core/event.h>
 #include <core/game.h>
 #include <core/image.h>
+#include <core/maths.h>
 #include <core/painter.h>
 #include <core/panic.h>
 #include <core/state.h>
@@ -31,50 +37,220 @@
 #include <rpg/map.h>
 #include <rpg/map-file.h>
 #include <rpg/rpg.h>
+#include <rpg/teleport.h>
 
 #include <assets/sprites/john.h>
 
 #define W       (1280)
 #define H       (720)
 
-static struct map_file loader;
-static struct map map;
+static struct action *load_action(struct map *, int, int, int, int, const char *);
+
+struct map_state {
+	struct map_file loader;
+	struct map map;
+	struct state state;
+};
 
 static struct texture john_texture;
 static struct sprite john_sprite;
 
 static void
-handle(struct state *st, const union event *ev)
+map_state_start(struct state *st)
 {
-	(void)st;
+	struct map_state *ms = st->data;
+
+	game.inhibit &= ~(INHIBIT_STATE_INPUT);
+
+	map_init(&ms->map);
+}
+
+static void
+map_state_update(struct state *st, unsigned int ticks)
+{
+	struct map_state *ms = st->data;
+
+	map_update(&ms->map, ticks);
+}
+
+static void
+map_state_handle(struct state *st, const union event *ev)
+{
+	struct map_state *ms = st->data;
 
 	switch (ev->type) {
 	case EVENT_QUIT:
 		game_quit();
 		break;
 	default:
-		map_handle(&map, ev);
+		map_handle(&ms->map, ev);
 		break;
 	}
 }
 
 static void
-update(struct state *st, unsigned int ticks)
+map_state_draw(struct state *st)
+{
+	struct map_state *ms = st->data;
+
+	painter_set_color(0xffffffff);
+	painter_clear();
+	map_draw(&ms->map);
+	painter_present();
+}
+
+static void
+map_state_finish(struct state *state)
+{
+	struct map_state *ms = state->data;
+
+	map_finish(&ms->map);
+	map_file_finish(&ms->loader);
+}
+
+static struct state *
+map_state_new(const char *name)
 {
-	(void)st;
+	char path[1024];
+	struct map_state *ms;
+
+	ms = alloc_zero(1, sizeof (*ms));
+	ms->loader.load_action = load_action;
+
+	snprintf(path, sizeof (path), "%s/%s.map", BINDIR, name);
+
+	if (!map_file_open(&ms->loader, path, &ms->map))
+		panic();
+
+	/* TODO: we may need to add a function in loader. */
+	ms->map.player_sprite = &john_sprite;
+
+	map_init(&ms->map);
+	ms->state.data = ms;
+	ms->state.start = map_state_start;
+	ms->state.handle = map_state_handle;
+	ms->state.update = map_state_update;
+	ms->state.draw = map_state_draw;
+	ms->state.finish = map_state_finish;
+
+	return &ms->state;
+}
 
-	map_update(&map, ticks);
+struct teleport_effect {
+	struct action act;
+	struct teleport tp;
+};
+
+static void
+teleport_effect_draw(struct action *act)
+{
+	struct teleport_effect *fx = act->data;
+
+	teleport_draw(&fx->tp);
+}
+
+static bool
+teleport_effect_update(struct action *act, unsigned int ticks)
+{
+	struct teleport_effect *fx = act->data;
+
+	return teleport_update(&fx->tp, ticks);
+}
+
+static void
+teleport_effect_finish(struct action *act)
+{
+	struct teleport_effect *fx = act->data;
+
+	teleport_finish(&fx->tp);
 }
 
 static void
-draw(struct state *st)
+teleport_effect(struct map *current_map, const char *name)
 {
-	(void)st;
+	struct teleport_effect *fx;
+
+	fx = alloc_zero(1, sizeof (*fx));
+	fx->tp.state = map_state_new(name);
+
+	fx->act.data = fx;
+	fx->act.draw = teleport_effect_draw;
+	fx->act.update = teleport_effect_update;
+	fx->act.finish = teleport_effect_finish;
+
+	/* Stop movement. */
+	current_map->player_movement = 0;
+
+	/* Player isn't allowed to move anymore. */
+	game.inhibit = INHIBIT_STATE_INPUT;
+
+	teleport_start(&fx->tp);
+	action_stack_add(&current_map->actions, &fx->act);
+}
+
+struct teleport_touch {
+	struct action act;
+	struct map *map;        /* Current map. */
+	char name[256];
+	int x;
+	int y;
+	unsigned int w;
+	unsigned int h;
+};
+
+static bool
+teleport_touch_update(struct action *act, unsigned int ticks)
+{
+	(void)ticks;
+
+	const struct teleport_touch *touch = act->data;
 
-	painter_set_color(0x000000ff);
-	painter_clear();
-	map_draw(&map);
-	painter_present();
+	const int x = touch->x - touch->map->player_sprite->cellw;
+	const int y = touch->y - touch->map->player_sprite->cellh;
+	const unsigned int w = touch->w + touch->map->player_sprite->cellw;
+	const unsigned int h = touch->h + touch->map->player_sprite->cellh;
+
+	if (maths_is_boxed(x, y, w, h, touch->map->player_x, touch->map->player_y)) {
+		teleport_effect(touch->map, touch->name);
+		return true;
+	}
+
+	return false;
+}
+
+static void
+teleport_touch_finish(struct action *act)
+{
+	free(act->data);
+}
+
+static struct action *
+teleport_touch_new(struct map *map, int x, int y, int w, int h, const char *name)
+{
+	struct teleport_touch *touch;
+
+	touch = alloc_zero(1, sizeof (*touch));
+	touch->map = map;
+	touch->x = x;
+	touch->y = y;
+	touch->w = w;
+	touch->h = h;
+	snprintf(touch->name, sizeof (touch->name), "%s", name);
+
+	touch->act.data = touch;
+	touch->act.update = teleport_touch_update;
+	touch->act.finish = teleport_touch_finish;
+
+	return &touch->act;
+}
+
+static struct action *
+load_action(struct map *map, int x, int y, int w, int h, const char *id)
+{
+	if (strncmp(id, "teleport:", 9) == 0)
+		return teleport_touch_new(map, x, y, w, h, id + 9);
+
+	return NULL;
 }
 
 static void
@@ -84,25 +260,15 @@
 		panic();
 	if (!window_open("Example - Map", W, H))
 		panic();
-	if (!map_file_open(&loader, BINDIR "/simple.map", &map))
-		panic();
-
 	if (!image_openmem(&john_texture, sprites_john, sizeof (sprites_john)))
 		panic();
 
 	sprite_init(&john_sprite, &john_texture, 48, 48);
-	map.player_sprite = &john_sprite;
-
-	if (!map_init(&map))
-		panic();
 }
 
 static void
 quit(void)
 {
-	map_finish(&map);
-	map_file_finish(&loader);
-
 	window_finish();
 	ui_finish();
 	core_finish();
@@ -111,13 +277,7 @@
 static void
 run(void)
 {
-	struct state state = {
-		.handle = handle,
-		.update = update,
-		.draw = draw
-	};
-
-	game_switch(&state, true);
+	game_switch(map_state_new("simple"), true);
 	game_loop();
 }
 
--- a/libadventure/adventure/molko.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/libadventure/adventure/molko.c	Mon Nov 16 14:26:13 2020 +0100
@@ -97,7 +97,6 @@
 	/* Init states. */
 	splashscreen_state(&molko.states[MOLKO_STATE_SPLASH], &molko.states[MOLKO_STATE_MAINMENU]);
 	mainmenu_state(&molko.states[MOLKO_STATE_MAINMENU]);
-	map_state(&molko.map, &molko.states[MOLKO_STATE_MAP]);
 
 	/* Start to splash. */
 	game_switch(&molko.states[MOLKO_STATE_SPLASH], true);
--- a/libadventure/adventure/state/mainmenu.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/libadventure/adventure/state/mainmenu.c	Mon Nov 16 14:26:13 2020 +0100
@@ -193,6 +193,7 @@
 	painter_draw_rectangle(
 	    main->texts[main->itemsel].x - 30,
 	    main->texts[main->itemsel].y + 11, 15, 15);
+	painter_present();
 }
 
 static void
--- a/libadventure/adventure/state/splashscreen.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/libadventure/adventure/state/splashscreen.c	Mon Nov 16 14:26:13 2020 +0100
@@ -91,6 +91,7 @@
 	painter_set_color(0xffffffff);
 	painter_clear();
 	texture_draw(&splash->tex, splash->x, splash->y);
+	painter_present();
 }
 
 static void
--- a/libcore/core/action.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/libcore/core/action.c	Mon Nov 16 14:26:13 2020 +0100
@@ -135,7 +135,7 @@
 }
 
 void
-action_stack_draw(struct action_stack *st)
+action_stack_draw(const struct action_stack *st)
 {
 	assert(st);
 
--- a/libcore/core/action.h	Sun Nov 15 21:47:49 2020 +0100
+++ b/libcore/core/action.h	Mon Nov 16 14:26:13 2020 +0100
@@ -215,7 +215,7 @@
  * \param st the stack
  */
 void
-action_stack_draw(struct action_stack *st);
+action_stack_draw(const struct action_stack *st);
 
 /**
  * Tells if there is any pending action in the stack.
--- a/librpg/CMakeLists.txt	Sun Nov 15 21:47:49 2020 +0100
+++ b/librpg/CMakeLists.txt	Mon Nov 16 14:26:13 2020 +0100
@@ -61,6 +61,8 @@
 	${librpg_SOURCE_DIR}/rpg/selection.h
 	${librpg_SOURCE_DIR}/rpg/spell.c
 	${librpg_SOURCE_DIR}/rpg/spell.h
+	${librpg_SOURCE_DIR}/rpg/teleport.c
+	${librpg_SOURCE_DIR}/rpg/teleport.h
 	${librpg_SOURCE_DIR}/rpg/walksprite.c
 	${librpg_SOURCE_DIR}/rpg/walksprite.h
 )
--- a/librpg/rpg/map-file.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/librpg/rpg/map-file.c	Mon Nov 16 14:26:13 2020 +0100
@@ -29,6 +29,7 @@
 #include <core/alloc.h>
 #include <core/error.h>
 #include <core/image.h>
+#include <core/trace.h>
 
 #include "map-file.h"
 
@@ -58,20 +59,11 @@
 }
 
 static bool
-parse_layer(struct parser *ps, const char *line)
+parse_tiles(struct parser *ps, const char *layer_name)
 {
-	char layer_name[32 + 1] = {0};
 	enum map_layer_type layer_type;
 	size_t amount, current;
 
-	/* Check if weight/height has been specified. */
-	if (ps->map->w == 0 || ps->map->h == 0)
-		return errorf("missing map dimensions before layer");
-
-	/* Determine layer type. */
-	if (sscanf(line, "layer|%32s", layer_name) <= 0)
-		return errorf("missing layer type definition");
-
 	if (strcmp(layer_name, "background") == 0)
 		layer_type = MAP_LAYER_TYPE_BACKGROUND;
 	else if (strcmp(layer_name, "foreground") == 0)
@@ -98,6 +90,47 @@
 }
 
 static bool
+parse_actions(struct parser *ps)
+{
+	char exec[128 + 1];
+	int x = 0, y = 0;
+	unsigned int w = 0, h = 0;
+
+	while (fscanf(ps->fp, "%d|%d|%u|%u|%128[^\n]\n", &x, &y, &w, &h, exec) == 5) {
+		struct action *act;
+
+		if (!ps->mf->load_action) {
+			tracef("ignoring action %d,%d,%u,%u,%s", x, y, w, h, exec);
+			continue;
+		}
+
+		if ((act = ps->mf->load_action(ps->map, x, y, w, h, exec)))
+			action_stack_add(&ps->map->actions, act);
+	}
+
+	return true;
+}
+
+static bool
+parse_layer(struct parser *ps, const char *line)
+{
+	char layer_name[32 + 1] = {0};
+
+	/* Check if weight/height has been specified. */
+	if (ps->map->w == 0 || ps->map->h == 0)
+		return errorf("missing map dimensions before layer");
+
+	/* Determine layer type. */
+	if (sscanf(line, "layer|%32s", layer_name) <= 0)
+		return errorf("missing layer type definition");
+
+	if (strcmp(layer_name, "actions") == 0)
+		return parse_actions(ps);
+
+	return parse_tiles(ps, layer_name);
+}
+
+static bool
 parse_tileset(struct parser *ps, const char *line)
 {
 	char filename[FILENAME_MAX + 1] = {0};
@@ -306,7 +339,6 @@
 	FILE *fp;
 	bool ret = true;
 
-	memset(file, 0, sizeof (*file));
 	memset(map, 0, sizeof (*map));
 
 	if (!(fp = fopen(path, "r")))
--- a/librpg/rpg/map-file.h	Sun Nov 15 21:47:49 2020 +0100
+++ b/librpg/rpg/map-file.h	Mon Nov 16 14:26:13 2020 +0100
@@ -62,6 +62,16 @@
  */
 struct map_file {
 	/**
+	 * (+?) User function to create an action when it is listed in the map
+	 * definition.
+	 *
+	 * The returned value is owned by the user and is never free'ed from the
+	 * map file itself. If the function is present and return a non-NULL
+	 * action it is added into map.
+	 */
+	struct action *(*load_action)(struct map *map, int x, int y, int w, int h, const char *exec);
+
+	/**
 	 * (*) Map title loaded from file.
 	 */
 	char title[MAP_FILE_TITLE_MAX];
--- a/librpg/rpg/map.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/librpg/rpg/map.c	Mon Nov 16 14:26:13 2020 +0100
@@ -140,7 +140,7 @@
 }
 
 static void
-start(struct map *map)
+init(struct map *map)
 {
 	map_repaint(map);
 
@@ -370,7 +370,8 @@
 
 	map->player_x += delta;
 
-	if (map->player_x < map->margin_x || map->player_x >= (int)(map->margin_x + map->margin_w))
+	if ((delta < 0 && map->player_x < map->margin_x) ||
+	    (delta > 0 && map->player_x >= (int)(map->margin_x + map->margin_w)))
 		map->view_x += delta;
 
 	if (map->view_x < 0)
@@ -393,7 +394,8 @@
 
 	map->player_y += delta;
 
-	if (map->player_y < map->margin_y || map->player_y >= (int)(map->margin_y + map->margin_h))
+	if ((delta < 0 && map->player_y < map->margin_y) ||
+	    (delta > 0 && map->player_y >= (int)(map->margin_y + map->margin_h)))
 		map->view_y += delta;
 
 	if (map->view_y < 0)
@@ -491,24 +493,6 @@
 	texture_finish(&colbox);
 }
 
-static void
-handle(struct state *state, const union event *ev)
-{
-	map_handle(state->data, ev);
-}
-
-static void
-update(struct state *state, unsigned int ticks)
-{
-	map_update(state->data, ticks);
-}
-
-static void
-draw(struct state *state)
-{
-	map_draw(state->data);
-}
-
 bool
 map_init(struct map *map)
 {
@@ -517,7 +501,7 @@
 	if (!texture_new(&map->picture, map->real_w, map->real_h))
 		return false;
 
-	start(map);
+	init(map);
 
 	return true;
 }
@@ -545,6 +529,8 @@
 {
 	assert(map);
 
+	action_stack_update(&map->actions, ticks);
+
 	move(map, ticks);
 }
 
@@ -565,6 +551,8 @@
 		map->player_x - map->view_x,
 		map->player_y - map->view_y);
 
+	action_stack_draw(&map->actions);
+
 	/* Draw collide box around player if requested. */
 	if (map->flags & MAP_FLAGS_SHOW_COLLIDE &&
 	    texture_new(&box, map->player_sprite->cellw, map->player_sprite->cellh)) {
@@ -591,23 +579,11 @@
 }
 
 void
-map_state(struct map *map, struct state *state)
-{
-	assert(map);
-	assert(state);
-
-	memset(state, 0, sizeof (*state));
-	state->data = map;
-	state->draw = draw;
-	state->handle = handle;
-	state->update = update;
-}
-
-void
 map_finish(struct map *map)
 {
 	assert(map);
 
+	action_stack_finish(&map->actions);
 	texture_finish(&map->picture);
 
 	memset(map, 0, sizeof (*map));
--- a/librpg/rpg/map.h	Sun Nov 15 21:47:49 2020 +0100
+++ b/librpg/rpg/map.h	Mon Nov 16 14:26:13 2020 +0100
@@ -26,6 +26,7 @@
 
 #include <stddef.h>
 
+#include <core/action.h>
 #include <core/texture.h>
 
 #include "walksprite.h"
@@ -96,6 +97,8 @@
 	/* View options. */
 	enum map_flags flags;           /*!< (+) View options. */
 
+	struct action_stack actions;    /*!< (+) */
+
 	/* Player. */
 	struct sprite *player_sprite;   /*!< (+) The sprite to use */
 	struct walksprite player_ws;    /*!< (-) Walking sprite for moving the player. */
@@ -175,23 +178,6 @@
 map_repaint(struct map *map);
 
 /**
- * Convert the map into a game state.
- *
- * Both objects must exist until the state is no longer used.
- *
- * \pre map != NULL
- * \pre state != NULL
- * \param map the map to use
- * \param state the state to fill
- * \post state->data is set to map
- * \post state->handle is set
- * \post state->update is set
- * \post state->draw is set
- */
-void
-map_state(struct map *map, struct state *state);
-
-/**
  * Dispose map resources.
  *
  * \pre map != NULL
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/teleport.c	Mon Nov 16 14:26:13 2020 +0100
@@ -0,0 +1,115 @@
+/*
+ * teleport.c -- animate and teleport
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <core/action.h>
+#include <core/game.h>
+#include <core/painter.h>
+#include <core/panic.h>
+#include <core/window.h>
+
+#include "map.h"
+#include "teleport.h"
+
+static bool
+update(struct action *act, unsigned int ticks)
+{
+	return teleport_update(act->data, ticks);
+}
+
+static void
+draw(struct action *act)
+{
+	teleport_draw(act->data);
+}
+
+static void
+finish(struct action *act)
+{
+	teleport_finish(act->data);
+}
+
+void
+teleport_start(struct teleport *tp)
+{
+	assert(tp);
+
+	if (!texture_new(&tp->fade, window.w, window.h))
+		panic();
+
+	texture_set_blend_mode(&tp->fade, TEXTURE_BLEND_BLEND);
+
+	PAINTER_BEGIN(&tp->fade);
+	painter_set_color(0x000000ff);
+	painter_clear();
+	PAINTER_END();
+
+	tp->elapsed = tp->alpha = 0;
+}
+
+bool
+teleport_update(struct teleport *tp, unsigned int ticks)
+{
+	assert(tp);
+
+	tp->elapsed += ticks;
+
+	if (tp->elapsed >= 10) {
+		if (tp->alpha >= 255) {
+			game_switch(tp->state, false);
+			return true;
+		}
+
+		tp->elapsed = 0;
+		tp->alpha += 5;
+	}
+
+	return false;
+}
+
+void
+teleport_draw(struct teleport *tp)
+{
+	texture_set_alpha_mod(&tp->fade, tp->alpha);
+	texture_draw(&tp->fade, 0, 0);
+}
+
+void
+teleport_finish(struct teleport *tp)
+{
+	assert(tp);
+
+	texture_finish(&tp->fade);
+}
+
+void
+teleport_action(struct teleport *tp, struct action *act)
+{
+	assert(tp && tp->state);
+	assert(act);
+
+	memset(act, 0, sizeof (*act));
+	act->data = tp;
+	act->update = update;
+	act->draw = draw;
+	act->finish = finish;
+
+	teleport_start(tp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/librpg/rpg/teleport.h	Mon Nov 16 14:26:13 2020 +0100
@@ -0,0 +1,57 @@
+/*
+ * teleport.h -- animate and teleport
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_RPG_TELEPORT_H
+#define MOLKO_RPG_TELEPORT_H
+
+/**
+ * \file teleport.h
+ * \brief Animate screen and teleport.
+ */
+
+#include <stdbool.h>
+
+#include <core/texture.h>
+
+struct action;
+struct map;
+struct state;
+
+struct teleport {
+	struct state *state;
+	unsigned int elapsed;
+	unsigned int alpha;
+	struct texture fade;
+};
+
+void
+teleport_start(struct teleport *tp);
+
+bool
+teleport_update(struct teleport *tp, unsigned int ticks);
+
+void
+teleport_draw(struct teleport *tp);
+
+void
+teleport_finish(struct teleport *tp);
+
+void
+teleport_action(struct teleport *tp, struct action *act);
+
+#endif /* !MOLKO_RPG_TELEPORT_H */
--- a/tools/map/map.c	Sun Nov 15 21:47:49 2020 +0100
+++ b/tools/map/map.c	Mon Nov 16 14:26:13 2020 +0100
@@ -44,7 +44,7 @@
 {
 	return strcmp(name, "background") == 0 ||
 	       strcmp(name, "foreground") == 0 ||
-	       strcmp(name, "objects") == 0;
+	       strcmp(name, "actions") == 0;
 }
 
 static const json_t *
@@ -66,6 +66,33 @@
 	return NULL;
 }
 
+static const json_t *
+find_action_exec(const json_t *props)
+{
+	assert(json_is_array(props));
+
+	json_t *prop, *name, *value;
+	size_t i;
+
+	json_array_foreach(props, i, prop) {
+		if (!json_is_object(prop))
+			die("invalid property in object\n");
+
+		name = json_object_get(prop, "name");
+		value = json_object_get(prop, "value");
+
+		if (!name || !json_is_string(name))
+			die("invalid 'name' property in object properties");
+		if (!value || !json_is_string(value))
+			die("invalid 'value' property in object properties");
+
+		if (strcmp(json_string_value(name), "exec") == 0)
+			return value;
+	}
+
+	return NULL;
+}
+
 static void
 write_title(const json_t *props)
 {
@@ -131,75 +158,38 @@
 }
 
 static void
-write_object_property(int id, const json_t *property)
-{
-	assert(json_is_object(property));
-
-	json_t *name = json_object_get(property, "name");
-	json_t *type = json_object_get(property, "type");
-	json_t *value = json_object_get(property, "value");
-
-	if (!name || !json_is_string(name))
-		die("invalid 'name' property in object");
-	if (!type || !json_is_string(type))
-		die("invalid 'type' property in object");
-	if (!value || !json_is_string(value))
-		die("invalid 'value' property in object");
-
-	printf("object-property|%d|%s|%s\n",
-	    id,
-	    json_string_value(name),
-	    json_string_value(value)
-	);
-}
-
-static void
 write_object(const json_t *object)
 {
 	assert(json_is_object(object));
 
-	json_t *id = json_object_get(object, "id");
 	json_t *x = json_object_get(object, "x");
 	json_t *y = json_object_get(object, "y");
 	json_t *width = json_object_get(object, "width");
 	json_t *height = json_object_get(object, "height");
-	json_t *type = json_object_get(object, "type");
 	json_t *props = json_object_get(object, "properties");
+	const json_t *exec;
 
-	if (!id || !json_is_integer(id))
-		die("invalid 'id' property in object\n");
-	if (!x || !json_is_real(x))
+	if (!x || !json_is_number(x))
 		die("invalid 'x' property in object\n");
-	if (!y || !json_is_real(y))
+	if (!y || !json_is_number(y))
 		die("invalid 'y' property in object\n");
-	if (!width || !json_is_real(width))
+	if (!width || !json_is_number(width))
 		die("invalid 'width' property in object\n");
-	if (!height || !json_is_real(height))
+	if (!height || !json_is_number(height))
 		die("invalid 'height' property in object\n");
-	if (!type || !json_is_string(type))
-		die("invalid 'type' property in object\n");
 
 	/* In tiled, those properties are float but we only use ints in MA */
-	printf("object|%lld|%s|%d|%d|%d|%d\n",
-	    json_integer_value(id),
-	    json_string_value(type),
-	    (int)json_real_value(x),
-	    (int)json_real_value(y),
-	    (int)json_real_value(width),
-	    (int)json_real_value(height)
+	printf("%d|%d|%d|%d|",
+	    (int)json_integer_value(x),
+	    (int)json_integer_value(y),
+	    (int)json_integer_value(width),
+	    (int)json_integer_value(height)
 	);
 
-	if (json_is_array(props)) {
-		json_t *prop;
-		size_t index;
+	if ((exec = find_action_exec(props)))
+		printf("%s", json_string_value(exec));
 
-		json_array_foreach(props, index, prop) {
-			if (!json_is_object(prop))
-				die("invalid property in object\n");
-
-			write_object_property(json_integer_value(id), prop);
-		}
-	}
+	printf("\n");
 }
 
 static void