Spherical forums

General Discussion => Hellos and Byes => Topic started by: Rahkiin on March 06, 2014, 09:43:18 am

Title: I say Hello, you say...
Post by: Rahkiin on March 06, 2014, 09:43:18 am
Hi guys!

I would like to use this opportunity... oh lets cut the formal crap: I am Rahkiin! A 19y old CS student from Delft, The Netherlands.

Then
I came to know Sphere when I had an game-dev assignment in my last year of high school. With three other guys, I made a game I was, and still am, very proud of. For future reference: the game was called Scala World, and was all in dutch. Although we did not know Sphere at all, we were very able to use the Sphere Editor and as I am quite good in programming, I picked up the JavaScript library pretty quick.
We used the map engine for the Pokemon-styled (literally, we used their tileset) game and programatically I added some nice tools, that are worth explaining, I think :)

Obstruction and animation
The roles in my group were divided perfectly: I was the programmer, one mate made the story and quests, another made spritesets and the fourth made the maps. Now I wanted to make the programmer able to do very easy obstruction mapping (non enterable blocks because a bench is there, but also doors you can only walk into (one-direction obstruction) and animation placing (eg, when jumping a ridge). To do this, I added two invisible layers to the map and obstruction tiles to my tileset, and I programmed the tile-based 4-way single-step (like Pokemon) movement with the obstruction layer as obstruction logic. And it worked very well! The map-designer could now create the maps all himself.

I also used the obstruction tiles for random person movement (NPCs). It is much faster than obstruction-segment collision matching.

I did use Map Zones, but just for Quest activation.

Portals
Our game would cover the whole building of my school, with every room in it. Most class rooms would just contain some walking students and the teacher assigned to that room/course.
Other classrooms would contain a teacher giving quests, in turn giving either keys or other objects. (The ultimate goal was to obtain the 3 elemental keys, open a dungeon and beat the boss (the master school bully) by answering questions about your adventures in the school).

This all meant we would have a lot of portals between rooms. And I did not want to program each of them by hand. So I created a portal system: blue and orange portals. A door entity placed where the doors are and upon collision, the player is moved to the linked door, in the right map, standing in the right direction. The portals were linked automatically: the portal in the corridor did not need to know the location of its opposing portal. Instead, they just both gave a link-name and a direction the player should be facing when returning. A script opened all maps and looked for all portals. A slow process, but with a portal-cachefile this was only done when changing any portals.
Result of all this in combination with a persistence script I found on the wiki: maps can be linked by placing entities and a small line in the entities touch-code: Portal('PortalName','east'). Even my map-designer could handle that!

I might upload the code somewhere, but I learned much last years and it ain't very clean code anymore (deadlines and all that).

Now
My current activities with sphere are somewhat different from writing a game. I am actually writing a Sphere Runtime for Mac OSX. (I am very Mac-ish these days, although I do have a gaming computer somewhere). I also went to write a native OSX Sphere DevKit, but this is on hold until I have my runtime (I tend to overcomplicate things, so I started with a full plugin-based environment. I think you guys know what I mean :P).

I want my runtime to have a couple of new features and functionalities. But in the end, I also want to be compatible with existing games.



If you are anything into OSX and iOS dev, you might notice these are all available on OSX natively, including JSC (since 10.9). That is great; what's also great is that even JSC is available on iOS7, so I could 'port' the runtime to iOS. And I think I could even convince Apple to put it in the App Store, because it is JavaScript and a Zip of resource files for a game, not some native code downloaded from the internet...  :D

Also, even though I wrote JavaScriptCore there, I have been writing an Objective-C wrapper very much alike JavaScriptCore for V8 (Flying Jester: I used the new Handle code  ;)), called L8. I do not trust it memory-wise though. And there is a problem is converting the C blocks to functions, and around. Quite a piece of code. So I resigned to JSC. (When I started this project, OSX 10.9 was not released yet).

That aside. The features:

All new class-based Sphere library
(With ECMAScript 1.6 (JS6) in mind, modules yeah!). This means a lot of 'classes', and no more function such as LoadFont(): instead there is the new Font() (create font) and new Font('font.rfn'). This is a lot nicer for me because of the JS-ObjC mapping done by JSC/L8.

Sphere compatibility with a Shim
Of course it should have everything Sphere has, but mostly via a shim.

