Blame src/sprite.c

3f9996
3f9996
#include "private.h"
3f9996
3f9996
#include "sprite.h"
8535a3
#include "group.h"
3954ba
#include "drawing.h"
dba3fc
#include "animation.h"
3f9996
a20939
3f9996
struct _Sprite {
3f9996
	double x;
3f9996
	double y;
3f9996
	double rotation;
3f9996
	double width;
3f9996
	double height;
3f9996
	double scale;
3f9996
	
3f9996
	double vx;
3f9996
	double vy;
f80a0a
	double ax;
f80a0a
	double ay;
3f9996
	double rotationSpeed;
3f9996
	
3f9996
	HeliCollider collider;
f80a0a
	double colliderSensitiveDistance;
f80a0a
	int massLevel;
f80a0a
	
f80a0a
	double wx;
f80a0a
	double wy;
f80a0a
	double friction;
f80a0a
	double airFriction;
f80a0a
	double touchFriction;
3f9996
	
3f9996
	int rotateToDirection;
3f9996
	int mirrorX;
3f9996
	int mirrorY;
3f9996
	double depth;
3f9996
	double lifeTime;
8535a3
	int visible;
6eadb0
	int frozen;
3f9996
	int debug;
3f9996
	
8535a3
	double shapeColor[4];
8535a3
	double tintColor[4];
3f9996
	
8535a3
	HeliArray groups;
8535a3
	
dba3fc
	Animation animation;
44355f
	
44355f
	int userTag;
44355f
	char *userText;
44355f
	void *userData;
44355f
	SpriteCallback destroyCallback;
3f9996
};
3f9996
3f9996
static HeliArray sprites;
8535a3
static HeliArray spritesSorted;
3f9996
3f9996
3f9996
static void prepareCollider(Sprite sprite, HeliCollider *collider) {
44355f
	memcpy(collider, &sprite->collider, sizeof(*collider));
44355f
	collider->x += sprite->x;
44355f
	collider->y += sprite->y;
44355f
	collider->rotation += sprite->rotation;
1c7488
	
f8c1ea
	// auto-size
f8c1ea
	if (collider->type == COLLIDER_RECTANGLE) {
f8c1ea
		if (collider->width < -HELI_PRECISION) collider->width = sprite->width;
f8c1ea
		if (collider->height < -HELI_PRECISION) collider->height = sprite->height;
f8c1ea
		if (collider->radius < -HELI_PRECISION) collider->radius = 0;
f8c1ea
	} else
f8c1ea
	if (collider->type == COLLIDER_CIRCLE) {
f8c1ea
		collider->width = 0;
f8c1ea
		collider->height = 0;
f8c1ea
		if (collider->radius < -HELI_PRECISION) collider->radius = 0.5*sprite->width;
f8c1ea
	}
1c7488
	
f8c1ea
	// scale
1c7488
	collider->width *= sprite->scale;
1c7488
	collider->height *= sprite->scale;
1c7488
	collider->radius *= sprite->scale;
f8c1ea
	
f8c1ea
	// fix small values
f8c1ea
	if (collider->width  < HELI_PRECISION) collider->width = HELI_PRECISION;
f8c1ea
	if (collider->height < HELI_PRECISION) collider->height = HELI_PRECISION;
f8c1ea
	if (collider->radius < HELI_PRECISION) collider->radius = HELI_PRECISION;
f8c1ea
	
f8c1ea
	// fix round corners
f8c1ea
	if (collider->type == COLLIDER_RECTANGLE) {
f8c1ea
		double rmax = 0.5*( collider->width < collider->height
f8c1ea
						  ? collider->width : collider->height );
e8cb52
		if (collider->radius >= rmax - HELI_PRECISION) {
f8c1ea
			collider->radius = rmax;
e8cb52
			collider->type = COLLIDER_CIRCLE;
e8cb52
			collider->width = 0;
e8cb52
			collider->height = 0;
e8cb52
		} else {
e8cb52
			collider->width  -= 2*collider->radius;
e8cb52
			collider->height -= 2*collider->radius;
e8cb52
		}
f8c1ea
	}
8535a3
}
8535a3
8535a3
f80a0a
static void autoRotate(Sprite sprite) {
f80a0a
	if (sprite->rotateToDirection)
f80a0a
		sprite->rotation = spriteGetDirection(sprite);
f80a0a
}
f80a0a
f80a0a
e8cb52
Sprite createSpriteEx(double x, double y, double width, double height, Animation animation) {
8eb855
	if (!heliInitialized) return NULL;
8eb855
8535a3
	Sprite s = calloc(1, sizeof(*s));
8535a3
	s->x = x;
8535a3
	s->y = y;
8535a3
	s->width = width;
8535a3
	s->height = height;
e8cb52
	s->animation = animation;
8535a3
	
8535a3
	s->scale = 1;
8535a3
	s->collider.type = COLLIDER_RECTANGLE;
f8c1ea
	s->collider.width = s->collider.height = -1;
f80a0a
	s->colliderSensitiveDistance = 0.001;
44355f
	s->collider.bounciness = 1;
6b5301
	s->friction = 0;
8535a3
	
8535a3
	s->mirrorX = 1;
8535a3
	s->mirrorY = 1;
8535a3
	s->lifeTime = -1;
8535a3
	s->visible = TRUE;
8535a3
	
8535a3
	s->shapeColor[0] = s->shapeColor[1] = s->shapeColor[2] = 0.5;
8535a3
	s->shapeColor[3] = 1;
8535a3
	s->tintColor[0] = s->tintColor[1] = s->tintColor[2] = s->tintColor[3] = 1;
8535a3
	
44355f
	s->userText = heliStringCopy("");
44355f
	
8535a3
	heliArrayInsert(&sprites, -1, s, NULL);
8535a3
	heliArrayInsert(&spritesSorted, -1, s, NULL);
07b70f
	
8eb855
	heliObjectRegister(s, (HeliFreeCallback)&spriteDestroy);
07b70f
	return s;
8535a3
}
8535a3
e8cb52
Sprite createSprite(double x, double y, double width, double height)
e8cb52
	{ return createSpriteEx(x, y, width, height, NULL); }
