Blame src/sprite.c

3f9996
3f9996
#include "private.h"
3f9996
3f9996
#include "sprite.h"
3f9996
#include "world.h"
8535a3
#include "group.h"
3954ba
#include "drawing.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;
3f9996
	double rotationSpeed;
3f9996
	
3f9996
	HeliCollider collider;
3f9996
	double bounciness;
3f9996
	
3f9996
	int rotateToDirection;
3f9996
	int mirrorX;
3f9996
	int mirrorY;
3f9996
	double depth;
3f9996
	double lifeTime;
8535a3
	int visible;
8935bc
	int tag;
3f9996
	int debug;
3f9996
	
8535a3
	double shapeColor[4];
8535a3
	double tintColor[4];
3f9996
	
3f9996
	int playing;
3f9996
	int frame;
3f9996
	
8535a3
	HeliArray groups;
8535a3
	
1c7488
	HeliAnimation *animation;
3f9996
};
3f9996
3f9996
static HeliArray sprites;
8535a3
static HeliArray spritesSorted;
3f9996
3f9996
3f9996
static void prepareCollider(Sprite sprite, HeliCollider *collider) {
8535a3
	collider->type = sprite->collider.type;
8535a3
	collider->x = sprite->collider.x + sprite->x;
8535a3
	collider->y = sprite->collider.y + sprite->y;
1c7488
	collider->width = sprite->collider.width;
1c7488
	collider->height = sprite->collider.height;
1c7488
	collider->radius = sprite->collider.radius;
8535a3
	collider->rotation = sprite->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 );
f8c1ea
		if (collider->radius >= rmax - HELI_PRECISION)
f8c1ea
			collider->radius = rmax;
f8c1ea
		collider->width  -= 2*collider->radius;
f8c1ea
		collider->height -= 2*collider->radius;
f8c1ea
	}
8535a3
}
8535a3
8535a3
8535a3
Sprite createSpriteEx(double x, double y, double width, double height) {
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;
8535a3
	
8535a3
	s->scale = 1;
8535a3
	s->collider.type = COLLIDER_RECTANGLE;
f8c1ea
	s->collider.width = s->collider.height = -1;
f8c1ea
	s->bounciness = 1;
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
	
8535a3
	heliArrayInsert(&sprites, -1, s, NULL);
8535a3
	heliArrayInsert(&spritesSorted, -1, s, NULL);
07b70f
	
8eb855
	heliObjectRegister(s, (HeliFreeCallback)&spriteDestroy);
07b70f
	return s;
8535a3
}
8535a3
8535a3
Sprite createSprite(double x, double y)
8535a3
	{ return createSpriteEx(x, y, 100, 100); }