Game libraries
I want to design the concept of a library for Sphere. Especially with the JavaScript modules (ES6) in mind. Any thoughts or comments are welcome. I think it would clean up a game, especially when using big libraries like Radlib. I am thinking about a folder with an extension (OSX-like), not zipped, but placed in a libraries folder. Then lib-files and your own files will not be mixed.

Native OSX game packages
This I find very awesome: I want to pack the runtime in an .app package and load the game into it. Then add some much more info to the game information file (actually, get rid of .sgm and make some .plist) to also include game icon and such. Then there is this simple .app package, such as Blockmap.app with the Blockmap icon, and double-clicking it opens the game directly. Save-files are stored in the user-data location and the app actually shows up with the correct icon in the dock. I would really prefer such package over sending someone a Zip with a bunch of files in it :)
I have no Idea how easy, hard or impossible such thing is for Windows people. (Exe can contain resources though).

My plans
As I said above, I am planning on making the dev environment. Just because I think Sphere is very awesome! I also plan to hang out on these forums.

As far as I can tell, I am speaking to just a couple of people here: Radnen, Flying Jester, NEO, Mooch, DaVince and Lord English. And Harry Bo. But that's cool :)
Please, do not tell me I waste my time here. Don't we all? ;D

So that is me, what i've done, what I do and what I plan to do with sphere. Any comments and ideas are welcome!


// Rahkiin
Title: Re: I say Hello, you say...
Post by: Radnen on March 06, 2014, 03:38:20 pm
Hi Rahkiin! Well, that was a lot of stuff you said. I've seen Sphere used for class projects too before and they usually do very well. I'm glad you picked up Sphere and saw how easy it was to use. :)

Making a Sphere port/version for OSX is not going to be too easy and I wish you the best of luck. :)

I am interested in this portal system of yours. So instead of typing in map names you just type in a "warp-id" and it goes straight there? That's extremely neat, practical, and useful. I never seen this in all my years with Sphere. Mainly because I thought the map name was good enough. But it also seems that the portals do even more and set the x/y of the person too. I like this system very much!

Oh, and sorry if you expected a larger community. Sphere definitely has it's ups and downs. What I'm hoping is that with new Sphere engines we can build up a community of newer programmers. One core issue with Sphere is that it's nearly twenty years old! (17 to be exact).

See you around!
Title: Re: I say Hello, you say...
Post by: Rahkiin on March 06, 2014, 05:25:39 pm

Making a Sphere port/version for OSX is not going to be too easy and I wish you the best of luck. :)

I would like the hear if you know of any who tried it and what they walked up against. The troubles. So I can either solve, circumvent or prevent them. Or I could stop with the project all together  :P


I am interested in this portal system of yours. So instead of typing in map names you just type in a "warp-id" and it goes straight there? That's extremely neat, practical, and useful. I never seen this in all my years with Sphere. Mainly because I thought the map name was good enough. But it also seems that the portals do even more and set the x/y of the person too. I like this system very much!


Thanks! And yes, that is what it does. I will write some tutorial on it and maybe release some code. But at this moment I am unaware of the state of that exact piece of code nor its ability to be standalone from the persistence.js I used.

What it essentially does when creating the portal-links:


In the end, there should be a list of IDs with matching portals. Now when a portal is hit by the player, it is activated. The matching portal is looked up, the map is loaded, X&Y is set and the direction is set. It is actually quite simple. Except that the direction also changes the actual X&Y (cant place the player on top of the portal as that directly triggers another warp and other of such small details).


Oh, and sorry if you expected a larger community. Sphere definitely has it's ups and downs. What I'm hoping is that with new Sphere engines we can build up a community of newer programmers. One core issue with Sphere is that it's nearly twenty years old! (17 to be exact).


Oh I don't mind! I think it is awesome to have a core group that have a good community together. I think there are many users of Sphere who don't register at all and just look at the posts in the Wiki and on the forums, never intending to reply or write anything. But that's ok.

If Sphere is already so old, could I push forward to Sphere 1.7 with a more objective oriented programming library? :D

I forgot to add a special feature to the OP, and I will add it now. The feature I want to add is Bonjour networking: giving games the opportunity to announce themselves to the LAN and to find peers. Easy for multiplayer games. Simply do an announce('player_name') and the Bonjour will do the work using the game name and the player name. Then list() will get some list of current players who are announcing. I think this will work and I also think it will make people want multiplayer network games as it is so easy to do. (No need to find your IP and give it to someone etc).

// Rahkiin
Title: Re: I say Hello, you say...
Post by: Radnen on March 06, 2014, 05:39:26 pm
Do you mind if I made my own portal system? :)


