Manyland

The Happening type lets you create a variety of visuals and more using JavaScript. There's different types of shapes like circles, rectangles, particles and more which you can freely setup and change over time. You can also draw all the cells of the item itself in various ways, and multiple times in one go. Happenings can be placed in the world and are then started when walking over, or triggered as personal boosts (not visible by others).

What Happening code looks like

To program Happenings, you need to know some JavaScript (there's many great resources available). Here's a sample code structure:

// An example script

var particles = [ { x: 0, y: -30, thickness: 5 } ];

function update(my) {
    particles[0].x += my.tick * 10;
    
    return {particles: particles};
}

First near the top, we're setting a persistent particles array. Now every Happening script has a main function called update, which is called multiple times a second. In it, you can now change the particles if you want. Movement and more should be multiplied by my.tick, which ensures constant speeds even at different frames per seconds. Finally, you'll return the particles and they will be drawn. Happenings run for up to 2 hours.

To try this yourself, you can login, hit the Create button, select Happening, then draw something and save. You can now place the block in a created area where you're listed as editor, and walk over it. Because you've kept the code field empty during saving, an editor box will now appear (you can also tick the attribute 'Offers Editing').

Tip: If you're looking for a good font, try our Doid.ttf, which is a spin on Droid especially suited for programming. After saving locally and installing as font on your machine, you can adjust your browser settings to use this as default monospace font.

Shapes data you can return

You can prepare the following shapes data to return:

Tip: On desktop, you can pick an rgba color value during creation by selecting a palette index, then pressing i.

Please note: Using a high number of shapes or items may make things slower.

Compositing modes

On any shape, you can optionally use the mode parameter to determine how it will be drawn. The following values are available:

Checking the surrounding

Happenings can optionally react to the specific surrounding blocks and entities. If your code is found to contain references to '.sight', 'my.x', 'my.y' and so on, then the following data is passed with the my object as well:

my

For details on these properties, please see the Brain type's explanations.

Native functions

The following functions are available in addition to JavaScript's native functions:

// True or false, depending on a chance of 0 - 100%
chance(percent);

// Returns a random integer from minInt to maxInt
getRandomInt(minInt, maxInt);

// For use with the Changer,
// converts pixels from palette indexes to rgba:
convertToFullColor(creation);

// Returns a copy of the object so that
// changes won't affect the original:
cloneObject(object);

// Limits a value into the numbers,
// sets to minimum if not numeric:
toLimit(value, min, max);

Note: In Happenings, Math.random is overloaded with a seedable randomizer that will result in consistently same randomizations, dependent on the hour of the day, the item position and more.

Logging

You can use the following functions to log debug information when the script editor is opened (you can pass as many parameters as you like):

log('label', someValue);

logOnce(someValue); // only logs once per run

logFresh(someValue); // clears the log and logs something new

logFresh(); // clears the log

Examples

Example: Drawing a variety of shapes

var data;

function update(my) { 
    if (!data) { initializeData(); }

    var line = data.lines[0];
    if (line.y2 <= 100) { line.y2 += 25 * my.tick; }
    data.circles[0].radius += 2.5 * my.tick;
    data.particles[0].x += 15 * my.tick;
    data.particles[1].y -= 15 * my.tick;
    data.particles[2].y -= 15 * my.tick;

    return data;
}

function initializeData() {
    data = {};
    data.particles = [ {x: 0, y: -70, thickness: 5, rgba: [0,255,128,.5], mode: enumMode.bright}, {x: 0, y: -60} ];
    data.lines = [ { x1: -50, y1: 50, x2: 50, y2: -50, rgba: [0,255,0,1], mode: enumMode.bright } ];
    data.rectangles = [ { x1: 10, y1: -50, x2: 50, y2: -40, rgba: [255,0,255,1], filled: true } ];
    data.items = [ {cell: 2, x: -10, y: 0, alpha: .75, flip: {x: true} } ];
    data.polygons = [ { points: [ {x: 10, y: 10 - 30}, {x: 30, y: 20 - 30}, {x: 20, y: 50 - 30} ], rgba: [255,0,0,.8], filled: true, mode: enumMode.luminosity } ];
    data.circles = [ { x: 0, y: -50, rgba: [0,255,255,.2], thickness: 3, radius: 30, filled: true } ];
    for (var i = 0; i < 30; i++) {
        data.particles.push( { x: getRandomInt(-150, 150), y: getRandomInt(-100, 100), thickness: 10, rgba: [0,0,0,.5] } );
    };
}

Example: Using the Back and Front layers

In this example, all layers (default plus back and front) are drawn to:

var particles = [ { x: -10, y: -30, thickness: 10 } ];
var particlesFront = [ { x: -30, y: -30, rgba: [0,255,255,1], thickness: 10 } ];
var particlesBack = [ { x: -10, y: -40, rgba: [0,255,0,1], thickness: 6 } ];

function update(my) {
    particlesBack[0].x -= my.tick * 20;
    particles[0].x += my.tick * 10;
    particlesFront[0].x -= my.tick * 10;
    
    return { particles: particles,
            back: {particles: particlesBack},
            front: {particles: particlesFront} };
}

Example: Many particles

Here we are moving a variety of particles upwards:

var inited = false;
var particles = [];
var tick = 0;

function update(my) { 
    if (!inited) { init(); }
    tick = my.tick;

    moveParticles();

    return {particles: particles}; 
} 

function moveParticles() {
    for (var i = 0; i < particles.length; i++) {
        particles[i].y -= 50 * tick;
    }
}

function init() {
    for (var i = 0; i < 100; i++) {
        var particle = {};
        particle.x = getRandomInt(-50, 50);
        particle.y = getRandomInt(-50, 50);
        particle.rgba = [getRandomInt(0,255), 0, 0, 1];
        particles.push(particle);
    }
    
    inited = true;
}

Example: Lines in the background

The following fades in a wobbling net of lines in the background:

var lines = [];
var alpha = 0;

function update(my) {  
    if (lines.length == 0) { initLines(); }
    alpha += .005;
    if (alpha > 1) { alpha = 1; }
    changeLines();
    return { back: {lines: lines} };
}

function changeLines() {
    var fuzzy = .25;
    for (var i = 0; i < lines.length; i++) {
        var line = lines[i];
        line.x1 += getRandomInt(-1, 1) * fuzzy;
        line.y1 += getRandomInt(-1, 1) * fuzzy;
        line.x2 += getRandomInt(-1, 1) * fuzzy;
        line.y2 += getRandomInt(-1, 1) * fuzzy;
        line.rgba = [255, 255, 255, alpha * .6];
    }
}

function initLines() {
    var fuzzy = 400;
    for (var i = 0; i < 100; i++) {
        var line = {};
        line.x1 = getRandomInt(-fuzzy, fuzzy);
        line.y1 = getRandomInt(-fuzzy, fuzzy);
        line.x2 = getRandomInt(-fuzzy, fuzzy);
        line.y2 = getRandomInt(-fuzzy, fuzzy);
        if ( chance(20) ) { line.thickness = 2; }
        lines.push(line);
    }
   
}

Example: Electricity between metal balls

In this example, we're drawing electricity lines between objects around named 'metal ball' (this works best if you add an electricity sound to the Happening during creation):

var soundCounter = 0;

function update(my) { 
    var lines = [];
    var doPlaySound = false;
    
    var balls = getPositionsByName(my, 'metal ball');
    if (balls.length >= 2) {
        lines = getEletricLines(balls);
    }
    
    if (++soundCounter <= 1) {
        doPlaySound = true;
    }
    return {lines: lines, doPlaySound: doPlaySound};
}

function getEletricLines(balls) {
    var lines = [];
    var maxLines = 100;
    var tileSize = 19;
    var fuzzy = 1;

    for (var ballI = 0; ballI < balls.length; ballI++) {
        for (var i = 3; i >= 1; i--) {
            var offX = -tileSize, offY = -tileSize;

            var ball1 = balls[ballI];            
            var ball2 = ballI == balls.length - 1 ?
                    balls[0] : balls[ballI + 1];
            
            var line = {
                    x1: ball1.x + offX, y1: ball1.y + offY,
                    x2: ball2.x + offX, y2: ball2.y + offY
                    };
            var alpha = getRandomInt(1, 10) * .1;
            line.rgba = [ 200, 200, getRandomInt(200,255), alpha ];
            line.thickness = i * 1;
            line.x1 += getRandomInt(-fuzzy, fuzzy);
            line.y1 += getRandomInt(-fuzzy, fuzzy);
            line.x2 += getRandomInt(-fuzzy, fuzzy);
            line.y2 += getRandomInt(-fuzzy, fuzzy);
            line.mode = enumMode.bright;
            lines.push(line);
            if (lines.length > maxLines) { return lines; }
        }
    }
    return lines;
}

function getPositionsByName(my, name) {
    var placements = my.sight.placements;
    var scope = my.sight.scope;
    var centerX = my.x;
    var centerY = my.y;
    
    var positions = [];
    var scope = 10;
    for (var y = -scope; y <= scope; y++) { 
        for (var x = -scope; x <= scope; x++) {
            var block = placements[x + scope][y + scope];
            if (block && block.name == name) {
                positions.push(
                    {x: block.x - centerX,
                     y: block.y - centerY}
                    );
            }
        }
    }
    return positions;
}

More...

Also check out the main introductory help and the Interacting help. Got a question which isn't answered here, or new ideas and feedback? Have a look here please.