8535a3
8535a3
void spriteDestroy(Sprite sprite) {
44355f
	if (sprite->destroyCallback) sprite->destroyCallback(sprite);
1c7488
	spriteSetNoAnimation(sprite);
8535a3
	while(sprite->groups.count > 0)
8535a3
		groupRemove((Group)sprite->groups.items[0].value, sprite);
502515
	for(int i = sprites.count - 1; i >= 0; --i)
8535a3
		if (sprites.items[i].value == sprite)
502515
			heliArrayRemove(&sprites, i);
502515
	for(int i = spritesSorted.count - 1; i >= 0; --i)
8535a3
		if (spritesSorted.items[i].value == sprite)
502515
			heliArrayRemove(&spritesSorted, i);
502515
	heliArrayDestroy(&sprite->groups);
87fe10
	heliObjectUnregister(sprite);
44355f
	free(sprite->userText);
8535a3
	free(sprite);
8535a3
}
8535a3
8535a3
void spriteDestroyTimer(Sprite sprite, double lifeTime) {
8535a3
	if (lifeTime <= 0) { spriteDestroy(sprite); return; }
8535a3
	sprite->lifeTime = lifeTime;
8535a3
}
8535a3
53e18e
Sprite spriteClone(Sprite sprite) {
53e18e
	if (!heliInitialized) return NULL;
53e18e
	Sprite s = calloc(1, sizeof(*s));
53e18e
	memcpy(s, sprite, sizeof(*s));
53e18e
	s->lifeTime = -1;
53e18e
	memset(&s->groups, 0, sizeof(s->groups));
44355f
	s->userText = heliStringCopy(sprite->userText);
53e18e
	return s;
53e18e
}
53e18e
53e18e
8535a3
double spriteGetX(Sprite sprite) { return sprite->x; }
8535a3
void spriteSetX(Sprite sprite, double x) { sprite->x = x; }
8535a3
double spriteGetY(Sprite sprite) { return sprite->y; }
8535a3
void spriteSetY(Sprite sprite, double y) { sprite->y = y; }
f80a0a
void spriteSetXY(Sprite sprite, double x, double y)
f80a0a
	{ spriteSetX(sprite, x); spriteSetY(sprite, y); }
8535a3
8535a3
double spriteGetVelocityX(Sprite sprite) { return sprite->vx; }
8535a3
void spriteSetVelocityX(Sprite sprite, double x)
8535a3
	{ spriteSetVelocityXY(sprite, x, sprite->vy); }
8535a3
double spriteGetVelocityY(Sprite sprite) { return sprite->vy; }
8535a3
void spriteSetVelocityY(Sprite sprite, double y)
8535a3
	{ spriteSetVelocityXY(sprite, sprite->vx, y); }