If Sphere is already so old, could I push forward to Sphere 1.7 with a more objective oriented programming library? :D

I forgot to add a special feature to the OP, and I will add it now. The feature I want to add is Bonjour networking: giving games the opportunity to announce themselves to the LAN and to find peers. Easy for multiplayer games. Simply do an announce('player_name') and the Bonjour will do the work using the game name and the player name. Then list() will get some list of current players who are announcing. I think this will work and I also think it will make people want multiplayer network games as it is so easy to do. (No need to find your IP and give it to someone etc).

// Rahkiin


Well, you shouldn't call it 1.7, unless you are working off the existing code base. If it's a Sphere rewrite I'd call it something else, like how TurboSphere and SphereSFML are named. They are still considered in the 'Sphere' family of engines. I like this Bonjour thing, networking always was tedious to set up in Sphere and Sphere aims to be user friendly in creating games with a scripting language.

One last remark: Sphere had OSX ports and Neologix can say more about that than me. The best I could say is it's a port of the old codebase, and used SDL for the graphics backend.
Title: Re: I say Hello, you say...
Post by: Rahkiin on March 06, 2014, 05:43:47 pm
Alright, but I was just hinting to a new improvement of sphere, not specially to an update of the original sphere. :) Are there any interests in such an OO update? (You did not actually answer that question :P)

I am currently using the existing codebase for how sphere handles stuff. Currently I only implemented all file formats into my own implementation. The codebase is the only source for that. Beside the codebase, there ain't much (none) documentation. Also, it is not a rewrite as it is in ObjC.

I will look more into the Bonjour thing. Is there anything else any of you want into sphere, that I could tryout in my own runtime?

Ping NeoLogiX :D

// Rahkiin
Title: Re: I say Hello, you say...
Post by: Radnen on March 06, 2014, 05:53:57 pm

Alright, but I was just hinting to a new improvement of sphere, not specially to an update of the original sphere. :) Are there any interests in such an OO update? (You did not actually answer that question :P)