8535a3
8535a3
void spriteDestroy(Sprite sprite) {
8eb855
	heliObjectUnregister(sprite);
1c7488
	spriteSetNoAnimation(sprite);
8535a3
	while(sprite->groups.count > 0)
8535a3
		groupRemove((Group)sprite->groups.items[0].value, sprite);
8535a3
	for(int i = 0; i < sprites.count; ++i)
8535a3
		if (sprites.items[i].value == sprite)
8535a3
			{ heliArrayRemove(&sprites, i); break; }
8535a3
	for(int i = 0; i < spritesSorted.count; ++i)
8535a3
		if (spritesSorted.items[i].value == sprite)
8535a3
			{ heliArrayRemove(&spritesSorted, i); break; }
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
8535a3
double spriteGetX(Sprite sprite) { return sprite->x; }
8535a3
void spriteSetX(Sprite sprite, double x) { sprite->x = x; }
8535a3
8535a3
double spriteGetY(Sprite sprite) { return sprite->y; }
8535a3
void spriteSetY(Sprite sprite, double y) { sprite->y = y; }
8535a3
8535a3
double spriteGetVelocityX(Sprite sprite) { return sprite->vx; }
8535a3
void spriteSetVelocityX(Sprite sprite, double x)
8535a3
	{ spriteSetVelocityXY(sprite, x, sprite->vy); }
8535a3
8535a3
double spriteGetVelocityY(Sprite sprite) { return sprite->vy; }
8535a3
void spriteSetVelocityY(Sprite sprite, double y)
8535a3
	{ spriteSetVelocityXY(sprite, sprite->vx, 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
8535a3
double spriteGetHeight(Sprite sprite) { return sprite->height; }
8535a3
void spriteSetHeight(Sprite sprite, double height)
8535a3
	{ sprite->height = height > 0 ? height : 0; }
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
8935bc
int spriteGetTag(Sprite sprite) { return sprite->tag; }
8935bc
void spriteSetTag(Sprite sprite, int tag)
8935bc
	{ sprite->tag = tag; }
8935bc
8535a3
int spriteGetDebug(Sprite sprite) { return sprite->debug; }
8535a3
void spriteSetDebug(Sprite sprite, int debug)
8535a3
	{ sprite->debug = debug != FALSE; }
8535a3
8535a3
int spriteOverlap(Sprite a, Sprite b)
8535a3
	{ return spriteCollideEx(a, b, TRUE, TRUE, 0); }
8535a3
int spriteCollide(Sprite a, Sprite b, double bounciness)
8535a3
	{ return spriteCollideEx(a, b, FALSE, FALSE, bounciness); }
8535a3
int spriteBounceOff(Sprite sprite, Sprite other, double bounciness)
8535a3
	{ return spriteCollideEx(sprite, other, FALSE, TRUE, bounciness); }
8535a3
int spritePush(Sprite sprite, Sprite other, double bounciness)
8535a3
	{ return spriteCollideEx(sprite, other, TRUE, FALSE, bounciness); }
8535a3
8535a3
int spriteCollideEx(Sprite a, Sprite b, int keepVelocityA, int keepVelocityB, double bounciness) {
f8c1ea
	if (a == b) return FALSE;
1c7488
	HeliCollider ca, cb;
1c7488
	prepareCollider(a, &ca);
1c7488
	prepareCollider(b, &cb);
1c7488
	double dx = 0, dy = 0;
1c7488
	double vx = a->vx - b->vx;
1c7488
	double vy = a->vy - b->vy;
1c7488
	bounciness = fabs(bounciness * a->bounciness * b->bounciness);
1c7488
	if (!heliCheckCollision(&ca, &cb, &dx, &dy, &vx, &vy, bounciness))
1c7488
		return FALSE;
1c7488
	
1c7488
	if ( keepVelocityA && !keepVelocityB) {
1c7488
		b->x -= dx;
1c7488
		b->y -= dy;
f8c1ea
		spriteSetVelocityXY(b, a->vx - vx, a->vy - vy);
1c7488
	} else
1c7488
	if (!keepVelocityA &&  keepVelocityB) {
1c7488
		a->x += dx;
1c7488
		a->y += dy;
f8c1ea
		spriteSetVelocityXY(a, b->vx + vx, b->vy + vy);
1c7488
	} else
1c7488
	if (!keepVelocityA && !keepVelocityB) {
1c7488
		double vxAvg = 0.5*(a->vx + b->vx);
1c7488
		double vyAvg = 0.5*(a->vy + b->vy);
1c7488
		
1c7488
		a->x += 0.5*dx;
1c7488
		a->y += 0.5*dy;
f8c1ea
		spriteSetVelocityXY(a, vxAvg + 0.5*vx, vyAvg + 0.5*vy);
1c7488
		
1c7488
		b->x -= 0.5*dx;
1c7488
		b->y -= 0.5*dy;
f8c1ea
		spriteSetVelocityXY(b, vxAvg - 0.5*vx, vyAvg - 0.5*vy);
1c7488
	}
1c7488
	
1c7488
	return TRUE;
8535a3
}
8535a3
8535a3
8535a3
double spriteGetBounciness(Sprite sprite) { return sprite->bounciness; }
8535a3
void spriteSetBounciness(Sprite sprite, double bounciness)
8535a3
	{ sprite->bounciness = bounciness > 0 ? bounciness : 0; }
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
1c7488
void spriteSetAnimation(Sprite sprite, const char *path) {
1c7488
	HeliAnimation *prev = sprite->animation;
1c7488
	sprite->animation = heliAnimationLoad(path);
1c7488
	if (prev) heliAnimationUnref(prev);
8535a3
	spriteSetFrame(sprite, sprite->frame);
8535a3
}
8535a3
1c7488
void spriteSetNoAnimation(Sprite sprite) {
1c7488
	if (sprite->animation) heliAnimationUnref(sprite->animation);
1c7488
	sprite->animation = NULL;
8935bc
}
8935bc
8535a3
void spritePlay(Sprite sprite) { sprite->playing = TRUE; }
8535a3
void spritePause(Sprite sprite) { sprite->playing = FALSE; }
8535a3
void spriteNextFrame(Sprite sprite) { spriteSetFrame(sprite, sprite->frame + 1); }
8535a3
8535a3
void spriteSetFrame(Sprite sprite, int frame) {
1c7488
	sprite->frame = frame >= 0 && sprite->animation && sprite->animation->frames.count > 0
1c7488
				  ? frame % sprite->animation->frames.count : 0;
3f9996
}
3f9996
8535a3
void spriteSetShapeColor(Sprite sprite, const char *color)
8535a3
	{ heliParseColor(color, sprite->shapeColor); }
8535a3
void spriteSetTintColor(Sprite sprite, const char *color)
8535a3
	{ heliParseColor(color, sprite->tintColor); }
8535a3
8535a3
void spriteSetVelocityXY(Sprite sprite, double x, double y) {
8535a3
	sprite->vx = x;
8535a3
	sprite->vy = y;
8535a3
	if (sprite->rotateToDirection)
8535a3
		sprite->rotation = spriteGetDirection(sprite);
8535a3
}
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) ); }
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
3f9996
3f9996
int worldGetSpriteCount()
3f9996
	{ return sprites.count; }
3f9996
07b70f
Sprite worldGetSprite(int i)
8535a3
	{ return (Sprite)heliArrayGetValue(&sprites, i); }
3f9996
8535a3
a20939
static void drawSpriteDebug(Sprite s) {
3954ba
	pushDrawingState();
3954ba
	strokeWeight(0.5);
8535a3
	
3954ba
	double a = s->rotation*(PI/180);
3954ba
	double sn = sin(a);
3954ba
	double cs = cos(a);
3954ba
	double hw = 0.5 * s->scale * s->mirrorX * s->width;
3954ba
	double hh = 0.5 * s->scale * s->mirrorX * s->height;
8535a3
	
3954ba
	double ltx = -hw*cs + hh*sn;
3954ba
	double lty = -hw*sn - hh*cs;
3954ba
	double rtx =  hw*cs + hh*sn;
3954ba
	double rty =  hw*sn - hh*cs;
3954ba
	
3954ba
	fill("transparent");
3954ba
	stroke(rgba(0, 0, 0, 0.75));
3954ba
3954ba
	// frame
3954ba
	moveTo(s->x + ltx, s->y + lty);
3954ba
	lineTo(s->x + rtx, s->y + rty);
3954ba
	lineTo(s->x - ltx, s->y - lty);
3954ba
	lineTo(s->x - rtx, s->y - rty);
3954ba
	closePath();
3954ba
	
3954ba
	// center cross
3954ba
	line( s->x - cs*hw*0.25, s->y - sn*hw*0.25,
3954ba
	      s->x + cs*hw*0.25, s->y + sn*hw*0.25 );
3954ba
	line( s->x + sn*hh*0.25, s->y - cs*hh*0.25,
3954ba
	      s->x - sn*hh*0.25, s->y + cs*hh*0.25 );
3954ba
	
3954ba
	// depth
3954ba
	stroke(rgba(0, 0, 0, 0.75));
8535a3
	char buf[1024];
09c823
	snprintf(buf, sizeof(buf)-1, "%lf", s->depth);
981405
	double s1 = hw*0.25;
8535a3
	double s2 = hh*0.5;
3954ba
	textSize(s1 < s2 ? s1 : s2);
3954ba
	textAlign(HALIGN_CENTER, VALIGN_CENTER);
3954ba
	text(buf, s->x, s->y);
3954ba
	
3954ba
	popDrawingState();
8535a3
}
8535a3
a20939
static void drawSprite(Sprite s) {
59dae5
	const double aaBorder = 1;
8535a3
	
a20939
	unsigned int texid = s->animation
a20939
	                   ? (unsigned int)(size_t)heliArrayGetValue(&s->animation->frames, s->frame) : 0u;
8535a3
	
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] };
8535a3
	
a20939
	if (color[3] < HELI_PRECISION) return;
a20939
	
a20939
	double w = 0.5*s->scale*s->width;
a20939
	double h = 0.5*s->scale*s->height;
a20939
	if (w < HELI_PRECISION || h < HELI_PRECISION) return;
a20939
	
a20939
	glPushMatrix();
a20939
	glTranslated(s->x, s->y, 0);
a20939
	glRotated(s->rotation, 0, 0, 1);
a20939
	
a20939
	if (aaBorder <= HELI_PRECISION) {
a20939
		// use OpenGL multisample antialiasing
a20939
		
a20939
		glEnable(GL_MULTISAMPLE);
650f35
		
a20939
		const int vcnt = 4;
a20939
		const double vertices[][2] = {
a20939
			{ -w, -h },
a20939
			{  w, -h },
a20939
			{ -w,  h },
a20939
			{  w,  h } };
a20939
		glColor4dv(color);
a20939
		if (texid) {
a20939
			const double texCoords[][2] = {
a20939
				{0, 0},
a20939
				{1, 0},
a20939
				{0, 1},
a20939
				{1, 1} };
a20939
			glEnable(GL_TEXTURE_2D);
a20939
			glBindTexture(GL_TEXTURE_2D, texid);
a20939
			
a20939
			glBegin(GL_TRIANGLE_STRIP);
a20939
			for(int i = 0; i < vcnt; ++i) {
a20939
				glTexCoord2dv(texCoords[i]);
a20939
				glVertex2dv(vertices[i]);
650f35
			}
a20939
			glEnd();
a20939
			
a20939
			glBindTexture(GL_TEXTURE_2D, 0);
a20939
			glDisable(GL_TEXTURE_2D);
650f35
		} else {
a20939
			glBegin(GL_TRIANGLE_STRIP);
a20939
			for(int i = 0; i < vcnt; ++i)
a20939
				glVertex2dv(vertices[i]);
a20939
			glEnd();
650f35
		}
650f35
		
a20939
		glDisable(GL_MULTISAMPLE);
a20939
	} else {
a20939
		// make antialiased borde manually
a20939
		
a20939
		double sideColor[4] = { color[0], color[1], color[2], 0 };
a20939
		
a20939
		double w0 = w - 0.5*aaBorder;
a20939
		double w1 = w + 0.5*aaBorder;
a20939
		if (w0 < HELI_PRECISION) w0 = 0;
a20939
		double h0 = h - 0.5*aaBorder;
a20939
		double h1 = h + 0.5*aaBorder;
a20939
		if (h0 < HELI_PRECISION) h0 = 0;
a20939
		
a20939
		const double k = (w1 < aaBorder ? w1/aaBorder : 1)
a20939
		               * (h1 < aaBorder ? h1/aaBorder : 1);
a20939
		color[3] *= k;
a20939
		
a20939
		w0 *= s->mirrorX;
a20939
		w1 *= s->mirrorX;
a20939
		h0 *= s->mirrorY;
a20939
		h1 *= s->mirrorY;
a20939
		
a20939
		const int vcnt = 14;
a20939
		
a20939
		const double vertices[][2] = {
a20939
			{ -w1, -h1 }, { -w0, -h0 },
a20939
			{  w1, -h1 }, {  w0, -h0 },
a20939
			{  w1,  h1 }, {  w0,  h0 },
a20939
			{ -w1,  h1 }, { -w0,  h0 },
a20939
			{ -w1, -h1 }, { -w0, -h0 },
a20939
			{ -w0, -h0 },
a20939
			{  w0, -h0 },
a20939
			{ -w0,  h0 },
a20939
			{  w0,  h0 } };
a20939
		
a20939
		const double *colors[] = {
a20939
			sideColor, color,
a20939
			sideColor, color,
a20939
			sideColor, color,
a20939
			sideColor, color,
a20939
			sideColor, color,
a20939
			color,
a20939
			color,
a20939
			color,
a20939
			color };
a20939
		
a20939
		if (texid) {
a20939
			const double tw0 = fabs(w0/w)*0.5 - 0.5;
a20939
			const double tw1 = fabs(w1/w)*0.5 - 0.5;
a20939
			const double th0 = fabs(h0/h)*0.5 - 0.5;
a20939
			const double th1 = fabs(h1/h)*0.5 - 0.5;
a20939
			const double texCoords[][2] = {
a20939
				{    -tw1,    -th1 }, {    -tw0,    -th0 },
a20939
				{ 1 + tw1,    -th1 }, { 1 + tw0,    -th0 },
a20939
				{ 1 + tw1, 1 + th1 }, { 1 + tw0, 1 + th0 },
a20939
				{    -tw1, 1 + th1 }, {    -tw0, 1 + th0 },
a20939
				{    -tw1,    -th1 }, {    -tw0,    -th0 },
a20939
				{    -tw0,    -th0 },
a20939
				{ 1 + tw0,    -th0 },
a20939
				{    -tw0, 1 + th0 },
a20939
				{ 1 + tw0, 1 + th0 } };
a20939
			
a20939
			glEnable(GL_TEXTURE_2D);
a20939
			glBindTexture(GL_TEXTURE_2D, texid);
a20939
			
a20939
			glBegin(GL_TRIANGLE_STRIP);
a20939
			for(int i = 0; i < vcnt; ++i) {
a20939
				glColor4dv(colors[i]);
a20939
				glTexCoord2dv(texCoords[i]);
a20939
				glVertex2dv(vertices[i]);
a20939
			}
a20939
			glEnd();
a20939
			
a20939
			glBindTexture(GL_TEXTURE_2D, 0);
a20939
			glDisable(GL_TEXTURE_2D);
a20939
		} else {
a20939
			glBegin(GL_TRIANGLE_STRIP);
a20939
			for(int i = 0; i < vcnt; ++i) {
a20939
				glColor4dv(colors[i]);
a20939
				glVertex2dv(vertices[i]);
a20939
			}
a20939
			glEnd();
8535a3
		}
8535a3
	}
ba9f06
	
ba9f06
	glColor4d(1, 1, 1, 1);
a20939
	glPopMatrix();
8535a3
}
8535a3
8535a3
void drawSprites() {
3f9996
	// sort
3f9996
	int found = TRUE;
3f9996
	while(found) {
3f9996
		found = FALSE;
8535a3
		// forward
07b70f
		for(int i = 1; i < spritesSorted.count; ++i) {
07b70f
			if ( ((Sprite)spritesSorted.items[i-1].value)->depth
07b70f
			   < ((Sprite)spritesSorted.items[i  ].value)->depth )
07b70f
			{
07b70f
				void *x = spritesSorted.items[i].value;
07b70f
				spritesSorted.items[i].value = spritesSorted.items[i-1].value;
07b70f
				spritesSorted.items[i-1].value = x;
3f9996
				found = TRUE;
3f9996
			}
3f9996
		}
8535a3
		if (!found) break;
8535a3
		// backward
8535a3
		found = FALSE;
07b70f
		for(int i = spritesSorted.count - 1; i > 0; --i) {
07b70f
			if ( ((Sprite)spritesSorted.items[i-1].value)->depth
07b70f
			   < ((Sprite)spritesSorted.items[i  ].value)->depth )
07b70f
			{
07b70f
				void *x = spritesSorted.items[i].value;
07b70f
				spritesSorted.items[i].value = spritesSorted.items[i-1].value;
07b70f
				spritesSorted.items[i-1].value = x;
07b70f
				found = TRUE;
8535a3
			}
8535a3
		}
3f9996
	}
3f9996
	
a20939
	// draw
a20939
	for(int i = 0; i < spritesSorted.count; ++i) {
a20939
		Sprite s = (Sprite)(spritesSorted.items[i].value);
a20939
		if (s->visible) drawSprite(s);
a20939
	}
a20939
	
a20939
	// draw debug marks
a20939
	for(int i = 0; i < spritesSorted.count; ++i) {
a20939
		Sprite s = (Sprite)(spritesSorted.items[i].value);
a20939
		if (s->debug) drawSpriteDebug(s);
3f9996
	}
3f9996
}
3f9996
3f9996
int mouseIsOver(Sprite sprite) {
3f9996
	HeliCollider collider;
3f9996
	prepareCollider(sprite, &collider);
07b70f
	return heliPointCollision(&collider, mouseX(), mouseY());
3f9996
}
3f9996
07b70f
void heliSpriteUpdate(double dt) {
8535a3
	// auto-remove
8535a3
	for(int i = sprites.count - 1; i > 0; --i) {
07b70f
		Sprite s = (Sprite)sprites.items[i].value;
1c7488
		if (s->lifeTime >= -HELI_PRECISION) {
8535a3
			s->lifeTime -= dt;
1c7488
			if (s->lifeTime <= HELI_PRECISION)
8535a3
				spriteDestroy(s);
8535a3
		}
8535a3
	}
8535a3
	
8535a3
	// update
8535a3
	for(int i = 0; i < sprites.count; ++i) {
8535a3
		Sprite s = (Sprite)sprites.items[i].value;
3f9996
		s->x += s->vx*dt;
3f9996
		s->y += s->vy*dt;
8535a3
		if (!s->rotateToDirection)
8535a3
			s->rotation += s->rotationSpeed*dt;
8535a3
		if (s->playing) spriteNextFrame(s);
3f9996
	}
3f9996
}
8535a3
8535a3
HeliArray* heliSpriteGetGroups(Sprite sprite)
8535a3
	{ return &sprite->groups; }
07b70f
07b70f
void heliSpriteFinish() {
07b70f
	while(worldGetSpriteCount())
07b70f
		{ spriteDestroy(worldGetSprite(0)); }
07b70f
	heliArrayDestroy(&sprites);
07b70f
	heliArrayDestroy(&spritesSorted);
07b70f
}
07b70f
07b70f