f80a0a
void spriteSetVelocityXY(Sprite sprite, double x, double y) {
f80a0a
	spriteResetTouch(sprite);
f80a0a
	sprite->vx = x; sprite->vy = y;
f80a0a
	autoRotate(sprite);
f80a0a
}
f80a0a
f80a0a
double spriteGetAccelerationX(Sprite sprite) { return sprite->ax; }
f80a0a
void spriteSetAccelerationX(Sprite sprite, double x) { sprite->ax = x; }
f80a0a
double spriteGetAccelerationY(Sprite sprite) { return sprite->ay; }
f80a0a
void spriteSetAccelerationY(Sprite sprite, double y) { sprite->ay = y; }
f80a0a
void spriteSetAccelerationXY(Sprite sprite, double x, double y)
f80a0a
	{ spriteSetAccelerationX(sprite, x); spriteSetAccelerationY(sprite, y); }
8535a3
8535a3
double spriteGetScale(Sprite sprite) { return sprite->scale; }
8535a3
void spriteSetScale(Sprite sprite, double scale) { sprite->scale = scale; }
8535a3
8535a3
double spriteGetWidth(Sprite sprite) { return sprite->width; }
8535a3
void spriteSetWidth(Sprite sprite, double width)
8535a3
	{ sprite->width = width > 0 ? width : 0; }
8535a3
double spriteGetHeight(Sprite sprite) { return sprite->height; }
8535a3
void spriteSetHeight(Sprite sprite, double height)
8535a3
	{ sprite->height = height > 0 ? height : 0; }
714a45
void spriteSetSize(Sprite sprite, double width, double height) {
714a45
	spriteSetWidth(sprite, width);
714a45
	spriteSetHeight(sprite, height);
714a45
}
8535a3
8535a3
int spriteGetRotateToDirection(Sprite sprite) { return sprite->rotateToDirection; }
8535a3
void spriteSetRotateToDirection(Sprite sprite, int rotateToDirection) {
8535a3
	if (rotateToDirection) {
8535a3
		sprite->rotateToDirection = TRUE;
8535a3
		sprite->rotation = spriteGetDirection(sprite);
8535a3
	} else {
8535a3
		sprite->rotateToDirection = FALSE;
8535a3
	}
8535a3
}
8535a3
8535a3
double spriteGetRotation(Sprite sprite) { return sprite->rotation; }
8535a3
void spriteSetRotation(Sprite sprite, double rotation)
8535a3
	{ if (!sprite->rotateToDirection) sprite->rotation = rotation; }
8535a3
8535a3
double spriteGetRotationSpeed(Sprite sprite) { return sprite->rotationSpeed; }
8535a3
void spriteSetRotationSpeed(Sprite sprite, double rotationSpeed)
8535a3
	{ sprite->rotationSpeed = rotationSpeed; }
8535a3
8535a3
int spriteGetMirrorX(Sprite sprite) { return sprite->mirrorX; }
8535a3
void spriteSetMirrorX(Sprite sprite, int mirrorX)
8535a3
	{ sprite->mirrorX = mirrorX < 0 ? -1 : 1; }
8535a3
8535a3
int spriteGetMirrorY(Sprite sprite) { return sprite->mirrorY; }
07b70f
void spriteSetMirrorY(Sprite sprite, int mirrorY)
8535a3
	{ sprite->mirrorY = mirrorY < 0 ? -1 : 1; }
8535a3
8535a3
double spriteGetDepth(Sprite sprite) { return sprite->depth; }
8535a3
void spriteSetDepth(Sprite sprite, double depth)
8535a3
	{ sprite->depth = depth; }
8535a3
8535a3
int spriteGetVisible(Sprite sprite) { return sprite->visible; }
8535a3
void spriteSetVisible(Sprite sprite, int visible)
8535a3
	{ sprite->visible = visible != FALSE; }
8535a3
6eadb0
int spriteGetFrozen(Sprite sprite) { return sprite->frozen; }
6eadb0
void spriteSetFrozen(Sprite sprite, int frozen)
6eadb0
	{ sprite->frozen = frozen != FALSE; }
6eadb0
8535a3
int spriteGetDebug(Sprite sprite) { return sprite->debug; }
8535a3
void spriteSetDebug(Sprite sprite, int debug)
8535a3
	{ sprite->debug = debug != FALSE; }