As outlined in Developing a Sphere-compatible engine (http://wiki.spheredev.org/Developing_a_Sphere-compatible_engine), there is interest in an OO update, TurboSphere does this already, but you should ideally create a shim. A piece of code that does this essentially,

Code: (javascript) [Select]

function CreateColor(r, g, b, a) {
    return new Color(r, g, b, a);
}

function LoadImage(filename) {
    return new Image(filename);
}

//...



I am currently using the existing codebase for how sphere handles stuff. Currently I only implemented all file formats into my own implementation. The codebase is the only source for that. Beside the codebase, there ain't much (none) documentation. Also, it is not a rewrite as it is in ObjC.


Well, if you are doing it in Obj-C, it is a rewrite since Sphere is done in C++.


I will look more into the Bonjour thing. Is there anything else any of you want into sphere, that I could tryout in my own runtime?


One thing that gets requested a lot and what would separate your engine from others is an isometric map engine. I've wanted to do that, but I'm working on like everything else first. But that is definitely something. Another idea is the 'shallow' or 'basic' map engine, one that has the ability to code it however you want and doesn't necessarily use tiles like the current map engine does. This would hopefully be good for the more adventurous coders looking to create platformers or any other kind of game that doesn't use the old map engine.
Title: Re: I say Hello, you say...
Post by: Rahkiin on March 06, 2014, 06:05:00 pm

As outlined in Developing a Sphere-compatible engine (http://wiki.spheredev.org/Developing_a_Sphere-compatible_engine), there is interest in an OO update, TurboSphere does this already, but you should ideally create a shim. A piece of code that does this essentially,

Code: (javascript) [Select]

function CreateColor(r, g, b, a) {
    return new Color(r, g, b, a);
}

function LoadImage(filename) {
    return new Image(filename);
}

//...



Yes I read about this, and my OP said it would have a shim. I will look into TS and its OO approach.


Well, if you are doing it in Obj-C, it is a rewrite since Sphere is done in C++.


But my 'rewrite' would not work on any other platform than iOS and OSX :P So much for an improvement. I will just have to come up with some nice fancy name for my runtime then :D


One thing that gets requested a lot and what would separate your engine from others is an isometric map engine. I've wanted to do that, but I'm working on like everything else first. But that is definitely something. Another idea is the 'shallow' or 'basic' map engine, one that has the ability to code it however you want and doesn't necessarily use tiles like the current map engine does. This would hopefully be good for the more adventurous coders looking to create platformers or any other kind of game that doesn't use the old map engine.


Isometric map engine, isn't that basically either morphing the tiles or using isomorphic tiles, and drawing everything in the correct back-to-front order?

Combining both isomorphic and shallow/basic map engine sounds like an adventure.


// Rahkiin
Title: Re: I say Hello, you say...
Post by: N E O on March 06, 2014, 07:13:32 pm
Re OSX port: Rhuan did pretty much all the work of actually making a working xcodeproj for Sphere and getting working versions of the necessary frameworks; my part was mainly making sure it compiled on Intel since he did it on PPC before it was eventually available in a Universal Binary.

I don't currently have access to my MacBook Pro but I've been meaning to re-upload the last version of the Mac source and the last UB since the reboot; in its current state it does require a bit of..."configuration." Still, it's nice to see people are still interested in a native Mac version of Sphere :D
Title: Re: I say Hello, you say...
Post by: Rahkiin on March 06, 2014, 07:20:03 pm
So the troubles was just that you guys had no idea how to work with Xcode, plus the libraries you used were not easily available for Mac? Because I intend to have a very small dependency list :) (Can't have dynamic libraries on iOS, so if I ever want to publish to the app store, I must be able to link statically. Something many licenses do now allow. *Ahum GPL*).

I browsed through the forums and through some TurboSphere code, and I think I should be doing OpenGL after all. Luckily it's builtin to OSX, iOS and Xcode :) Unfortunately, I suck at it.

// Rahkiin

PS: I have created an on-topic discussion about the engine here: http://forums.spheredev.org/index.php/topic,1146.0.html (http://forums.spheredev.org/index.php/topic,1146.0.html)
Title: Re: I say Hello, you say...
Post by: DaVince on March 08, 2014, 02:07:30 pm
Hallo! You just got here, write 29 forum posts and do all these things like write a new, backwards compatible runtime? Man, you're awesome. Welcome to Spherical. :)
Title: Re: I say Hello, you say...
Post by: Rahkiin on March 08, 2014, 02:11:15 pm

Hallo!

Hi fellow dutchman!


write 29 forum posts

One can call it spamming  ::)


and do all these things like write a new, backwards compatible runtime? Man, you're awesome. Welcome to Spherical. :)

Yeah. Crazy, isn't it? It will be a coding overhaul but with an impossible amount of shims :)

And thanks!

// Rahkiin
Title: Re: I say Hello, you say...
Post by: Harry Bo21 on March 10, 2014, 02:11:20 am
Quote
As far as I can tell, I am speaking to just a couple of people here: Radnen, Flying Jester, NEO, Mooch, DaVince and Lord English.


And Harry Bo! (I go quiet from time to time, but I am always lurking here :))

Welcome to sphere dude, sounds like you will fit in great!
Title: Re: I say Hello, you say...
Post by: Rahkiin on March 10, 2014, 02:16:11 am
Hi! Added you :P

Yeah, I guess I will :)
Title: Re: I say Hello, you say...
Post by: Legaia on March 20, 2014, 01:13:53 pm
G'day! I'm more of a lurker as of late as well. Always glad to see new members though! Welcome :)
Title: Re: I say Hello, you say...
Post by: FBnil on June 07, 2015, 02:43:17 pm
More than a year late, but still: Welkom Rahkiin! Good to see you have the drive we had in the past.
Title: Re: I say Hello, you say...
Post by: FBnil on June 07, 2015, 06:41:39 pm
Love the portal idea, maybe you can check my teleporthica demo (type it in in google, gets you 1 anwser). Here is the API. It is fairly complete.
It has that same thing you were talking about classrooms. You can define 1 empy room, and make a hallway jump to that empty room each time, but you can give each room a different title, so it does not feel like it is the same room. And then use a WarpBack() function to warp to where you warped from, getting to the right spot of the hallway.
One thing I now, after reading your text, want to do is to automatically calculate the warp back offset (to not warp onto the trigger). Warp delay was for that, but I think the latter is better now (will be a setting of course)


Code: [Select]

