Blame src/sprite.c

Ivan Mahonin 3f9996
Ivan Mahonin 8535a3
#include <cairo.h>
Ivan Mahonin 8535a3
Ivan Mahonin 3f9996
#include "private.h"
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
#include "sprite.h"
Ivan Mahonin 3f9996
#include "world.h"
Ivan Mahonin 8535a3
#include "group.h"
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
struct _Sprite {
Ivan Mahonin 3f9996
	double x;
Ivan Mahonin 3f9996
	double y;
Ivan Mahonin 3f9996
	double rotation;
Ivan Mahonin 3f9996
	double width;
Ivan Mahonin 3f9996
	double height;
Ivan Mahonin 3f9996
	double scale;
Ivan Mahonin 3f9996
	
Ivan Mahonin 3f9996
	double vx;
Ivan Mahonin 3f9996
	double vy;
Ivan Mahonin 3f9996
	double rotationSpeed;
Ivan Mahonin 3f9996
	
Ivan Mahonin 3f9996
	HeliCollider collider;
Ivan Mahonin 3f9996
	double bounciness;
Ivan Mahonin 3f9996
	
Ivan Mahonin 3f9996
	int rotateToDirection;
Ivan Mahonin 3f9996
	int mirrorX;
Ivan Mahonin 3f9996
	int mirrorY;
Ivan Mahonin 3f9996
	double depth;
Ivan Mahonin 3f9996
	double lifeTime;
Ivan Mahonin 8535a3
	int visible;
Ivan Mahonin 8935bc
	int tag;
Ivan Mahonin 3f9996
	int debug;
Ivan Mahonin 3f9996
	
Ivan Mahonin 8535a3
	double shapeColor[4];
Ivan Mahonin 8535a3
	double tintColor[4];
Ivan Mahonin 3f9996
	
Ivan Mahonin 3f9996
	int playing;
Ivan Mahonin 3f9996
	int frame;
Ivan Mahonin 3f9996
	
Ivan Mahonin 8535a3
	HeliArray groups;
Ivan Mahonin 8535a3
	
Ivan Mahonin 1c7488
	HeliAnimation *animation;
Ivan Mahonin 3f9996
};
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
static HeliArray sprites;
Ivan Mahonin 8535a3
static HeliArray spritesSorted;
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
static void prepareCollider(Sprite sprite, HeliCollider *collider) {
Ivan Mahonin 8535a3
	collider->type = sprite->collider.type;
Ivan Mahonin 8535a3
	collider->x = sprite->collider.x + sprite->x;
Ivan Mahonin 8535a3
	collider->y = sprite->collider.y + sprite->y;
Ivan Mahonin 1c7488
	collider->width = sprite->collider.width;
Ivan Mahonin 1c7488
	collider->height = sprite->collider.height;
Ivan Mahonin 1c7488
	collider->radius = sprite->collider.radius;
Ivan Mahonin 8535a3
	collider->rotation = sprite->collider.rotation + sprite->rotation;
Ivan Mahonin 1c7488
	
Ivan Mahonin f8c1ea
	// auto-size
Ivan Mahonin f8c1ea
	if (collider->type == COLLIDER_RECTANGLE) {
Ivan Mahonin f8c1ea
		if (collider->width < -HELI_PRECISION) collider->width = sprite->width;
Ivan Mahonin f8c1ea
		if (collider->height < -HELI_PRECISION) collider->height = sprite->height;
Ivan Mahonin f8c1ea
		if (collider->radius < -HELI_PRECISION) collider->radius = 0;
Ivan Mahonin f8c1ea
	} else
Ivan Mahonin f8c1ea
	if (collider->type == COLLIDER_CIRCLE) {
Ivan Mahonin f8c1ea
		collider->width = 0;
Ivan Mahonin f8c1ea
		collider->height = 0;
Ivan Mahonin f8c1ea
		if (collider->radius < -HELI_PRECISION) collider->radius = 0.5*sprite->width;
Ivan Mahonin f8c1ea
	}
Ivan Mahonin 1c7488
	
Ivan Mahonin f8c1ea
	// scale
Ivan Mahonin 1c7488
	collider->width *= sprite->scale;
Ivan Mahonin 1c7488
	collider->height *= sprite->scale;
Ivan Mahonin 1c7488
	collider->radius *= sprite->scale;
Ivan Mahonin f8c1ea
	
Ivan Mahonin f8c1ea
	// fix small values
Ivan Mahonin f8c1ea
	if (collider->width  < HELI_PRECISION) collider->width = HELI_PRECISION;
Ivan Mahonin f8c1ea
	if (collider->height < HELI_PRECISION) collider->height = HELI_PRECISION;
Ivan Mahonin f8c1ea
	if (collider->radius < HELI_PRECISION) collider->radius = HELI_PRECISION;
Ivan Mahonin f8c1ea
	
Ivan Mahonin f8c1ea
	// fix round corners
Ivan Mahonin f8c1ea
	if (collider->type == COLLIDER_RECTANGLE) {
Ivan Mahonin f8c1ea
		double rmax = 0.5*( collider->width < collider->height
Ivan Mahonin f8c1ea
						  ? collider->width : collider->height );
Ivan Mahonin f8c1ea
		if (collider->radius >= rmax - HELI_PRECISION)
Ivan Mahonin f8c1ea
			collider->radius = rmax;
Ivan Mahonin f8c1ea
		collider->width  -= 2*collider->radius;
Ivan Mahonin f8c1ea
		collider->height -= 2*collider->radius;
Ivan Mahonin f8c1ea
	}
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
Sprite createSpriteEx(double x, double y, double width, double height) {
Ivan Mahonin 8535a3
	Sprite s = calloc(1, sizeof(*s));
Ivan Mahonin 8535a3
	s->x = x;
Ivan Mahonin 8535a3
	s->y = y;
Ivan Mahonin 8535a3
	s->width = width;
Ivan Mahonin 8535a3
	s->height = height;
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	s->scale = 1;
Ivan Mahonin 8535a3
	s->collider.type = COLLIDER_RECTANGLE;
Ivan Mahonin f8c1ea
	s->collider.width = s->collider.height = -1;
Ivan Mahonin f8c1ea
	s->bounciness = 1;
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	s->mirrorX = 1;
Ivan Mahonin 8535a3
	s->mirrorY = 1;
Ivan Mahonin 8535a3
	s->lifeTime = -1;
Ivan Mahonin 8535a3
	s->visible = TRUE;
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	s->shapeColor[0] = s->shapeColor[1] = s->shapeColor[2] = 0.5;
Ivan Mahonin 8535a3
	s->shapeColor[3] = 1;
Ivan Mahonin 8535a3
	s->tintColor[0] = s->tintColor[1] = s->tintColor[2] = s->tintColor[3] = 1;
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	heliArrayInsert(&sprites, -1, s, NULL);
Ivan Mahonin 8535a3
	heliArrayInsert(&spritesSorted, -1, s, NULL);
Ivan Mahonin 07b70f
	
Ivan Mahonin 07b70f
	return s;
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
Sprite createSprite(double x, double y)
Ivan Mahonin 8535a3
	{ return createSpriteEx(x, y, 100, 100); }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
void spriteDestroy(Sprite sprite) {
Ivan Mahonin 1c7488
	spriteSetNoAnimation(sprite);
Ivan Mahonin 8535a3
	while(sprite->groups.count > 0)
Ivan Mahonin 8535a3
		groupRemove((Group)sprite->groups.items[0].value, sprite);
Ivan Mahonin 8535a3
	for(int i = 0; i < sprites.count; ++i)
Ivan Mahonin 8535a3
		if (sprites.items[i].value == sprite)
Ivan Mahonin 8535a3
			{ heliArrayRemove(&sprites, i); break; }
Ivan Mahonin 8535a3
	for(int i = 0; i < spritesSorted.count; ++i)
Ivan Mahonin 8535a3
		if (spritesSorted.items[i].value == sprite)
Ivan Mahonin 8535a3
			{ heliArrayRemove(&spritesSorted, i); break; }
Ivan Mahonin 8535a3
	free(sprite);
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
void spriteDestroyTimer(Sprite sprite, double lifeTime) {
Ivan Mahonin 8535a3
	if (lifeTime <= 0) { spriteDestroy(sprite); return; }
Ivan Mahonin 8535a3
	sprite->lifeTime = lifeTime;
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetX(Sprite sprite) { return sprite->x; }
Ivan Mahonin 8535a3
void spriteSetX(Sprite sprite, double x) { sprite->x = x; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetY(Sprite sprite) { return sprite->y; }
Ivan Mahonin 8535a3
void spriteSetY(Sprite sprite, double y) { sprite->y = y; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetVelocityX(Sprite sprite) { return sprite->vx; }
Ivan Mahonin 8535a3
void spriteSetVelocityX(Sprite sprite, double x)
Ivan Mahonin 8535a3
	{ spriteSetVelocityXY(sprite, x, sprite->vy); }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetVelocityY(Sprite sprite) { return sprite->vy; }
Ivan Mahonin 8535a3
void spriteSetVelocityY(Sprite sprite, double y)
Ivan Mahonin 8535a3
	{ spriteSetVelocityXY(sprite, sprite->vx, y); }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetScale(Sprite sprite) { return sprite->scale; }
Ivan Mahonin 8535a3
void spriteSetScale(Sprite sprite, double scale) { sprite->scale = scale; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetWidth(Sprite sprite) { return sprite->width; }
Ivan Mahonin 8535a3
void spriteSetWidth(Sprite sprite, double width)
Ivan Mahonin 8535a3
	{ sprite->width = width > 0 ? width : 0; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetHeight(Sprite sprite) { return sprite->height; }
Ivan Mahonin 8535a3
void spriteSetHeight(Sprite sprite, double height)
Ivan Mahonin 8535a3
	{ sprite->height = height > 0 ? height : 0; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
int spriteGetRotateToDirection(Sprite sprite) { return sprite->rotateToDirection; }
Ivan Mahonin 8535a3
void spriteSetRotateToDirection(Sprite sprite, int rotateToDirection) {
Ivan Mahonin 8535a3
	if (rotateToDirection) {
Ivan Mahonin 8535a3
		sprite->rotateToDirection = TRUE;
Ivan Mahonin 8535a3
		sprite->rotation = spriteGetDirection(sprite);
Ivan Mahonin 8535a3
	} else {
Ivan Mahonin 8535a3
		sprite->rotateToDirection = FALSE;
Ivan Mahonin 8535a3
	}
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetRotation(Sprite sprite) { return sprite->rotation; }
Ivan Mahonin 8535a3
void spriteSetRotation(Sprite sprite, double rotation)
Ivan Mahonin 8535a3
	{ if (!sprite->rotateToDirection) sprite->rotation = rotation; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetRotationSpeed(Sprite sprite) { return sprite->rotationSpeed; }
Ivan Mahonin 8535a3
void spriteSetRotationSpeed(Sprite sprite, double rotationSpeed)
Ivan Mahonin 8535a3
	{ sprite->rotationSpeed = rotationSpeed; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
int spriteGetMirrorX(Sprite sprite) { return sprite->mirrorX; }
Ivan Mahonin 8535a3
void spriteSetMirrorX(Sprite sprite, int mirrorX)
Ivan Mahonin 8535a3
	{ sprite->mirrorX = mirrorX < 0 ? -1 : 1; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
int spriteGetMirrorY(Sprite sprite) { return sprite->mirrorY; }
Ivan Mahonin 07b70f
void spriteSetMirrorY(Sprite sprite, int mirrorY)
Ivan Mahonin 8535a3
	{ sprite->mirrorY = mirrorY < 0 ? -1 : 1; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetDepth(Sprite sprite) { return sprite->depth; }
Ivan Mahonin 8535a3
void spriteSetDepth(Sprite sprite, double depth)
Ivan Mahonin 8535a3
	{ sprite->depth = depth; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
int spriteGetVisible(Sprite sprite) { return sprite->visible; }
Ivan Mahonin 8535a3
void spriteSetVisible(Sprite sprite, int visible)
Ivan Mahonin 8535a3
	{ sprite->visible = visible != FALSE; }
Ivan Mahonin 8535a3
Ivan Mahonin 8935bc
int spriteGetTag(Sprite sprite) { return sprite->tag; }
Ivan Mahonin 8935bc
void spriteSetTag(Sprite sprite, int tag)
Ivan Mahonin 8935bc
	{ sprite->tag = tag; }
Ivan Mahonin 8935bc
Ivan Mahonin 8535a3
int spriteGetDebug(Sprite sprite) { return sprite->debug; }
Ivan Mahonin 8535a3
void spriteSetDebug(Sprite sprite, int debug)
Ivan Mahonin 8535a3
	{ sprite->debug = debug != FALSE; }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
int spriteOverlap(Sprite a, Sprite b)
Ivan Mahonin 8535a3
	{ return spriteCollideEx(a, b, TRUE, TRUE, 0); }
Ivan Mahonin 8535a3
int spriteCollide(Sprite a, Sprite b, double bounciness)
Ivan Mahonin 8535a3
	{ return spriteCollideEx(a, b, FALSE, FALSE, bounciness); }
Ivan Mahonin 8535a3
int spriteBounceOff(Sprite sprite, Sprite other, double bounciness)
Ivan Mahonin 8535a3
	{ return spriteCollideEx(sprite, other, FALSE, TRUE, bounciness); }
Ivan Mahonin 8535a3
int spritePush(Sprite sprite, Sprite other, double bounciness)
Ivan Mahonin 8535a3
	{ return spriteCollideEx(sprite, other, TRUE, FALSE, bounciness); }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
int spriteCollideEx(Sprite a, Sprite b, int keepVelocityA, int keepVelocityB, double bounciness) {
Ivan Mahonin f8c1ea
	if (a == b) return FALSE;
Ivan Mahonin 1c7488
	HeliCollider ca, cb;
Ivan Mahonin 1c7488
	prepareCollider(a, &ca);
Ivan Mahonin 1c7488
	prepareCollider(b, &cb);
Ivan Mahonin 1c7488
	double dx = 0, dy = 0;
Ivan Mahonin 1c7488
	double vx = a->vx - b->vx;
Ivan Mahonin 1c7488
	double vy = a->vy - b->vy;
Ivan Mahonin 1c7488
	bounciness = fabs(bounciness * a->bounciness * b->bounciness);
Ivan Mahonin 1c7488
	if (!heliCheckCollision(&ca, &cb, &dx, &dy, &vx, &vy, bounciness))
Ivan Mahonin 1c7488
		return FALSE;
Ivan Mahonin 1c7488
	
Ivan Mahonin 1c7488
	if ( keepVelocityA && !keepVelocityB) {
Ivan Mahonin 1c7488
		b->x -= dx;
Ivan Mahonin 1c7488
		b->y -= dy;
Ivan Mahonin f8c1ea
		spriteSetVelocityXY(b, a->vx - vx, a->vy - vy);
Ivan Mahonin 1c7488
	} else
Ivan Mahonin 1c7488
	if (!keepVelocityA &&  keepVelocityB) {
Ivan Mahonin 1c7488
		a->x += dx;
Ivan Mahonin 1c7488
		a->y += dy;
Ivan Mahonin f8c1ea
		spriteSetVelocityXY(a, b->vx + vx, b->vy + vy);
Ivan Mahonin 1c7488
	} else
Ivan Mahonin 1c7488
	if (!keepVelocityA && !keepVelocityB) {
Ivan Mahonin 1c7488
		double vxAvg = 0.5*(a->vx + b->vx);
Ivan Mahonin 1c7488
		double vyAvg = 0.5*(a->vy + b->vy);
Ivan Mahonin 1c7488
		
Ivan Mahonin 1c7488
		a->x += 0.5*dx;
Ivan Mahonin 1c7488
		a->y += 0.5*dy;
Ivan Mahonin f8c1ea
		spriteSetVelocityXY(a, vxAvg + 0.5*vx, vyAvg + 0.5*vy);
Ivan Mahonin 1c7488
		
Ivan Mahonin 1c7488
		b->x -= 0.5*dx;
Ivan Mahonin 1c7488
		b->y -= 0.5*dy;
Ivan Mahonin f8c1ea
		spriteSetVelocityXY(b, vxAvg - 0.5*vx, vyAvg - 0.5*vy);
Ivan Mahonin 1c7488
	}
Ivan Mahonin 1c7488
	
Ivan Mahonin 1c7488
	return TRUE;
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetBounciness(Sprite sprite) { return sprite->bounciness; }
Ivan Mahonin 8535a3
void spriteSetBounciness(Sprite sprite, double bounciness)
Ivan Mahonin 8535a3
	{ sprite->bounciness = bounciness > 0 ? bounciness : 0; }
Ivan Mahonin 8535a3
Ivan Mahonin f8c1ea
void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset, double rotationOffset)
Ivan Mahonin f8c1ea
	{ spriteSetColliderEx(sprite, type, xOffset, yOffset, rotationOffset, -1, -1, -1); }
Ivan Mahonin f8c1ea
void spriteSetColliderCircle(Sprite sprite, double xOffset, double yOffset, double radius)
Ivan Mahonin f8c1ea
	{ spriteSetColliderEx(sprite, COLLIDER_CIRCLE, xOffset, yOffset, 0, 0, 0, radius); }
Ivan Mahonin f8c1ea
void spriteSetColliderRectangle(
Ivan Mahonin f8c1ea
	Sprite sprite, double xOffset, double yOffset, double rotationOffset,
Ivan Mahonin f8c1ea
	double width, double height, double cornersRadius )
Ivan Mahonin f8c1ea
{
Ivan Mahonin f8c1ea
	spriteSetColliderEx(
Ivan Mahonin f8c1ea
		sprite, COLLIDER_RECTANGLE,
Ivan Mahonin f8c1ea
		xOffset, yOffset, rotationOffset,
Ivan Mahonin f8c1ea
		width, height, cornersRadius);
Ivan Mahonin f8c1ea
}
Ivan Mahonin f8c1ea
Ivan Mahonin f8c1ea
void spriteSetColliderEx(
Ivan Mahonin f8c1ea
	Sprite sprite, Collider type,
Ivan Mahonin f8c1ea
	double xOffset, double yOffset, double rotationOffset,
Ivan Mahonin f8c1ea
	double width, double height, double radius )
Ivan Mahonin 8535a3
{
Ivan Mahonin 8535a3
	sprite->collider.type = type;
Ivan Mahonin 8535a3
	sprite->collider.x = xOffset;
Ivan Mahonin 8535a3
	sprite->collider.y = yOffset;
Ivan Mahonin 8535a3
	sprite->collider.rotation = rotationOffset;
Ivan Mahonin f8c1ea
	if (type == COLLIDER_CIRCLE) {
Ivan Mahonin f8c1ea
		sprite->collider.width = 0;
Ivan Mahonin f8c1ea
		sprite->collider.height = 0;
Ivan Mahonin f8c1ea
		sprite->collider.radius = radius;
Ivan Mahonin f8c1ea
	} else
Ivan Mahonin f8c1ea
	if (type == COLLIDER_RECTANGLE) {
Ivan Mahonin f8c1ea
		sprite->collider.width = width;
Ivan Mahonin f8c1ea
		sprite->collider.height = height;
Ivan Mahonin f8c1ea
		sprite->collider.radius = radius;
Ivan Mahonin f8c1ea
	} else {
Ivan Mahonin f8c1ea
		sprite->collider.width = 0;
Ivan Mahonin f8c1ea
		sprite->collider.height = 0;
Ivan Mahonin f8c1ea
		sprite->collider.radius = 0;
Ivan Mahonin f8c1ea
	}
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 1c7488
void spriteSetAnimation(Sprite sprite, const char *path) {
Ivan Mahonin 1c7488
	HeliAnimation *prev = sprite->animation;
Ivan Mahonin 1c7488
	sprite->animation = heliAnimationLoad(path);
Ivan Mahonin 1c7488
	if (prev) heliAnimationUnref(prev);
Ivan Mahonin 8535a3
	spriteSetFrame(sprite, sprite->frame);
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 1c7488
void spriteSetNoAnimation(Sprite sprite) {
Ivan Mahonin 1c7488
	if (sprite->animation) heliAnimationUnref(sprite->animation);
Ivan Mahonin 1c7488
	sprite->animation = NULL;
Ivan Mahonin 8935bc
}
Ivan Mahonin 8935bc
Ivan Mahonin 8535a3
void spritePlay(Sprite sprite) { sprite->playing = TRUE; }
Ivan Mahonin 8535a3
void spritePause(Sprite sprite) { sprite->playing = FALSE; }
Ivan Mahonin 8535a3
void spriteNextFrame(Sprite sprite) { spriteSetFrame(sprite, sprite->frame + 1); }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
void spriteSetFrame(Sprite sprite, int frame) {
Ivan Mahonin 1c7488
	sprite->frame = frame >= 0 && sprite->animation && sprite->animation->frames.count > 0
Ivan Mahonin 1c7488
				  ? frame % sprite->animation->frames.count : 0;
Ivan Mahonin 3f9996
}
Ivan Mahonin 3f9996
Ivan Mahonin 8535a3
void spriteSetShapeColor(Sprite sprite, const char *color)
Ivan Mahonin 8535a3
	{ heliParseColor(color, sprite->shapeColor); }
Ivan Mahonin 8535a3
void spriteSetTintColor(Sprite sprite, const char *color)
Ivan Mahonin 8535a3
	{ heliParseColor(color, sprite->tintColor); }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
void spriteSetVelocityXY(Sprite sprite, double x, double y) {
Ivan Mahonin 8535a3
	sprite->vx = x;
Ivan Mahonin 8535a3
	sprite->vy = y;
Ivan Mahonin 8535a3
	if (sprite->rotateToDirection)
Ivan Mahonin 8535a3
		sprite->rotation = spriteGetDirection(sprite);
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
void spriteSetSpeedAndDirection(Sprite sprite, double speed, double angle) {
Ivan Mahonin 8535a3
	double a = angle*(PI/180);
Ivan Mahonin 8535a3
	spriteSetVelocityXY(sprite, cos(a)*speed, sin(a)*speed);
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetSpeed(Sprite sprite)
Ivan Mahonin 8535a3
	{ return sqrt(sprite->vx*sprite->vx + sprite->vy*sprite->vy); }
Ivan Mahonin 8535a3
double spriteGetDirection(Sprite sprite) {
Ivan Mahonin 1c7488
	return fabs(sprite->vx) > HELI_PRECISION || fabs(sprite->vy) > HELI_PRECISION
Ivan Mahonin 8535a3
	     ? atan2(sprite->vy, sprite->vx)*(180/PI) : 0;
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
void spritePointTo(Sprite sprite, double x, double y)
Ivan Mahonin 981405
	{ spriteSetRotation( sprite, atan2(y - sprite->y, x - sprite->x)*(180/PI) ); }
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
double spriteGetScaledWidth(Sprite sprite)
Ivan Mahonin 8535a3
	{ return sprite->width * sprite->scale; }
Ivan Mahonin 8535a3
double spriteGetScaledHeight(Sprite sprite)
Ivan Mahonin 8535a3
	{ return sprite->height * sprite->scale; }
Ivan Mahonin 8535a3
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
int worldGetSpriteCount()
Ivan Mahonin 3f9996
	{ return sprites.count; }
Ivan Mahonin 3f9996
Ivan Mahonin 07b70f
Sprite worldGetSprite(int i)
Ivan Mahonin 8535a3
	{ return (Sprite)heliArrayGetValue(&sprites, i); }
Ivan Mahonin 3f9996
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
static void drawSpriteDebug(cairo_t *cr, Sprite s) {
Ivan Mahonin 8535a3
	cairo_save(cr);
Ivan Mahonin 981405
	cairo_set_line_width(cr, 0.5);
Ivan Mahonin 8535a3
	cairo_translate(cr, s->x, s->y);
Ivan Mahonin 981405
	cairo_rotate(cr, s->rotation*(PI/180));
Ivan Mahonin 8535a3
	cairo_scale(cr, s->scale*s->mirrorX, s->scale*s->mirrorY);
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	double hw = s->width*0.5;
Ivan Mahonin 8535a3
	double hh = s->height*0.5;
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
	cairo_set_source_rgba(cr, 0, 0, 0, 0.75);
Ivan Mahonin 8535a3
	cairo_move_to(cr, -hw, -hh);
Ivan Mahonin 8535a3
	cairo_line_to(cr,  hw, -hh);
Ivan Mahonin 8535a3
	cairo_line_to(cr,  hw,  hh);
Ivan Mahonin 8535a3
	cairo_line_to(cr, -hw,  hh);
Ivan Mahonin 8535a3
	cairo_close_path(cr);
Ivan Mahonin 07b70f
	cairo_stroke(cr);
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
	cairo_move_to(cr, -hw*0.25, 0);
Ivan Mahonin 8535a3
	cairo_line_to(cr,  hw*0.25, 0);
Ivan Mahonin 8535a3
	cairo_move_to(cr, 0, -hh*0.25);
Ivan Mahonin 8535a3
	cairo_line_to(cr, 0,  hh*0.25);
Ivan Mahonin 07b70f
	cairo_stroke(cr);
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	char buf[1024];
Ivan Mahonin 09c823
	snprintf(buf, sizeof(buf)-1, "%lf", s->depth);
Ivan Mahonin 8535a3
	
Ivan Mahonin 981405
	double s1 = hw*0.25;
Ivan Mahonin 8535a3
	double s2 = hh*0.5;
Ivan Mahonin 8535a3
	cairo_set_font_size(cr, s1 < s2 ? s1 : s2);
Ivan Mahonin 981405
Ivan Mahonin 981405
	cairo_move_to(cr, -hw*0.9, -hh*0.5);
Ivan Mahonin 8535a3
	cairo_show_text(cr, buf);
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
	cairo_restore(cr);
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
static void drawSprite(cairo_t *cr, Sprite s) {
Ivan Mahonin 8535a3
	cairo_save(cr);
Ivan Mahonin 8535a3
	cairo_translate(cr, s->x, s->y);
Ivan Mahonin 981405
	cairo_rotate(cr, s->rotation*(PI/180));
Ivan Mahonin 8535a3
	cairo_scale(cr, s->scale*s->mirrorX, s->scale*s->mirrorY);
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	double hw = s->width*0.5;
Ivan Mahonin 8535a3
	double hh = s->height*0.5;
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	cairo_surface_t *frame = s->animation
Ivan Mahonin 1c7488
						   ? (cairo_surface_t*)heliArrayGetValue(&s->animation->frames, s->frame) : NULL;
Ivan Mahonin 8535a3
Ivan Mahonin 650f35
	double a = s->tintColor[3];
Ivan Mahonin 1c7488
	int tint = fabs(s->tintColor[0] - 1) > HELI_PRECISION
Ivan Mahonin 1c7488
			|| fabs(s->tintColor[1] - 1) > HELI_PRECISION
Ivan Mahonin 1c7488
			|| fabs(s->tintColor[2] - 1) > HELI_PRECISION;
Ivan Mahonin 1c7488
	int alpha = fabs(a) > HELI_PRECISION;
Ivan Mahonin 1c7488
	int visible = fabs(a) > HELI_PRECISION;
Ivan Mahonin 8535a3
	
Ivan Mahonin 650f35
	if (visible) {
Ivan Mahonin 650f35
		if (tint) cairo_push_group(cr);
Ivan Mahonin 650f35
		
Ivan Mahonin 650f35
		if (frame) {
Ivan Mahonin 650f35
			// image
Ivan Mahonin 650f35
			double ihw = cairo_image_surface_get_width(frame)*0.5;
Ivan Mahonin 650f35
			double ihh = cairo_image_surface_get_height(frame)*0.5;
Ivan Mahonin 1c7488
			if (ihw > HELI_PRECISION && ihh > HELI_PRECISION) {
Ivan Mahonin 650f35
				cairo_save(cr);
Ivan Mahonin 650f35
				cairo_scale(cr, hw/ihw, hh/ihh);
Ivan Mahonin 650f35
				cairo_set_source_surface(cr, frame, -ihw, -ihh);
Ivan Mahonin 650f35
				if (alpha) cairo_paint_with_alpha(cr, a); else cairo_paint(cr);
Ivan Mahonin 650f35
				cairo_restore(cr);
Ivan Mahonin 650f35
			}
Ivan Mahonin 650f35
		} else {
Ivan Mahonin 650f35
			// rectangle
Ivan Mahonin 650f35
			cairo_set_source_rgba(cr, s->shapeColor[0], s->shapeColor[1], s->shapeColor[2], s->shapeColor[3]*a);
Ivan Mahonin 650f35
			cairo_move_to(cr, -hw, -hh);
Ivan Mahonin 650f35
			cairo_line_to(cr,  hw, -hh);
Ivan Mahonin 650f35
			cairo_line_to(cr,  hw,  hh);
Ivan Mahonin 650f35
			cairo_line_to(cr, -hw,  hh);
Ivan Mahonin 650f35
			cairo_close_path(cr);
Ivan Mahonin 650f35
			cairo_fill(cr);
Ivan Mahonin 650f35
		}
Ivan Mahonin 650f35
		
Ivan Mahonin 650f35
		if (tint) {
Ivan Mahonin 650f35
			cairo_pattern_t *spriteGroup = cairo_pop_group(cr);
Ivan Mahonin 650f35
			cairo_push_group(cr);
Ivan Mahonin 650f35
			cairo_set_source(cr, spriteGroup);
Ivan Mahonin 650f35
			cairo_paint(cr);
Ivan Mahonin 650f35
			cairo_set_source_rgba(cr, s->tintColor[0], s->tintColor[1], s->tintColor[2], 1);
Ivan Mahonin 650f35
			cairo_set_operator(cr, CAIRO_OPERATOR_MULTIPLY);
Ivan Mahonin 650f35
			cairo_mask(cr, spriteGroup);
Ivan Mahonin 650f35
			cairo_pop_group_to_source(cr);
Ivan Mahonin 981405
			cairo_paint(cr);
Ivan Mahonin 650f35
			cairo_pattern_destroy(spriteGroup);
Ivan Mahonin 8535a3
		}
Ivan Mahonin 8535a3
	}
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	cairo_restore(cr);
Ivan Mahonin 8535a3
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
void drawSprites() {
Ivan Mahonin 3f9996
	// sort
Ivan Mahonin 3f9996
	int found = TRUE;
Ivan Mahonin 3f9996
	while(found) {
Ivan Mahonin 3f9996
		found = FALSE;
Ivan Mahonin 8535a3
		// forward
Ivan Mahonin 07b70f
		for(int i = 1; i < spritesSorted.count; ++i) {
Ivan Mahonin 07b70f
			if ( ((Sprite)spritesSorted.items[i-1].value)->depth
Ivan Mahonin 07b70f
			   < ((Sprite)spritesSorted.items[i  ].value)->depth )
Ivan Mahonin 07b70f
			{
Ivan Mahonin 07b70f
				void *x = spritesSorted.items[i].value;
Ivan Mahonin 07b70f
				spritesSorted.items[i].value = spritesSorted.items[i-1].value;
Ivan Mahonin 07b70f
				spritesSorted.items[i-1].value = x;
Ivan Mahonin 3f9996
				found = TRUE;
Ivan Mahonin 3f9996
			}
Ivan Mahonin 3f9996
		}
Ivan Mahonin 8535a3
		if (!found) break;
Ivan Mahonin 8535a3
		// backward
Ivan Mahonin 8535a3
		found = FALSE;
Ivan Mahonin 07b70f
		for(int i = spritesSorted.count - 1; i > 0; --i) {
Ivan Mahonin 07b70f
			if ( ((Sprite)spritesSorted.items[i-1].value)->depth
Ivan Mahonin 07b70f
			   < ((Sprite)spritesSorted.items[i  ].value)->depth )
Ivan Mahonin 07b70f
			{
Ivan Mahonin 07b70f
				void *x = spritesSorted.items[i].value;
Ivan Mahonin 07b70f
				spritesSorted.items[i].value = spritesSorted.items[i-1].value;
Ivan Mahonin 07b70f
				spritesSorted.items[i-1].value = x;
Ivan Mahonin 07b70f
				found = TRUE;
Ivan Mahonin 8535a3
			}
Ivan Mahonin 8535a3
		}
Ivan Mahonin 3f9996
	}
Ivan Mahonin 3f9996
	
Ivan Mahonin 8535a3
	if (heliCairo) {
Ivan Mahonin 981405
		// draw
Ivan Mahonin 07b70f
		for(int i = 0; i < spritesSorted.count; ++i) {
Ivan Mahonin 07b70f
			Sprite s = (Sprite)(spritesSorted.items[i].value);
Ivan Mahonin 8535a3
			if (s->visible) drawSprite(heliCairo, s);
Ivan Mahonin 8535a3
		}
Ivan Mahonin 981405
		// draw debug marks
Ivan Mahonin 07b70f
		for(int i = 0; i < spritesSorted.count; ++i) {
Ivan Mahonin 07b70f
			Sprite s = (Sprite)(spritesSorted.items[i].value);
Ivan Mahonin 8535a3
			if (s->debug) drawSpriteDebug(heliCairo, s);
Ivan Mahonin 8535a3
		}
Ivan Mahonin 3f9996
	}
Ivan Mahonin 3f9996
}
Ivan Mahonin 3f9996
Ivan Mahonin 3f9996
int mouseIsOver(Sprite sprite) {
Ivan Mahonin 3f9996
	HeliCollider collider;
Ivan Mahonin 3f9996
	prepareCollider(sprite, &collider);
Ivan Mahonin 07b70f
	return heliPointCollision(&collider, mouseX(), mouseY());
Ivan Mahonin 3f9996
}
Ivan Mahonin 3f9996
Ivan Mahonin 07b70f
void heliSpriteUpdate(double dt) {
Ivan Mahonin 8535a3
	// auto-remove
Ivan Mahonin 8535a3
	for(int i = sprites.count - 1; i > 0; --i) {
Ivan Mahonin 07b70f
		Sprite s = (Sprite)sprites.items[i].value;
Ivan Mahonin 1c7488
		if (s->lifeTime >= -HELI_PRECISION) {
Ivan Mahonin 8535a3
			s->lifeTime -= dt;
Ivan Mahonin 1c7488
			if (s->lifeTime <= HELI_PRECISION)
Ivan Mahonin 8535a3
				spriteDestroy(s);
Ivan Mahonin 8535a3
		}
Ivan Mahonin 8535a3
	}
Ivan Mahonin 8535a3
	
Ivan Mahonin 8535a3
	// update
Ivan Mahonin 8535a3
	for(int i = 0; i < sprites.count; ++i) {
Ivan Mahonin 8535a3
		Sprite s = (Sprite)sprites.items[i].value;
Ivan Mahonin 3f9996
		s->x += s->vx*dt;
Ivan Mahonin 3f9996
		s->y += s->vy*dt;
Ivan Mahonin 8535a3
		if (!s->rotateToDirection)
Ivan Mahonin 8535a3
			s->rotation += s->rotationSpeed*dt;
Ivan Mahonin 8535a3
		if (s->playing) spriteNextFrame(s);
Ivan Mahonin 3f9996
	}
Ivan Mahonin 3f9996
}
Ivan Mahonin 8535a3
Ivan Mahonin 8535a3
HeliArray* heliSpriteGetGroups(Sprite sprite)
Ivan Mahonin 8535a3
	{ return &sprite->groups; }
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
void heliSpriteFinish() {
Ivan Mahonin 07b70f
	while(worldGetSpriteCount())
Ivan Mahonin 07b70f
		{ spriteDestroy(worldGetSprite(0)); }
Ivan Mahonin 07b70f
	heliArrayDestroy(&sprites);
Ivan Mahonin 07b70f
	heliArrayDestroy(&spritesSorted);
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f