Skip to main content

News

Topic: analogue.js (Read 5929 times) previous topic - next topic

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
analogue.js
Introducing: analogue.js

Ever used tung's system but found it to be too slow, or that it doesn't work right at times? Well have no fear since this sleek and lightweight alternative has arrived. I call it "analogue". It's a direct code analogue of the entities on the map, that loads persist scripts and can interact with scripts exactly like how persist can.

Source: revision 10


This can be used a a direct replacement of tung's 'persist.js'.

How to use:

You make a script and store it at "../scripts/maps/your_map.js" for the map 'your_map.rmp' in the /maps directory. This logic holds true for maps in subfolders. "/maps/town/inside.rmp" would need a code stored in "/scripts/maps/town/inside.js" to function.

The code for a town looks like this:
Code: (javascript) [Select]
({
    enter: function() {
        /* code to run on entry of a map */
    },

    leave: function() {
        /* code to run on leave */
    },

    leaveNorth: function() {
        /* code to run when you leave north of the map */
        /* also: South, West, and East variants. */
    },

    // people:
    Bob: {
        talk: function() {
            /* code to run when talked to Bob */
        },

        create: function() {
            /* code to run when Bob is created */
        },

        destroy: function() {
            /* code to run when Bob is destroyed */
        },

        generate: function() {
            /* code to run when Bob generates commands */
        },

        touch: function() {
            /* code to run when Bob is touched by the player */
        },
       
    }
})

When you write code in those 'handlers', it'll directly add it to the person on the map. People also persist values, so you can give bob a property and he'll retain it, like if he were talked to and said something different on the first visit:

Code: (javascript) [Select]
    Bob: {
        visited: false,

        talk: function() {
            if (!this.visited) {
                /* do something when he wasn't visited */
            }

            this.visited = true;
        },
    }

In the code above, visiting Bob means he'll do the action in talk once. Have fun and don't shy away with persisting variables!

Alternatively you can reach the world, map, and person scopes by using three passed variables to the function calls:
Code: (javascript) [Select]
({
    event: 21324,  // some event number, or whatever

    Bob: {
        visited: false,

        talk: function(me, map, world) {
            if (!me.visited) {
                me.visited = true;
                /* do something when he wasn't visited. */
            }
            else if (world.majorEvent) {
                /* talk about some major event in the world. */
            }
            else if (map.event == 21324) {
                /* or perhaps something local to just this map. */
            }
        },
    }
})
  • Last Edit: September 17, 2017, 11:26:55 am by DaVince
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: analogue.js
Reply #1
Nice, I'll have to drop this into Specs tomorrow and give it a proper test drive! :)
miniSphere 5.0b4 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #2
It needs testing. I put it here because dropping it in as a direct replacement in my Blockman game seems to still work. Persons still have their properties, shops work (!), and maps and person events are being called like they used to. But by all means, tell me if you notice a problem. Tung isn't here to update persist, so at least we can guarantee that this will be up-to-date.

I should give some credit to tung: I even used some of his code (the mapEvents and personEvents were directly copied). :)
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: analogue.js
Reply #3
Just looking at your code, I notice something suspect--SetWorld only sets map data.  What about when a user adds custom properties directly to their world object (party data, e.g.) and wants that serialized as well?
miniSphere 5.0b4 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #4

Just looking at your code, I notice something suspect--SetWorld only sets map data.  What about when a user adds custom properties directly to their world object (party data, e.g.) and wants that serialized as well?


Well, you can.

Code: (javascript) [Select]

analogue.map().Bob.newProp = [true, "hi", 5];

// or

analogue.world.party = ["Jenny", "Lars", "Bob"];

// or

analogue.map().visited = true;


There, then when you JSON the analogue.world object, and the use SetWorld on load, all the new custom members will stay. It's made with that in mind. That the world is a playground, and so are the maps and people.
  • Last Edit: June 28, 2013, 02:03:53 am by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: analogue.js
Reply #5
I'm confused though... I only see Absorb calls for the maps... Before those you just do world = {}.  So if you set world.party, JSON it and then later do SetWorld(), where exactly is world.party being deserialized?
miniSphere 5.0b4 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #6

I'm confused though... I only see Absorb calls for the maps... Before those you just do world = {}.  So if you set world.party, JSON it and then later do SetWorld(), where exactly is world.party being deserialized?


Oh, I see, global world scripts won't get added even though I intended for it, my mistake. Yeah, only the map level and below was being saved. And I can see how that can be a problem for those that saved stuff to the world object. x.x

Edit: all fixed, and some other issues as well. Now it'll work right... It wasn't before due to referencing the older named "RadPersist".
  • Last Edit: June 28, 2013, 04:44:26 am by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: analogue.js
Reply #7
Nice, just dropped analogue.js into Specs.  Quick modification to two lines and it worked with no modification to my persist scripts!