//___________________________________
//                                   \
// Teleporthica MapChange Engine.  version 1.3 $Id: teleporthica.js 108 2008-07-18 21:23:47Z nilton $
//___________________________________/
/*
To change to a map, when you want several entry points is impossible with ChangeMap() only.
You may only need this:

RequireScript("teleporthica.js");

And then you can use:

WarpXY(rmpfilename,x,y,tonewlayer); //(x,y) in Pixels
or
WarpTo(rmpfilename,tx,ty,tonewlayer); //(tx,ty) in Tiles

The values of x, y and layer will be taken from the EntryPoint if ommited.
But to get more functionality, you need to set up some things.


*** SHORTWRITES ***

Shortwrite the Warp() and WarpXY() functions, now they are easy to call,
these functions are automatically created for you:

Warp = function(r,p,t,r,i,f) {return MapChange.warp(r,p,t,r,i,f);}
WarpXY = function(r,x,y,l,t,r,i,f) {return MapChange.warpXY(r,x,y,l,t,r,i,f);}
WarpTo = function(r,tx,ty,l,t,r,i,f) {return MapChange.warpTo(r,tx,ty,l,t,r,i,f);}
WarpBack = function(retrigger,msg,run,fade) {return MapChange.warpBack(retrigger,msg,run,fade);}
SeamlessWarpXY = function(new_map,new_XX,new_YY) {MapChange.SeamlessWarpXY(new_map,new_XX,new_YY);}

(For the parameters you can use, see the real functions below in the code)

*** WARP points ***

Inside the Entry script of a map (or in the mapEnter function, if using BindScript),
set the mapname and WARP points:

WARP = {
mapname: "How did I end up here?", // The name of the map
NorthernEntry : {
tx: 52,
ty: 21,
  run: "ResetPuzzle(1)",
face: COMMAND_FACE_NORTH,
name: "Alternative map name" // Overrides mapname
}, 
StairsDownToCatacombs: { tx:46, ty:22, dx:8 },
Upstairs: { x:162, y:230, layer:2}
GoingUp: { tx:null, ty:-1 }
}


where tx and ty are tile coordinates (not pixels!), x and y also exist, for the ones that
want pixelperfect placement. dx and dy exist to offset by pixels. If the warppoint does not exist,
then Warp() will default to the entrypoint. The layer has to be specified only if you need to
warp to another layer than the entrypoint has. run is for when you also need to execute something
complex like a sound. face runs QueuePersonCommand(person,<face>, true); to face a direction.
Each value can be ommited, in that case, the defaults will be taken from the entrypoint.
if tx, x, ty, y or layer is undefined, then that value will be taken from the entrypoint.
if tx, x, ty, y or layer is null, then that value will be taken from the previous map.
if tx, x, ty, y or layer are -1, then that maximum possible value will be used (minus 1).

Then, at a place in any map where you want an exitpoint, define a trigger with 
Warp(rmpfilename,warppoint,titlemsg,run,wbinfo,fade) for example:

Warp('mymap.rmp','NorthernEntry');

Or, you can also use the classical:

WarpXY(rmpfilename,x,y,tonewlayer,titlemsg,run,warpbackInfoObject)

These (x,y) are in pixels, use WarpTo() when specifying tiles (the function has
the same order of parameters).

WarpTo(rmpfilename,tx,ty,tonewlayer,titlemsg,run,warpbackInfoObject)

In the end, everything is optional and you can only use: WarpTo('level2.rmp',1,10);

titlemsg will override any set mapname
run Additional command you want to run, after the map change and the repositioning of the input person and after the run defined in the warppoint.
wbinfo Is the WarpBack info, I'll explain that in a moment
fade during this warp, use this fade.

*** Special warp functions ***

If you need to fake you're entering the map you are currently in (to reset the puzzles on
the map, for example) then use:

MapChange.warpRestart();

If you need to warp back to the map, just before you came to the current map
(for example: to return from a cutscene), use:

WarpBack();

If you are unsure if the WarpBack() will warp you back to a place where another trigger
will warp you, use:

WarpBack(true)

WarpBack will warp you back to the previous map/place, but dont use it unless its a room with no other warps out.
WarpBack is useful for a hall with lots of doors that warp you to the same empty room (you make one single map of an empty room).
This way, each time you return to the correct door in the hall.
In order to WarpBack() correctly, you need to give the previous warp with WarpBack Information.

A warpbackInfoObject can contain the following information:

x:   When using warpback, it will warp to this x (in pixels)
dx:  When using warpback, it will offset dx pixels
tx:  When using warpback, it will warp to this x (in tiles)
dtx: When using warpback, it will offset dtx tiles (it automatically centers the sprite on that tile)
                y:   When using warpback, it will warp to this y (in pixels)
                dy:  When using warpback, it will offset dy pixels
                ty:  When using warpback, it will warp to this y (in tiles)
                dty: When using warpback, it will offset dty tiles (it automatically centers the sprite on that tile)
layer: Layer
dir: Facing direction of the input person
name: Name of the input person
rmp: Name of the previous map


So, lets say we have a door and we enter into it going up, and that when we warp back, we dont want to be standing on that trigger again.
We want us to warp back to one tile down, so dty=1

Warp('emptyroom.rmp', 'EmptyRoom', 'An empty room', {dty:1} )

Then, from the empty room we exit with:

WarpBack();

note: You can use more values together in the warpbackInfoObject, just separate them with ",".
example: warp to Y tile 5 (ty=4 + dty=1). and X 338 pixels (x=340 + dx=-2)

Warp('emptyroom.rmp', 'EmptyRoom', 'An empty room', {dty:1, x:340,dx:-2,ty:4} );


run this when you just entered a dungeon:

MapChange.storeWarpBackInfo();

Then, when you need to immediately exit the dungeon, just
MapChange.restoreWarpBackInfo(); MapChange.warpBack(true);
You could also manage your own warpback point, or use these functions to restore to a savespot
(note that maybe you'll want a mix of loading a savegame, but keeping all experience)



MapChange.SeamlessWarpXY() is a very special warp type, it allows to move to another map in a
zelda-link-to-the-past style. It works better on maps that are of equal size.
You call it inside the map SCRIPT_ON_LEAVE_MAP_* scripts like so:
NORTH: SeamlessWarpXY("map.rmp", null, -1);
EAST:  SeamlessWarpXY("map.rmp", 0, null);
SOUTH: SeamlessWarpXY("map.rmp", null, 0);
WEST:  SeamlessWarpXY("map.rmp", -1, null);

Note that a screenshot is taken from both maps, so to get rid of the statusbar (if you have one)
You may want to clear the renderscript/set the renderscript before running this, or even better,
put this in your renderscript code:  if(!MapChange.active) { <Do your statbar things here>}
You can still draw the statusbar while scrolling to the next map, just redefine .scrollUpdateAndDraw()
Also, make something obstruct at the edge of 4 maps, or bad things will happen.


*** Tweaking ***

You can redefine these functions:
.preWarp() is everything you need to do before warping, after fading out.
Just like .postWarp() is what you redefine after a warp, before fading in.
.showName() is what you redefine after a warp, after fading in.
MapChange.active is 0 when not active, 2 when fading out, and 1 just before fading in. (and 3 when seamless-ing)

To change the fade-in speed from the current fade to 50, just: code somewhere MapChange.fadeInFrames=50;
You can also change the way the current fade is done by directly redefining MapChange.fadeIn();
The best way to create fade is to define your own, at the end of this file you can see
MapChange.DefineFade('grayfade', .... );
Note that fadeIn() and fadeOut() only get called once, from there overload UpdateAndDraw() if you need to loop.

For example, I've already coded spotfade, so to activate it do this:

MapChange.setFade('spot');


*** FLAGS ***

MapChange.maps holds information about visited maps, you may want to use/save this information.
To make our life easier, define a global variable like so:

var FLAGS = {MAP:MapChange.maps};

If you use Spheritype, maybe even this:

var FLAGS = { MAP: MapChange.maps, STATE: ScriptBind.world.state, MAPS: ScriptBind.world.maps };

This object can hold events that have happened. Do not confuse with EventMarker[], which can be resetted each time a cutscene starts.
FLAGS.MAP will hold flags of events that happen inside a certain map. We can check things like:
if(FLAGS.MAP[GetCurrentMap()].hasgrabbedtheloot) {...};
Be scarse with those objects, as they are all loaded/saved. Note that you can also use:
if(FLAGS.myevent1==true){...};

Flags are usefull when we have a one-time treasure or cutscene that playes only once, we can mark it inside this object with a persistant flag.

function hasHappened(it,val){ if(val==undefined) return FLAGS[it]; return FLAGS[it]=val; }


*** DO NOT CREATE CIRCULAR WARPPOINTS ***

Its bad when you warp on top of a trigger that warps you elsewhere, it will trigger this warp
and you'll probably be back on the same place you triggered from, or worse, stuck being
triggered back and forth between triggers. The way to avoid it is create a trigger at tile (X,Y)
and return at tile (X,Y+1) when you would be walking south ( (X+1,Y)) when walking east, etc.)
If you do need circular warppoints, increase this variable like so:  MapChange.delaybetweenwarps=300;

*/