8535a3
f80a0a
f80a0a
void heliSpriteCollisionApply(Sprite a, Sprite b, HeliCollisionInfo *info) {
f80a0a
	if ( a->massLevel > b->massLevel) {
f80a0a
		b->x -= info->dx;
f80a0a
		b->y -= info->dy;
f80a0a
		b->vx = a->vx - info->vx;
f80a0a
		b->vy = a->vy - info->vy;
f80a0a
		autoRotate(b);
f80a0a
		if (b->wx*b->wx + b->wy*b->wy < info->ax*info->ax + info->ay*info->ay) {
f80a0a
			b->wx = info->ax;
f80a0a
			b->wy = info->ay;
f80a0a
			b->touchFriction = a->friction;
f80a0a
		}
f80a0a
	} else
f80a0a
	if ( b->massLevel > a->massLevel ) {
f80a0a
		a->x += info->dx;
f80a0a
		a->y += info->dy;
f80a0a
		a->vx = b->vx + info->vx;
f80a0a
		a->vy = b->vy + info->vy;
f80a0a
		autoRotate(a);
f80a0a
		if (a->wx*a->wx + a->wy*a->wy < info->ax*info->ax + info->ay*info->ay) {
f80a0a
			a->wx = -info->ax;
f80a0a
			a->wy = -info->ay;
f80a0a
			a->touchFriction = b->friction;
f80a0a
		}
f80a0a
	} else {
f80a0a
		double vxAvg = 0.5*(a->vx + b->vx);
f80a0a
		double vyAvg = 0.5*(a->vy + b->vy);
f80a0a
		double wx = info->ax;
f80a0a
		double wy = info->ay;
f80a0a
		double w = wx*wx + wy*wy;
f80a0a
		
f80a0a
		a->x += 0.5*info->dx;
f80a0a
		a->y += 0.5*info->dy;
f80a0a
		a->vx = vxAvg + 0.5*info->vx;
f80a0a
		a->vy = vyAvg + 0.5*info->vy;
f80a0a
		autoRotate(a);
f80a0a
		if (a->wx*a->wx + a->wy*a->wy < w) {
f80a0a
			a->wx = -wx;
f80a0a
			a->wy = -wy;
f80a0a
			a->touchFriction = b->friction;
f80a0a
		}
f80a0a
		
f80a0a
		b->x -= 0.5*info->dx;
f80a0a
		b->y -= 0.5*info->dy;
f80a0a
		b->vx = vxAvg - 0.5*info->vx;
f80a0a
		b->vy = vyAvg - 0.5*info->vy;
f80a0a
		autoRotate(b);
f80a0a
		if (b->wx*b->wx + b->wy*b->wy < w) {
f80a0a
			b->wx = wx;
f80a0a
			b->wy = wy;
f80a0a
			b->touchFriction = a->friction;
f80a0a
		}
f80a0a
	}
f80a0a
}
f80a0a
f80a0a
int heliSpriteCollisionCheck(Sprite a, Sprite b, HeliCollisionInfo *info) {
f8c1ea
	if (a == b) return FALSE;
44355f
	HeliCollider ca = {}, cb = {};
1c7488
	prepareCollider(a, &ca);
1c7488
	prepareCollider(b, &cb);
f80a0a
	memset(info, 0, sizeof(*info));
f80a0a
	info->vx = a->vx - b->vx;
f80a0a
	info->vy = a->vy - b->vy;
f80a0a
	info->ax = a->ax - b->ax;
f80a0a
	info->ay = a->ay - b->ay;
f80a0a
	return heliCheckCollision(&ca, &cb, info, a->colliderSensitiveDistance + b->colliderSensitiveDistance);
f80a0a
}
f80a0a
f80a0a
int spriteOverlap(Sprite a, Sprite b) {
f80a0a
	HeliCollisionInfo info = {};
f80a0a
	heliSpriteCollisionCheck(a, b, &info);
f80a0a
	return info.actualCollision;
8535a3
}
8535a3
f80a0a
int spriteCollide(Sprite a, Sprite b) {
f80a0a
	HeliCollisionInfo info = {};
f80a0a
	if (heliSpriteCollisionCheck(a, b, &info)) {
f80a0a
		heliSpriteCollisionApply(a, b, &info);
f80a0a
		return info.actualCollision;
f80a0a
	}
f80a0a
	return FALSE;
f80a0a
}
44355f
8535a3
44355f
double spriteGetBounciness(Sprite sprite)
44355f
	{ return sprite->collider.bounciness; }
8535a3
void spriteSetBounciness(Sprite sprite, double bounciness)
44355f
	{ sprite->collider.bounciness = bounciness > 0 ? bounciness : 0; }
44355f
double spriteGetBouncinessThreshold(Sprite sprite)
44355f
	{ return sprite->collider.bouncinessThreshold; }
