Create a gist now

Instantly share code, notes, and snippets.

import js.Browser.*;
import js.html.CanvasElement;
import js.html.CanvasRenderingContext2D;
import js.html.HTMLDocument;
import js.html.MouseEvent;
import sf.test.Test.Entity;
/**
* @author YellowAfterlife
* http://yal.cc/top-down-bouncing-loot-effects/
*/
class Test {
static var canvas:CanvasElement = document.createCanvasElement();
static var context:CanvasRenderingContext2D = canvas.getContext2d();
static var texture:Array<CanvasElement> = [];
static var entities:Array<Entity>;
static inline var width = 640;
static inline var height = 360;
static function reset() {
entities = [new ClosedChest(width / 2, height / 2)];
}
static function render() {
context.clearRect(0, 0, width, height);
var i:Int = 0, n:Int = entities.length, e:Entity;
while (i < n) {
e = entities[i];
if (e.remove > 0 && --e.remove <= 0) {
entities.splice(i, 1);
n--;
} else {
e.update();
i++;
}
}
entities.sort(Entity.compareY);
// pass 1: draw shadows
context.save();
context.globalAlpha = 0.7;
context.fillStyle = "black";
i = 0; while (i < n) {
e = entities[i];
var r = e.getShadowSize();
var x = e.x - r;
var y = e.y - r / 2;
var w = r * 2;
var h = r;
var x1 = x + w / 2;
var y1 = y + h / 2;
var x2 = x + w;
var y2 = y + h;
var xm = w * 0.275892;
var ym = h * 0.275892;
//
context.beginPath();
context.moveTo(x1, y);
context.bezierCurveTo(x1 + xm, y, x2, y1 - ym, x2, y1);
context.bezierCurveTo(x2, y1 + ym, x1 + xm, y2, x1, y2);
context.bezierCurveTo(x1 - xm, y2, x, y1 + ym, x, y1);
context.bezierCurveTo(x, y1 - ym, x1 - xm, y, x1, y);
context.closePath();
context.fill();
//
i++;
}
context.restore();
// pass 2: draw objects
i = 0; while (i < n) {
e = entities[i];
if (e.visible) {
context.drawImage(texture[e.frame], Std.int(e.x - 32), Std.int(e.y - e.z - 42));
}
i++;
}
}
static function main() {
var atlas = document.createImageElement();
atlas.onload = function() {
canvas.width = width;
canvas.height = height;
document.body.appendChild(canvas);
for (i in 0 ... 10) {
var tex = document.createCanvasElement();
tex.width = tex.height = 64;
tex.getContext2d().drawImage(atlas, -i * 64, 0);
texture.push(tex);
}
reset();
canvas.addEventListener("mousedown", function(event) {
var x = event.offsetX; if (x == null) x = width / 2;
var y = event.offsetY; if (y == null) y = height / 2;
var n = entities.length, i = 0;
while (i < n) {
var e = entities[i];
var action = e.action;
if (action != 0) {
var ex = e.x, ey = e.y, ez = e.z; // positions
var dx = ex - x, dy = (ey - ez) - y; // delta
if (dx * dx + dy * dy < 32 * 32) {
if (action == 1) {
entities.splice(i, 1);
var oc = new OpenChest(ex, ey, ez);
oc.zspeed = e.zspeed;
entities.push(oc);
var num = Std.int(10 + Math.random() * 5);
while (--num >= 0) entities.push(new Coin(ex, ey, ez));
}
break;
}
}
i += 1;
}
if (i >= n) entities.push(new ClosedChest(x, y));
});
window.setInterval(function() {
render();
}, 50);
};
atlas.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAABACAMAAAC9ZDaMAAAABGdBTUEAALGPC/xhBQAAAwBQTFRFAAAAFAwccWk/hUww0n0syqwA3r0A374A3bwa374a9ds4/+kA/+oA//862tRe3u7WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjA1oYgAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNvyMY98AAAYpSURBVHhe7ZvtYtpIDEU3IU3abhPe/2mzvroSGht/SON0DUHnB8bDXJnRnNSFtP98fmOeFT1NouEBHSjCPCl6ukIJuIiGB3SgCKP+3b+AakCHAkj9VnoqeLovD5B7PH0h3vn8MnA6bUtYAi5QAvZyQwJyA/ZsnhowKKDDIdqkgTF9OcBcXl8Kguv9K+Su3IIk0NO7AMqdTufzxwcU3JawBJylBOzlSwXU9Xe3T/dONj9XZY8A11ny+hqtMV8h8w5MPiPfQ9TQC6flN1AD6On/xNPTywuk+xh4ecHNeE3CEnCGEnAPXybgvgY8P7+/a3zgdSCzgRoTkAWxvGVfX98VpuPvYaqfryP6DuSCAuT78yevILrX9i+XJvsF7gMCngZMQrAsYQl4RQm4j78g4I8f2SWwgUT3IixgK4BGlUgFpqf6GdsVrvUDeBbLo4JcSulR0Lrn149nnaMEdAVdwuUPJCXghBJwP18iYKvQ1wiYEQhoaMR2BeTH+v38KVEhlufV8ahlBuJ5m8mjKxjvYds9I542vEo+uw8XELQS4uuZsYIl4IQScD9fLmD2o/xUP+R51AkruAAtvp3rNXDlpdsv2crb1fGoZQR7R1trsJXKZCWrIFbR9hBEs45XyGShi6ODaVxBHlELEsrQcKbTBkrAESWgeHdBB9PcjIC6A7ohkSrIUoExtqXrNdp3zflTtvK4NgXUMgLHtvOowFkyWYF8+LGIdhGraFcColnHK0Sz0ISKGBjRF1OYgLjtSqEB1OYXMq2CJeCIElBcufBwAlpSQiOiAtoN+Ncvzp+ylRf/BtlYhXCMCm6tYU5AwEqxPqILbf9BLNniFWJZ3CQhDBFnhD4JoZuW0lo42lkJuEAJaIgzwuECop2Z5XMTLUtim+dXldCIqIBLeWMrL6b9brcf5/xQsp1HBc7Co6OlOgTEdUks6/gaYsmxgEC8EXISYjbydgSogg8hJeBGXva6BBSgCoFKOi2AiWdHgCrdAuYWb5vIDSNUYrsKryqRCbaxOnERVJDALJE8fnhwNVl8IwDWE+nDtYKsBGJ95BqY8C7Gsk5OQNOP/5i+BcJAHuikk1fALFRCBmmcS5mBEjCULwHHQJiDBYw2jrD13DADVTLNnwIteNRpi9jGuwDZX8Vx1bie6cfV4BhZQ6seYD0QywPvvncjmjV6BKSEYw0h39xXyNdQPpsLWMElpIIY0UgJOKUEBDchoLUuL6Btl8EqsRouoG9kXD/gCk5ViL8DYmvgeuJ/EWmvq6UGcBavoKEBKTMQzRpt/3RoBSgCeagJHgk/OjhLCkKy84BOO+G5J01CCtjWKAGvKAFvRkCjT0DbPlMq3vxWIZMPxCpMtXPi7wAbr4u4EO9Bu/WmEs90wiZHCMjbLiSkgHZuCpElBds85yHD/6DJGpRwmi8BZygB8exwAbXvsv2ZxU+TPRWQmJLZvusKGNOXA/gajEzaVqAGDchbSPYAKc/m0sDXEEny4wPl4dGeu3xgWUCJX/JI2bnpiDNIqBGhBJylBKQueLw7AT0rfRPyzdOgktMHIKHhjjTztg480+Ew05totobnZQkD+Q7w3ccFpDI48qsX6mcSmUbLAk7zmG/Y+TRfAi6ATAl4pwIi/fYmXVN6NtAEym6dgyTQ0w725JGkQH01LA3e3no6qBuYEBBAFcrCI8cpD1gTEEi8ybfygRIwwZ48kiVge+TrHQL2tQ+geTjqYBIp0Zm9DfasoNW3bwd0A9O3YAPSQB88Uh7MWRNQgwpy9uX0Ur4E/KvsWcGDC8jm4Q//PgFJX+sKgu7t6T1+/EFUwI8P6AJxIAp1Em8urAvYky8Bbxh074EFBPtaUBwLdy+2f/iCGMIAE+haIfy6bUnAvnwJ+I3h7t21gMUj0UqkxshNk9jovICgJ18CFhdKwOJwINFYGz7aCF7XqbNk8yVgMSIr0JRsvgQsrqBEBOJE5TMy+RKwuCIj0ByZfAlYzAJZcOPMymdE8yVgMUtUoCWi+RKwWIQS5eUzIvkSsFgkItAakXwJWKyyJk+ErXwJWKxSAhbfmhKwOJQSsDiUErA4lBKwOJQSsDiUErA4lBKwOJQSsDiUErA4kM/P/wAXdPS9Q8I7oAAAAABJRU5ErkJggg==";
}
}
class Entity {
public var x:Float;
public var y:Float;
public var z:Float;
public var xspeed:Float = 0;
public var yspeed:Float = 0;
public var zspeed:Float = 0;
public var friction:Float = 0.3;
public var gravity:Float = 1.0;
public var frame:Int;
public var action:Int = 0;
public var remove:Int = 0;
public var flicker:Int = 0;
public var visible:Bool = true;
public function new(x:Float, y:Float, z:Float) {
this.x = x;
this.y = y;
this.z = z;
}
public function update():Void {
if (flicker > 0 && --flicker <= 0) {
visible = !visible;
flicker = 2;
}
x += xspeed;
y += yspeed;
// friction:
var xyspeed = Math.sqrt(xspeed * xspeed + yspeed * yspeed);
if (xyspeed > 0) {
var mul = Math.max(0, xyspeed - friction) / xyspeed;
xspeed *= mul;
yspeed *= mul;
}
// vertical:
if (z > 0) zspeed -= gravity;
z += zspeed;
if (z < 0) {
if (zspeed < 0) zspeed = -zspeed * 0.6 - 0.7;
if (zspeed < 1) {
zspeed = 0;
z = 0;
} else z = -z;
}
}
public function getShadowSize():Float {
return 24 / (1 + z / 20);
}
public static function compareY(a:Entity, b:Entity):Int {
return a.y < b.y ? -1 : a.y > b.y ? 1 : 0;
}
}
class Coin extends Entity {
public function new(x:Float, y:Float, z:Float) {
super(x, y, z);
frame = Std.int(Math.random() * 8);
remove = 40;
flicker = 20;
var dir = Math.random() * Math.PI * 2;
var len = 8 + Math.random() * 2;
xspeed = Math.cos(dir) * len;
yspeed = Math.sin(dir) * len;
zspeed = 8;
}
override public function update():Void {
super.update();
frame = (frame + 1) % 8;
}
override public function getShadowSize():Float {
return 12 / (1 + (z + Math.cos(frame / 8 * 2 * Math.PI)) / 20);
}
}
class Chest extends Entity {
override public function getShadowSize():Float {
return 24 / (1 + z / 20);
}
}
class ClosedChest extends Entity {
public function new(x:Float, y:Float) {
super(x, y, 64);
zspeed = -16;
frame = 8;
action = 1;
}
}
class OpenChest extends Entity {
public function new(x:Float, y:Float, z:Float) {
super(x, y, z);
frame = 9;
remove = 50;
flicker = 30;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment