|
|
3f9996 |
|
|
|
3f9996 |
#include "private.h"
|
|
|
3f9996 |
|
|
|
3f9996 |
#include "sprite.h"
|
|
|
3f9996 |
#include "world.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 );
|
|
|
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 |
|
|
|
f80a0a |
static void autoRotate(Sprite sprite) {
|
|
|
f80a0a |
if (sprite->rotateToDirection)
|
|
|
f80a0a |
sprite->rotation = spriteGetDirection(sprite);
|
|
|
f80a0a |
}
|
|
|
f80a0a |
|
|
|
f80a0a |
|
|
|
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;
|
|
|
f80a0a |
s->colliderSensitiveDistance = 0.001;
|
|
|
44355f |
s->collider.bounciness = 1;
|
|
|
f80a0a |
s->friction = 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 |
|
|
|
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 |
|
|
|
8535a3 |
Sprite createSprite(double x, double y)
|
|
|
8535a3 |
{ return createSpriteEx(x, y, 100, 100); }
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
d4e89f |
void spriteSetShapeColor(Sprite sprite, unsigned int colorCode)
|
|
|
d4e89f |
{ heliColorToDouble(colorCode, sprite->shapeColor); }
|
|
|
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 |
|
|
|
3f9996 |
int worldGetSpriteCount()
|
|
|
3f9996 |
{ return sprites.count; }
|
|
|
07b70f |
Sprite worldGetSprite(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 |
|
|
|
3954ba |
// depth
|
|
|
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;
|
|
|
deef1d |
double ss = s1 < s2 ? s1 : s2;
|
|
|
deef1d |
textSize(ss);
|
|
|
deef1d |
textAlign(HALIGN_LEFT, VALIGN_BOTTOM);
|
|
|
deef1d |
text(buf, 0.1*ss - hw, hh - 0.1*ss);
|
|
|
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 |
|
|
|
6eadb0 |
void spriteDraw(Sprite sprite) {
|
|
|
6eadb0 |
if (sprite->visible) heliSpriteDraw(sprite);
|
|
|
6eadb0 |
if (sprite->debug) heliSpriteDrawDebug(sprite);
|
|
|
6eadb0 |
}
|
|
|
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 |
|
|
|
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;
|
|
|
6eadb0 |
if (s->frozen) continue;
|
|
|
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;
|
|
|
6eadb0 |
if (s->frozen) continue;
|
|
|
f80a0a |
double vx = s->vx + s->ax*dt;
|
|
|
f80a0a |
double vy = s->vy + s->ay*dt;
|
|
|
f80a0a |
|
|
|
f80a0a |
double weight = spriteGetTouchWeight(s);
|
|
|
f80a0a |
double dvf = fabs(weight*s->friction*s->touchFriction)*dt;
|
|
|
f80a0a |
if (dvf > HELI_PRECISION) {
|
|
|
f80a0a |
double nx = s->wx/weight;
|
|
|
f80a0a |
double ny = s->wy/weight;
|
|
|
f80a0a |
double vb = nx*vx + ny*vy;
|
|
|
f80a0a |
double vs = ny*vx - nx*vy;
|
|
|
f80a0a |
if (vs > dvf + HELI_PRECISION) vs -= dvf; else
|
|
|
f80a0a |
if (vs < -dvf - HELI_PRECISION) vs += dvf; else vs = 0;
|
|
|
f80a0a |
vx = nx*vb + ny*vs;
|
|
|
f80a0a |
vy = ny*vb - nx*vs;
|
|
|
f80a0a |
}
|
|
|
f80a0a |
|
|
|
f80a0a |
double dvaf = fabs(s->airFriction)*dt;
|
|
|
f80a0a |
if (dvaf > HELI_PRECISION) {
|
|
|
f80a0a |
double v = sqrt(vx*vx + vy*vy), vn = v;
|
|
|
f80a0a |
if (v > dvaf + HELI_PRECISION) vn -= dvaf; else
|
|
|
f80a0a |
if (v < -dvaf - HELI_PRECISION) vn += dvaf; else vn = 0;
|
|
|
f80a0a |
double k = vn > HELI_PRECISION ? vn/v : 0;
|
|
|
f80a0a |
vx *= k;
|
|
|
f80a0a |
vy *= k;
|
|
|
f80a0a |
}
|
|
|
f80a0a |
|
|
|
f80a0a |
s->vx = vx;
|
|
|
f80a0a |
s->vy = vy;
|
|
|
f80a0a |
autoRotate(s);
|
|
|
f80a0a |
spriteResetTouch(s);
|
|
|
f80a0a |
|
|
|
3f9996 |
s->x += s->vx*dt;
|
|
|
3f9996 |
s->y += s->vy*dt;
|
|
|
8535a3 |
if (!s->rotateToDirection)
|
|
|
8535a3 |
s->rotation += s->rotationSpeed*dt;
|
|
|
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 |
|