44355f
void spriteSetBouncinessThreshold(Sprite sprite, double bouncinessThreshold)
44355f
	{ sprite->collider.bouncinessThreshold = bouncinessThreshold > 0 ? bouncinessThreshold : 0; }
44355f
f80a0a
double spriteGetFriction(Sprite sprite)
f80a0a
	{ return sprite->friction; }
f80a0a
void spriteSetFriction(Sprite sprite, double friction)
f80a0a
	{ sprite->friction = friction > 0 ? friction : 0; }
f80a0a
double spriteGetAirFriction(Sprite sprite)
f80a0a
	{ return sprite->airFriction; }
f80a0a
void spriteSetAirFriction(Sprite sprite, double friction)
f80a0a
	{ sprite->airFriction = friction > 0 ? friction : 0; }
f80a0a
f80a0a
int spriteGetMassLevel(Sprite sprite)
f80a0a
	{ return sprite->massLevel; }
f80a0a
void spriteSetMassLevel(Sprite sprite, int massLevel)
f80a0a
	{ sprite->massLevel = massLevel; }
f80a0a
f80a0a
double spriteGetTouchWeight(Sprite sprite)
f80a0a
	{ return sqrt(sprite->wx*sprite->wx + sprite->wy*sprite->wy); }
f80a0a
double spriteGetTouchWeightX(Sprite sprite)
f80a0a
	{ return sprite->wx; }
f80a0a
double spriteGetTouchWeightY(Sprite sprite)
f80a0a
	{ return sprite->wy; }
f80a0a
double spriteGetTouchFriction(Sprite sprite)
f80a0a
	{ return sprite->touchFriction; }
f80a0a
void spriteResetTouch(Sprite sprite) {
f80a0a
	sprite->wx = 0;
f80a0a
	sprite->wy = 0;
f80a0a
	sprite->touchFriction = 0;
f80a0a
}
44355f
f80a0a
double spriteGetColliderSensitiveDistance(Sprite sprite)
f80a0a
	{ return sprite->colliderSensitiveDistance; }
f80a0a
void spriteSetColliderSensitiveDistance(Sprite sprite, double distance)
f80a0a
	{ sprite->colliderSensitiveDistance = distance > 10*HELI_PRECISION ? distance : 10*HELI_PRECISION; }
8535a3
f8c1ea
void spriteSetCollider(Sprite sprite, Collider type, double xOffset, double yOffset, double rotationOffset)
f8c1ea
	{ spriteSetColliderEx(sprite, type, xOffset, yOffset, rotationOffset, -1, -1, -1); }
f8c1ea
void spriteSetColliderCircle(Sprite sprite, double xOffset, double yOffset, double radius)
f8c1ea
	{ spriteSetColliderEx(sprite, COLLIDER_CIRCLE, xOffset, yOffset, 0, 0, 0, radius); }
f8c1ea
void spriteSetColliderRectangle(
f8c1ea
	Sprite sprite, double xOffset, double yOffset, double rotationOffset,
f8c1ea
	double width, double height, double cornersRadius )
f8c1ea
{
f8c1ea
	spriteSetColliderEx(
f8c1ea
		sprite, COLLIDER_RECTANGLE,
f8c1ea
		xOffset, yOffset, rotationOffset,
f8c1ea
		width, height, cornersRadius);
f8c1ea
}
f8c1ea
f8c1ea
void spriteSetColliderEx(
f8c1ea
	Sprite sprite, Collider type,
f8c1ea
	double xOffset, double yOffset, double rotationOffset,
f8c1ea
	double width, double height, double radius )
8535a3
{
8535a3
	sprite->collider.type = type;
8535a3
	sprite->collider.x = xOffset;
8535a3
	sprite->collider.y = yOffset;
8535a3
	sprite->collider.rotation = rotationOffset;
f8c1ea
	if (type == COLLIDER_CIRCLE) {
f8c1ea
		sprite->collider.width = 0;
f8c1ea
		sprite->collider.height = 0;
f8c1ea
		sprite->collider.radius = radius;
f8c1ea
	} else
f8c1ea
	if (type == COLLIDER_RECTANGLE) {
f8c1ea
		sprite->collider.width = width;
f8c1ea
		sprite->collider.height = height;
f8c1ea
		sprite->collider.radius = radius;
f8c1ea
	} else {
f8c1ea
		sprite->collider.width = 0;
f8c1ea
		sprite->collider.height = 0;
f8c1ea
		sprite->collider.radius = 0;
f8c1ea
	}
8535a3
}
8535a3
53e18e
int spriteIsPointInside(Sprite sprite, double x, double y) {
53e18e
	HeliCollider collider;
53e18e
	prepareCollider(sprite, &collider);
53e18e
	return heliPointCollision(&collider, x, y);
53e18e
}
53e18e
f8dca4
Animation spriteGetAnimation(Sprite sprite)
f8dca4
	{ return sprite->animation; }