Incidentally, I figured out what the state ensuring in persist was meant to do: lazy initialization.  Seems like an unnecessary optimization though, persisted data is lightweight enough that it just complicates things for not much gain.  One thing that does bug me in analogue though (other than the name, seems pretty obtuse) is the semantics of the world setter.  If I see an assignment like this:

Code: (javascript) [Select]
analogue.world = { someVar: "maggie" };


I expect it to completely replace the object with what's on the right of the equal sign.  If I weren't familiar with the library in question, I would consider it very odd behavior indeed to merge the objects!  Unless I'm misunderstanding things and that's not actually what happens?

Edit: Wow, not sure what the heck happened there.  Just for everyone's amusement, this is what originally got posted:
Quote
Nice, just dropped analogue.js into Spe


Something went wrong with the forums I think, since I typed way more than that before I clicked post!
  • Last Edit: June 28, 2013, 08:40:31 am by Lord English
miniSphere 5.0b4 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #8
Well there is analogue.setWorld() which I think is better than using the .world setter. Some people like using '.prop' setters while others use functions so I put in both methods.

I that analogue was a clever name!  ;D Certainly better than the "RadPersist" I had before.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: analogue.js
Reply #9
Wait, I see what's happening here.  It does do a full replacement of the state, it just doesn't do it the way I had expected.  What confused me was the Absorb() calls, so I assumed setWorld() was merging the old world object with the new, which isn't the case (hence the initial world = {};).  I am still a bit confused by why you handle the map data as a special case (i.e. the GetMap() calls in setWorld() ), instead of just letting the Absorb() call handle that by itself?  Is there a specific reason?  I assume it has something to do with your comment in the code about there "not being maps in maps", but that statement doesn't make any sense to me. ???
  • Last Edit: June 28, 2013, 04:19:26 pm by Lord English
miniSphere 5.0b4 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #10
Well, say you modified a file after you saved a game. It needs to update the new map code so that when it replaces it doesn't leave things out. Useful for when you are playing and editing at the same time!
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #11
Revision 5
Compatibility update and world switching

New Features
- analogue.map([map]) now takes a map name as persist did.
- analogue.person(name [, map]) added, takes a person name and an optional mapname.
- analogue.changeWorld(num) added to change the world you are using.

The last feature is cool. Instead of working with a gigantic file for your entire game, you can block up the world spaces, this is useful for games that may have more than one world. This also makes lazy initialization not that important since if a world does get too big you can split it up. In this way you can choose to lazy-load based on your save file format and where you are in the world.

It must be used with care. If you are on world 2, and need info from world 1, then you'll have to do a simple switch:
Code: (javascript) [Select]

analogue.setWorld(0);
var data = analogue.world.map("name.rmp").data;
analogue.setWorld(1);
// do something with the data... //


It can also be useful for saved games, where each saved file is a separate world space. Makes loading potentially faster. You can, for example, use world 0 for all autosaving. For very fast autosave or quick-save functionality.
  • Last Edit: June 28, 2013, 11:11:39 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: analogue.js
Reply #12
Neat, that multi-world feature should come in handy once I finally get to the point of implementing game saving in Spectacles. :)  Not sure I'm a big fan of this API setup though, why not just do what you did with the maps and set up the API like this:
Code: (javascript) [Select]
var data = analogue.world(0).map("name.rmp").data;


Seems like a better setup from a consistency standpoint and would prevent bugs due to missed calls to changeWorld().
miniSphere 5.0b4 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #13
Good idea, will look into it next revision. :)
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: analogue.js
Reply #14
Revision 8 - Breaking Change

New world handling features and some helper functions in the world object.

New Features
- world objects now have methods .map(), and .person()
- analogue.mergeWorld(data [, world_num]) will merge a saved world into the existing world or world num
- analogue.getWorld([world_num]) will get the numbered world
- analogue.setWorld(data [, world_num]) will replace the world; don't do this if you intend to merge.
- analogue.clear() clears the current world by replacing it with a new one.

Bug Fixes
- Merge's Absorb will respond to updated objects if a member had changed from an object to something else.
- analogue.setWorld() now Absorbs the replacement into an empty world to preserve .map() and .person().

etc.
- Some code cleaning and commenting.

So, to remind the users: if you want to use multiple worlds in-game you must use .changeWorld(). .getWorld() and .setWorld() only retrieve or replace information. analogue will only run automatically on the current world (the world set by .changeWorld()). Of course you can keep the current world at 0, not use .changeWorld(), and instead replace world 0 whenever you load games. So in other words: .changeWorld() is really optional if you stick to world 0 for the game and world 1 and on as strictly data.

So, now you can do this:
Code: (javascript) [Select]

var data = analogue.getWorld(2).map("map.rmp").data;


Oh and about the world .person() and .map() calls, well, that means you shouldn't put 'person' or 'map' members into the world object. If you do, they will indeed persist, but overwrite the function calls. Small price to pay for convenience.
  • Last Edit: June 29, 2013, 03:15:55 am by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here