f8dca4
dba3fc
void spriteSetAnimation(Sprite sprite, Animation animation)
dba3fc
	{ sprite->animation = animation; }
8535a3
dba3fc
void spriteSetNoAnimation(Sprite sprite)
dba3fc
	{ spriteSetAnimation(sprite, NULL); }
3f9996
83acad
unsigned int spriteGetShapeColor(Sprite sprite) {
83acad
	return colorByRGBA(
83acad
		sprite->shapeColor[0],
83acad
		sprite->shapeColor[1],
83acad
		sprite->shapeColor[2],
83acad
		sprite->shapeColor[3] );
83acad
}
d4e89f
void spriteSetShapeColor(Sprite sprite, unsigned int colorCode)
d4e89f
	{ heliColorToDouble(colorCode, sprite->shapeColor); }
83acad
83acad
unsigned int spriteGetTintColor(Sprite sprite) {
83acad
	return colorByRGBA(
83acad
		sprite->tintColor[0],
83acad
		sprite->tintColor[1],
83acad
		sprite->tintColor[2],
83acad
		sprite->tintColor[3] );
83acad
}
d4e89f
void spriteSetTintColor(Sprite sprite, unsigned int colorCode)
d4e89f
	{ heliColorToDouble(colorCode, sprite->tintColor); }
8535a3
8535a3
void spriteSetSpeedAndDirection(Sprite sprite, double speed, double angle) {
8535a3
	double a = angle*(PI/180);
8535a3
	spriteSetVelocityXY(sprite, cos(a)*speed, sin(a)*speed);
8535a3
}
8535a3
8535a3
double spriteGetSpeed(Sprite sprite)
8535a3
	{ return sqrt(sprite->vx*sprite->vx + sprite->vy*sprite->vy); }
8535a3
double spriteGetDirection(Sprite sprite) {
1c7488
	return fabs(sprite->vx) > HELI_PRECISION || fabs(sprite->vy) > HELI_PRECISION
8535a3
	     ? atan2(sprite->vy, sprite->vx)*(180/PI) : 0;
8535a3
}
8535a3
void spritePointTo(Sprite sprite, double x, double y)
981405
	{ spriteSetRotation( sprite, atan2(y - sprite->y, x - sprite->x)*(180/PI) ); }
909bc2
void spriteMoveBy(Sprite sprite, double dx, double dy)
909bc2
	{ spriteSetXY(sprite, spriteGetX(sprite) + dx, spriteGetY(sprite) + dy); }
909bc2
void spriteMoveToDirection(Sprite sprite, double distance, double angle) {
909bc2
	double a = angle*PI/180.0;
909bc2
	spriteMoveBy(sprite, cos(a)*distance, sin(a)*distance);
909bc2
}
8535a3
8535a3
double spriteGetScaledWidth(Sprite sprite)
8535a3
	{ return sprite->width * sprite->scale; }
8535a3
double spriteGetScaledHeight(Sprite sprite)
8535a3
	{ return sprite->height * sprite->scale; }
8535a3
3f9996
44355f
int spriteGetUserTag(Sprite sprite)
44355f
	{ return sprite->userTag; }
44355f
void spriteSetUserTag(Sprite sprite, int tag)
44355f
	{ sprite->userTag = tag; }
44355f
const char* spriteGetUserText(Sprite sprite)
44355f
	{ return sprite->userText; }
44355f
void spriteSetUserText(Sprite sprite, const char *text) {
44355f
	if (sprite->userText == text) return;
44355f
	free(sprite->userText);
44355f
	sprite->userText = heliStringCopy(text);
44355f
}
44355f
void* spriteGetUserData(Sprite sprite)
44355f
	{ return sprite->userData; }
44355f
void spriteSetUserData(Sprite sprite, void *data)
44355f
	{ sprite->userData = data; }
44355f
44355f
void spriteSetDestroy(Sprite sprite, SpriteCallback destroy)
44355f
	{ sprite->destroyCallback = destroy; }
44355f
3f9996
1024d1
int spritesGetCount()
3f9996
	{ return sprites.count; }
1024d1
Sprite spritesGet(int i)
8535a3
	{ return (Sprite)heliArrayGetValue(&sprites, i); }
3f9996
8535a3
6eadb0
void heliSpriteDrawDebug(Sprite s) {
deef1d
	saveState();
deef1d
	translate(s->x, s->y);
deef1d
	rotate(s->rotation);
8535a3
	
deef1d
	strokeWidth(0.5);
8535a3
	
deef1d
	double hw = 0.5 * s->scale * s->width;
deef1d
	double hh = 0.5 * s->scale * s->height;
3954ba
	
d4e89f
	noFill();
d4e89f
	stroke(colorByRGBA(0, 0, 0, 0.75));
3954ba
3954ba
	// frame
deef1d
	rect(-hw, -hh, 2*hw, 2*hh);
3954ba
	
3954ba
	// center cross
deef1d
	line(-hw/4, 0, hw/4, 0);
deef1d
	line(0, -hh/4, 0, hh/4);
3954ba
	
e8cb52
	// collider
e8cb52
	HeliCollider cl = {};
e8cb52
	prepareCollider(s, &cl);
e8cb52
	saveState();
e8cb52
	rotate(-s->rotation);
e8cb52
	translate(-s->x, -s->y);
e8cb52
	translate(cl.x, cl.y);
e8cb52
	rotate(cl.rotation);
e8cb52
	stroke(colorByRGBA(0, 0, 1, 0.75));
e8cb52
	double cw = cl.width + 2*cl.radius;
e8cb52
	double ch = cl.height + 2*cl.radius;
e8cb52
	rectRounded(-cw/2, -ch/2, cw, ch, cl.radius);
e8cb52
	restoreState();
e8cb52
	
3954ba
	// depth
981405
	double s1 = hw*0.25;
8535a3
	double s2 = hh*0.5;
deef1d
	double ss = s1 < s2 ? s1 : s2;
deef1d
	textSize(ss);
deef1d
	textAlign(HALIGN_LEFT, VALIGN_BOTTOM);
5d2371
	textf(0.1*ss - hw, hh - 0.1*ss, "%lf", s->depth);
3954ba
	
deef1d
	restoreState();
8535a3
}
8535a3
6eadb0
void heliSpriteDraw(Sprite s) {
deef1d
	double w = 0.5*s->scale*s->width;
deef1d
	double h = 0.5*s->scale*s->height;
deef1d
	if (w < HELI_PRECISION || h < HELI_PRECISION) return;
deef1d
	w *= s->mirrorX;
deef1d
	h *= s->mirrorY;
8535a3
	
deef1d
	unsigned int texid = s->animation ? animationGetGLTexId(s->animation) : 0u;
a20939
	double color[4] = {
a20939
		(texid ? 1.0 : s->shapeColor[0])*s->tintColor[0],
a20939
		(texid ? 1.0 : s->shapeColor[1])*s->tintColor[1],
a20939
		(texid ? 1.0 : s->shapeColor[2])*s->tintColor[2],
a20939
		(texid ? 1.0 : s->shapeColor[3])*s->tintColor[3] };
a20939
	if (color[3] < HELI_PRECISION) return;
deef1d
deef1d
	saveState();
deef1d
	translate(s->x, s->y);
deef1d
	rotate(s->rotation);
deef1d
	noStroke();
d4e89f
	fill(colorByRGBA(color[0], color[1], color[2], color[3]));
f8dca4
	rectTextured(s->animation, -w, -h, 2*w, 2*h);
deef1d
	restoreState();
8535a3
}
8535a3
44355f
void heliSpriteSort(HeliArray *sprites) {
3f9996
	int found = TRUE;
3f9996
	while(found) {
3f9996
		found = FALSE;
8535a3
		// forward
44355f
		for(int i = 1; i < sprites->count; ++i) {
44355f
			if ( ((Sprite)sprites->items[i-1].value)->depth
44355f
			   < ((Sprite)sprites->items[i  ].value)->depth )
07b70f
			{
44355f
				void *x = sprites->items[i].value;
44355f
				sprites->items[i].value = sprites->items[i-1].value;
44355f
				sprites->items[i-1].value = x;
3f9996
				found = TRUE;
3f9996
			}
3f9996
		}
8535a3
		if (!found) break;
8535a3
		// backward
8535a3
		found = FALSE;
44355f
		for(int i = sprites->count - 1; i > 0; --i) {
44355f
			if ( ((Sprite)sprites->items[i-1].value)->depth
44355f
			   < ((Sprite)sprites->items[i  ].value)->depth )
07b70f
			{
44355f
				void *x = sprites->items[i].value;
44355f
				sprites->items[i].value = sprites->items[i-1].value;
44355f
				sprites->items[i-1].value = x;
07b70f
				found = TRUE;
8535a3
			}
8535a3
		}
3f9996
	}
44355f
}
44355f
6eadb0
void spriteDraw(Sprite sprite) {
6eadb0
	if (sprite->visible) heliSpriteDraw(sprite);
6eadb0
	if (sprite->debug) heliSpriteDrawDebug(sprite);
6eadb0
}
6eadb0
44355f
void drawSprites() {
44355f
	heliSpriteSort(&spritesSorted);
a20939
	for(int i = 0; i < spritesSorted.count; ++i) {
a20939
		Sprite s = (Sprite)(spritesSorted.items[i].value);
6eadb0
		if (s->visible) heliSpriteDraw(s);
a20939
	}
a20939
	for(int i = 0; i < spritesSorted.count; ++i) {
a20939
		Sprite s = (Sprite)(spritesSorted.items[i].value);
6eadb0
		if (s->debug) heliSpriteDrawDebug(s);
3f9996
	}
3f9996
}
3f9996
1024d1
int spriteUpdate(Sprite sprite, double time) {
1024d1
	if (sprite->lifeTime >= -HELI_PRECISION) {
1024d1
		sprite->lifeTime -= time;
1024d1
		if (sprite->lifeTime <= HELI_PRECISION) {
1024d1
			sprite->lifeTime = 0;
1024d1
			spriteDestroy(sprite);
1024d1
			return FALSE;
83acad
		}
83acad
	}
1024d1
	
1024d1
	double vx = sprite->vx + sprite->ax*time;
1024d1
	double vy = sprite->vy + sprite->ay*time;
1024d1
	
1024d1
	double weight = spriteGetTouchWeight(sprite);
1024d1
	double dvf = fabs(weight * sprite->friction * sprite->touchFriction)*time;
1024d1
	if (dvf > HELI_PRECISION) {
1024d1
		double nx = sprite->wx / weight;
1024d1
		double ny = sprite->wy / weight;
1024d1
		double vb = nx*vx + ny*vy;
1024d1
		double vs = ny*vx - nx*vy;
1024d1
		if (vs >  dvf + HELI_PRECISION) vs -= dvf; else
1024d1
		if (vs < -dvf - HELI_PRECISION) vs += dvf; else vs = 0;
1024d1
		vx = nx*vb + ny*vs;
1024d1
		vy = ny*vb - nx*vs;
8535a3
	}
8535a3
	
1024d1
	double dvaf = fabs(sprite->airFriction)*time;
1024d1
	if (dvaf > HELI_PRECISION) {
1024d1
		double v = sqrt(vx*vx + vy*vy), vn = v;
1024d1
		if (v >  dvaf + HELI_PRECISION) vn -= dvaf; else
1024d1
		if (v < -dvaf - HELI_PRECISION) vn += dvaf; else vn = 0;
1024d1
		double k = vn > HELI_PRECISION ? vn/v : 0;
1024d1
		vx *= k;
1024d1
		vy *= k;
1024d1
	}
1024d1
	
1024d1
	sprite->vx = vx;
1024d1
	sprite->vy = vy;
1024d1
	autoRotate(sprite);
1024d1
	spriteResetTouch(sprite);
1024d1
	
1024d1
	sprite->x += sprite->vx * time;
1024d1
	sprite->y += sprite->vy * time;
1024d1
	if (!sprite->rotateToDirection)
1024d1
		sprite->rotation += sprite->rotationSpeed*time;
1024d1
	
1024d1
	return TRUE;
1024d1
}
1024d1
1024d1
void heliSpriteUpdate(double dt) {
1024d1
	for(int i = spritesGetCount() - 1; i >= 0; --i) {
1024d1
		if (i < spritesGetCount()) {
1024d1
			Sprite s = spritesGet(i);
1024d1
			if (!spriteGetFrozen(s))
1024d1
				spriteUpdate(spritesGet(i), dt);
f80a0a
		}
3f9996
	}
3f9996
}
8535a3
8535a3
HeliArray* heliSpriteGetGroups(Sprite sprite)
8535a3
	{ return &sprite->groups; }
07b70f
07b70f
void heliSpriteFinish() {
1024d1
	while(spritesGetCount())
1024d1
		{ spriteDestroy(spritesGet(0)); }
07b70f
	heliArrayDestroy(&sprites);
07b70f
	heliArrayDestroy(&spritesSorted);
07b70f
}
07b70f
07b70f