2018-01-25 08:41:10 +01:00
|
|
|
/*
|
|
|
|
Copyright (C) 2018 Parallel Realities
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2018-01-31 22:50:49 +01:00
|
|
|
#include "entities.h"
|
|
|
|
|
2018-02-03 09:44:02 +01:00
|
|
|
static void doMarker(Marker *m, int delta);
|
|
|
|
static void handleTeleport(void);
|
|
|
|
static int isObserving(void);
|
|
|
|
static void addRider(void);
|
|
|
|
static void checkPlatformContact(void);
|
|
|
|
static void moveEntity(void);
|
|
|
|
static void haltAtEdge(void);
|
|
|
|
static int canWalkOnEntity(float x, float y);
|
|
|
|
static void moveToOthers(float dx, float dy, PointF *position);
|
|
|
|
static void addTouched(Entity *e);
|
2018-02-03 11:01:14 +01:00
|
|
|
static int pushEntity(Entity *e, float dx, float dy);
|
|
|
|
static void moveToMap(float dx, float dy, PointF *position);
|
|
|
|
static int hasHitWorld(int mx, int my);
|
|
|
|
static void compareEnvironments(void);
|
2018-02-03 12:47:50 +01:00
|
|
|
static int getMarkerType(void);
|
2018-02-05 23:06:00 +01:00
|
|
|
static int drawComparator(const void *a, const void *b);
|
2018-03-07 22:38:18 +01:00
|
|
|
static void checkStuckInWall(void);
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
static Entity *riders[MAX_RIDERS];
|
|
|
|
static Entity *touched[MAX_TOUCHED];
|
2018-01-31 22:50:49 +01:00
|
|
|
static Texture *atlasTexture;
|
2018-02-03 09:44:02 +01:00
|
|
|
static Marker targetMarker[3];
|
2018-01-31 22:50:49 +01:00
|
|
|
|
|
|
|
void initEntities(void)
|
|
|
|
{
|
2018-02-03 09:44:02 +01:00
|
|
|
int i;
|
2018-02-11 13:37:37 +01:00
|
|
|
SDL_Rect *r;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
2018-01-31 22:50:49 +01:00
|
|
|
atlasTexture = getTexture("gfx/atlas/atlas.png");
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
for (i = 0 ; i < 3 ; i++)
|
|
|
|
{
|
|
|
|
memset(&targetMarker[i], 0, sizeof(Marker));
|
|
|
|
targetMarker[i].sprite = getSprite("Marker");
|
|
|
|
}
|
2018-02-11 13:37:37 +01:00
|
|
|
|
|
|
|
for (self = world.entityHead.next ; self != NULL ; self = self->next)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Most things retain their dimensions, so this isn't a big deal. If we set
|
|
|
|
* this each frame, it will muck up the bouncing, especially in the case of grenades.
|
|
|
|
*/
|
|
|
|
if (self->w == 0 || self->h == 0)
|
|
|
|
{
|
|
|
|
r = &self->sprite[self->facing]->frames[self->spriteFrame]->rect;
|
|
|
|
|
|
|
|
self->w = r->w;
|
|
|
|
self->h = r->h;
|
|
|
|
}
|
|
|
|
|
|
|
|
addToQuadtree(self, &world.quadtree);
|
2018-03-07 22:38:18 +01:00
|
|
|
|
|
|
|
checkStuckInWall();
|
2018-02-11 13:37:37 +01:00
|
|
|
}
|
2018-01-31 22:50:49 +01:00
|
|
|
}
|
|
|
|
|
2018-02-01 22:51:43 +01:00
|
|
|
void doEntities(void)
|
|
|
|
{
|
2018-02-09 08:42:36 +01:00
|
|
|
Entity *prev, *oldSelf, *e;
|
2018-02-03 11:01:14 +01:00
|
|
|
int camMidX, camMidY, flicker, i;
|
2018-02-03 19:31:55 +01:00
|
|
|
SDL_Rect *r;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
memset(riders, 0, sizeof(Entity*) * MAX_RIDERS);
|
|
|
|
|
|
|
|
camMidX = camera.x + (SCREEN_WIDTH / 2);
|
|
|
|
camMidY = camera.y + (SCREEN_HEIGHT / 2);
|
|
|
|
|
|
|
|
doMarker(&targetMarker[0], 1);
|
|
|
|
doMarker(&targetMarker[1], -1);
|
|
|
|
doMarker(&targetMarker[2], 1);
|
|
|
|
|
|
|
|
flicker = world.frameCounter % 3 > 0;
|
|
|
|
|
2018-02-03 11:01:14 +01:00
|
|
|
prev = &world.entityHead;
|
|
|
|
|
2018-02-01 22:51:43 +01:00
|
|
|
for (self = world.entityHead.next ; self != NULL ; self = self->next)
|
|
|
|
{
|
2018-02-04 13:01:55 +01:00
|
|
|
if (self->w == 0 || self->h == 0)
|
|
|
|
{
|
2018-02-25 18:31:32 +01:00
|
|
|
r = &self->sprite[0]->frames[0]->rect;
|
2018-02-05 23:06:00 +01:00
|
|
|
|
2018-02-04 13:01:55 +01:00
|
|
|
self->w = r->w;
|
|
|
|
self->h = r->h;
|
|
|
|
}
|
2018-02-03 19:31:55 +01:00
|
|
|
|
2018-02-03 09:44:02 +01:00
|
|
|
removeFromQuadtree(self, &world.quadtree);
|
|
|
|
|
2018-02-11 08:59:11 +01:00
|
|
|
if (self->alive == ALIVE_DEAD)
|
|
|
|
{
|
|
|
|
prev->next = self->next;
|
|
|
|
|
|
|
|
if (self == world.entityTail)
|
|
|
|
{
|
|
|
|
world.entityTail = prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(self);
|
|
|
|
|
2018-02-22 20:03:48 +01:00
|
|
|
/* assign prev entity to self */
|
2018-02-11 08:59:11 +01:00
|
|
|
self = prev;
|
|
|
|
|
2018-02-22 20:03:48 +01:00
|
|
|
/* assign prev as self, so that prev doesn't point at the now freed memory */
|
|
|
|
prev = self;
|
|
|
|
|
2018-02-11 08:59:11 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-02-06 08:32:08 +01:00
|
|
|
self->isVisible = 0;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
if (self->flags & EF_TELEPORTING)
|
|
|
|
{
|
2018-03-08 08:52:42 +01:00
|
|
|
world.saveDelay = FPS;
|
2018-02-03 09:44:02 +01:00
|
|
|
handleTeleport();
|
2018-02-03 11:01:14 +01:00
|
|
|
prev = self;
|
2018-02-03 09:44:02 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((self->flags & EF_ALWAYS_PROCESS) > 0 || getDistance(camMidX, camMidY, self->x, self->y) < SCREEN_WIDTH || isObserving())
|
|
|
|
{
|
2018-02-06 08:32:08 +01:00
|
|
|
self->isVisible = 1;
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
|
|
|
else if (self->flags & EF_KILL_OFFSCREEN)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
2018-02-03 09:44:02 +01:00
|
|
|
self->alive = ALIVE_DEAD;
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
self->riding = NULL;
|
2018-02-02 09:00:27 +01:00
|
|
|
|
2018-02-06 08:32:08 +01:00
|
|
|
if (self->isVisible)
|
2018-02-03 09:44:02 +01:00
|
|
|
{
|
|
|
|
memset(touched, 0, sizeof(Entity*) * MAX_TOUCHED);
|
|
|
|
|
|
|
|
if (--self->thinkTime <= 0)
|
|
|
|
{
|
|
|
|
self->thinkTime = 0;
|
|
|
|
|
|
|
|
self->action();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->flags & EF_GONE)
|
|
|
|
{
|
2018-02-06 08:32:08 +01:00
|
|
|
self->isVisible = 0;
|
2018-02-09 08:21:37 +01:00
|
|
|
prev = self;
|
2018-02-03 09:44:02 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->tick();
|
|
|
|
|
|
|
|
self->isOnGround = 0;
|
|
|
|
|
|
|
|
if (self->dy >= 0 && (!(self->flags & EF_WEIGHTLESS)))
|
|
|
|
{
|
|
|
|
checkPlatformContact();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!self->isStatic)
|
|
|
|
{
|
|
|
|
moveEntity();
|
|
|
|
}
|
|
|
|
|
|
|
|
self->animate();
|
2018-02-03 11:01:14 +01:00
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_TOUCHED ; i++)
|
|
|
|
{
|
|
|
|
if (touched[i])
|
|
|
|
{
|
|
|
|
self->touch(touched[i]);
|
2018-02-03 12:32:03 +01:00
|
|
|
|
2018-02-22 20:03:48 +01:00
|
|
|
/* for objects that never move */
|
2018-02-03 12:32:03 +01:00
|
|
|
if (touched[i]->isStatic)
|
|
|
|
{
|
2018-02-03 14:07:56 +01:00
|
|
|
oldSelf = self;
|
|
|
|
|
|
|
|
self = touched[i];
|
|
|
|
|
2018-02-22 20:03:48 +01:00
|
|
|
touched[i]->touch(oldSelf);
|
2018-02-03 14:07:56 +01:00
|
|
|
|
|
|
|
self = oldSelf;
|
2018-02-03 12:32:03 +01:00
|
|
|
}
|
2018-02-03 11:01:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(self->flags & EF_NO_ENVIRONMENT))
|
|
|
|
{
|
|
|
|
compareEnvironments();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always sink if not in the air
|
|
|
|
*/
|
|
|
|
if (self->environment != ENV_AIR && (!(self->flags & EF_SWIMS)))
|
|
|
|
{
|
|
|
|
self->dy = 0.5f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-03 12:32:03 +01:00
|
|
|
if ((self->flags & EF_FLICKER) && flicker)
|
2018-02-03 11:01:14 +01:00
|
|
|
{
|
2018-02-06 08:32:08 +01:00
|
|
|
self->isVisible = 0;
|
2018-02-03 11:01:14 +01:00
|
|
|
}
|
|
|
|
|
2018-02-11 08:59:11 +01:00
|
|
|
if (self->alive == ALIVE_ALIVE)
|
2018-02-03 11:01:14 +01:00
|
|
|
{
|
2018-02-04 12:00:42 +01:00
|
|
|
if (self->health <= 0)
|
|
|
|
{
|
|
|
|
self->alive = ALIVE_DYING;
|
2018-02-09 23:34:01 +01:00
|
|
|
self->spriteFrame = 0;
|
2018-02-04 12:00:42 +01:00
|
|
|
self->die();
|
|
|
|
}
|
2018-02-03 11:01:14 +01:00
|
|
|
}
|
2018-03-08 08:52:42 +01:00
|
|
|
|
|
|
|
if (self->alive == ALIVE_DYING)
|
|
|
|
{
|
|
|
|
world.saveDelay = FPS;
|
|
|
|
}
|
2018-02-16 09:41:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(self->flags & (EF_TELEPORTING | EF_GONE)))
|
|
|
|
{
|
|
|
|
addToQuadtree(self, &world.quadtree);
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
2018-02-03 11:01:14 +01:00
|
|
|
|
|
|
|
prev = self;
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
2018-02-09 08:42:36 +01:00
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_RIDERS ; i++)
|
|
|
|
{
|
|
|
|
e = riders[i];
|
|
|
|
|
|
|
|
if (e != NULL)
|
2018-02-25 18:31:32 +01:00
|
|
|
{
|
|
|
|
removeFromQuadtree(e, &world.quadtree);
|
|
|
|
|
2018-02-09 08:42:36 +01:00
|
|
|
if (e->dy > 0)
|
|
|
|
{
|
|
|
|
pushEntity(e, e->riding->dx, 0);
|
|
|
|
|
|
|
|
if (!pushEntity(e, 0, e->riding->dy))
|
|
|
|
{
|
|
|
|
e->riding->y -= e->riding->dy;
|
2018-03-04 14:36:00 +01:00
|
|
|
|
|
|
|
if (e->flags & EF_CRUSHABLE)
|
|
|
|
{
|
2018-03-04 18:04:13 +01:00
|
|
|
e->health *= 0.5;
|
2018-03-04 14:36:00 +01:00
|
|
|
}
|
2018-02-09 08:42:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
e->y = e->riding->y - e->h;
|
|
|
|
}
|
2018-02-25 18:31:32 +01:00
|
|
|
|
|
|
|
addToQuadtree(e, &world.quadtree);
|
2018-02-09 08:42:36 +01:00
|
|
|
}
|
|
|
|
}
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
|
2018-02-18 18:01:38 +01:00
|
|
|
void doEntitiesStatic(void)
|
|
|
|
{
|
|
|
|
int camMidX, camMidY;
|
|
|
|
|
|
|
|
camMidX = camera.x + (SCREEN_WIDTH / 2);
|
|
|
|
camMidY = camera.y + (SCREEN_HEIGHT / 2);
|
|
|
|
|
|
|
|
for (self = world.entityHead.next ; self != NULL ; self = self->next)
|
|
|
|
{
|
|
|
|
if (getDistance(camMidX, camMidY, self->x, self->y) < SCREEN_WIDTH || isObserving())
|
|
|
|
{
|
|
|
|
self->isVisible = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-02 09:00:27 +01:00
|
|
|
void drawEntities(int plane)
|
2018-01-31 22:50:49 +01:00
|
|
|
{
|
2018-02-05 23:06:00 +01:00
|
|
|
int x, y, draw, i, t;
|
|
|
|
Entity **candidates;
|
|
|
|
|
|
|
|
candidates = getAllEntsWithin(camera.x, camera.y, SCREEN_WIDTH, SCREEN_HEIGHT, NULL);
|
|
|
|
|
|
|
|
/* counting entities to draw */
|
|
|
|
for (i = 0, self = candidates[i] ; self != NULL ; self = candidates[++i]) {};
|
|
|
|
|
|
|
|
qsort(candidates, i, sizeof(Entity*), drawComparator);
|
2018-01-31 22:50:49 +01:00
|
|
|
|
2018-02-05 23:06:00 +01:00
|
|
|
for (i = 0, self = candidates[i] ; self != NULL ; self = candidates[++i])
|
2018-01-31 22:50:49 +01:00
|
|
|
{
|
2018-02-09 08:42:36 +01:00
|
|
|
draw = self->isVisible && self->plane == plane;
|
2018-02-03 00:01:51 +01:00
|
|
|
|
|
|
|
if (draw)
|
2018-02-02 09:00:27 +01:00
|
|
|
{
|
|
|
|
x = (-camera.x + self->x);
|
|
|
|
y = (-camera.y + self->y);
|
|
|
|
|
|
|
|
blitRect(atlasTexture->texture, x, y, self->getCurrentSprite(), 0);
|
2018-02-03 12:47:50 +01:00
|
|
|
|
|
|
|
x += (self->w / 2) - 9;
|
|
|
|
|
|
|
|
if (self->type == ET_ENEMY && ((Unit*)self)->carriedItem != NULL)
|
|
|
|
{
|
2018-02-16 09:41:02 +01:00
|
|
|
blitRect(atlasTexture->texture, x, y - (targetMarker[0].y + 5), &targetMarker[0].sprite->frames[0]->rect, 0);
|
2018-02-03 12:47:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (self->isMissionTarget)
|
|
|
|
{
|
2018-02-05 23:06:00 +01:00
|
|
|
t = getMarkerType();
|
2018-02-03 12:47:50 +01:00
|
|
|
|
2018-02-16 09:41:02 +01:00
|
|
|
blitRect(atlasTexture->texture, x, y - (targetMarker[t].y + 5), &targetMarker[t].sprite->frames[t]->rect, 0);
|
2018-02-03 12:47:50 +01:00
|
|
|
}
|
2018-02-02 09:00:27 +01:00
|
|
|
}
|
2018-01-31 22:50:49 +01:00
|
|
|
}
|
|
|
|
}
|
2018-02-01 22:51:43 +01:00
|
|
|
|
2018-02-03 12:47:50 +01:00
|
|
|
static int getMarkerType(void)
|
|
|
|
{
|
|
|
|
switch (self->type)
|
|
|
|
{
|
|
|
|
case ET_ENEMY:
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ET_MIA:
|
|
|
|
return 2;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-03 09:44:02 +01:00
|
|
|
static void checkPlatformContact(void)
|
|
|
|
{
|
|
|
|
Entity *e;
|
2018-02-09 08:42:36 +01:00
|
|
|
Entity **candidates;
|
2018-02-09 08:21:37 +01:00
|
|
|
int i;
|
2018-02-09 09:21:08 +01:00
|
|
|
SDL_Rect srcRect;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
2018-02-12 19:27:12 +01:00
|
|
|
srcRect.x = self->x;
|
|
|
|
srcRect.y = self->y;
|
|
|
|
srcRect.w = self->w;
|
|
|
|
srcRect.h = self->h + 8;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
2018-02-09 08:21:37 +01:00
|
|
|
candidates = getAllEntsWithin(srcRect.x, srcRect.y, srcRect.w, srcRect.h, NULL);
|
|
|
|
|
|
|
|
for (i = 0, e = candidates[i] ; e != NULL ; e = candidates[++i])
|
2018-02-03 09:44:02 +01:00
|
|
|
{
|
|
|
|
if (e == self || e->type != ET_LIFT)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-02-09 09:21:08 +01:00
|
|
|
if (e->y > self->y + self->h - (8 + self->dy) && collision(self->x, self->y + 4, self->w, self->h, e->x, e->y, e->w, e->h))
|
2018-02-03 09:44:02 +01:00
|
|
|
{
|
|
|
|
self->riding = e;
|
|
|
|
self->isOnGround = 1;
|
|
|
|
self->dy = 0;
|
|
|
|
addRider();
|
|
|
|
|
|
|
|
/* required for bullets */
|
|
|
|
self->touch(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void moveEntity(void)
|
|
|
|
{
|
|
|
|
PointF position;
|
|
|
|
|
|
|
|
switch (self->environment)
|
|
|
|
{
|
|
|
|
case ENV_AIR:
|
|
|
|
if (!(self->flags & EF_WEIGHTLESS))
|
|
|
|
{
|
|
|
|
self->dy += GRAVITY_POWER;
|
2018-02-03 12:32:03 +01:00
|
|
|
self->dy = limit(self->dy, -25, 25);
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
if (self->dy > 0 && self->dy < 1)
|
|
|
|
{
|
|
|
|
self->dy = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENV_WATER:
|
|
|
|
self->flags &= ~EF_BOUNCES;
|
2018-02-03 12:32:03 +01:00
|
|
|
if (!(self->flags & EF_SWIMS))
|
2018-02-03 09:44:02 +01:00
|
|
|
{
|
|
|
|
self->dy += GRAVITY_POWER;
|
|
|
|
self->dy = limit(self->dy, -2, 2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENV_SLIME:
|
|
|
|
case ENV_LAVA:
|
|
|
|
self->dy += GRAVITY_POWER;
|
|
|
|
self->dx = limit(self->dx, -2, 2);
|
|
|
|
self->dy = limit(self->dy, -2, 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->flags & EF_HALT_AT_EDGE)
|
|
|
|
{
|
|
|
|
haltAtEdge();
|
|
|
|
}
|
|
|
|
|
2018-02-14 23:31:21 +01:00
|
|
|
/* Deal with x movement */
|
2018-02-03 09:44:02 +01:00
|
|
|
position.x = self->x;
|
2018-02-03 12:32:03 +01:00
|
|
|
position.y = self->y;
|
2018-02-03 09:44:02 +01:00
|
|
|
position.x += self->dx;
|
|
|
|
moveToOthers(self->dx, 0, &position);
|
2018-02-03 11:01:14 +01:00
|
|
|
moveToMap(self->dx, 0, &position);
|
2018-02-03 09:44:02 +01:00
|
|
|
|
2018-02-14 23:31:21 +01:00
|
|
|
/* Deal with Y movement */
|
2018-02-03 09:44:02 +01:00
|
|
|
position.y += self->dy;
|
|
|
|
moveToOthers(0, self->dy, &position);
|
2018-02-03 11:01:14 +01:00
|
|
|
moveToMap(0, self->dy, &position);
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
if (self->dy > 0 && self->riding != NULL)
|
|
|
|
{
|
|
|
|
position.y = self->riding->y;
|
|
|
|
position.y -= self->h;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->x = position.x;
|
|
|
|
self->y = position.y;
|
|
|
|
|
|
|
|
if (!(self->flags & (EF_KILL_OFFSCREEN | EF_NO_CLIP)))
|
|
|
|
{
|
|
|
|
self->x = limit(self->x, world.map.bounds.x, world.map.bounds.w + SCREEN_WIDTH - self->w);
|
2018-02-25 09:36:40 +01:00
|
|
|
self->y = limit(self->y, world.map.bounds.y - (self->h - 1), world.map.bounds.h + SCREEN_HEIGHT - self->h);
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 22:38:18 +01:00
|
|
|
static void checkStuckInWall(void)
|
|
|
|
{
|
|
|
|
int mx, my;
|
|
|
|
|
2018-03-16 23:27:47 +01:00
|
|
|
mx = self->x / MAP_TILE_SIZE;
|
|
|
|
my = self->y / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
if (!isWithinMap(mx, my))
|
|
|
|
{
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "%s (%d) outside world at %d,%d", self->name, self->type, mx, my);
|
|
|
|
}
|
|
|
|
|
2018-03-07 22:38:18 +01:00
|
|
|
switch (self->type)
|
|
|
|
{
|
|
|
|
case ET_PRESSURE_PLATE:
|
|
|
|
case ET_TELEPORTER:
|
|
|
|
case ET_DOOR:
|
2018-03-09 08:31:50 +01:00
|
|
|
case ET_ITEM_PAD:
|
2018-03-07 22:38:18 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (hasHitWorld(mx, my))
|
|
|
|
{
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "%s (%d): in wall at %d,%d", self->name, self->type, mx, my);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-03 09:44:02 +01:00
|
|
|
static void haltAtEdge(void)
|
|
|
|
{
|
|
|
|
float x, y;
|
|
|
|
int mx, my, i;
|
|
|
|
|
|
|
|
if (!(self->flags & (EF_WEIGHTLESS | EF_SWIMS)))
|
|
|
|
{
|
|
|
|
if (self->environment == ENV_WATER)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
x = self->x + self->dx + (self->w / 2);
|
|
|
|
y = self->y + self->h + 1;
|
|
|
|
|
|
|
|
if (canWalkOnEntity(x, y))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mx = x / MAP_TILE_SIZE;
|
|
|
|
my = y / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
if (isLiquid(mx, my + i))
|
|
|
|
{
|
|
|
|
self->walk();
|
|
|
|
self->dx = 0;
|
|
|
|
self->thinkTime = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isWalkable(mx, my + i))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self->walk();
|
|
|
|
self->dx = 0;
|
|
|
|
self->thinkTime = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if ((self->flags & EF_SWIMS) && self->dy < 0)
|
|
|
|
{
|
|
|
|
x = self->x + self->dx + (self->w / 2);
|
|
|
|
y = self->y + self->dy + (self->h / 2);
|
|
|
|
|
|
|
|
mx = x / MAP_TILE_SIZE;
|
|
|
|
my = y / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
my--;
|
|
|
|
|
|
|
|
if (world.map.data[mx][my] == MAP_TILE_AIR)
|
|
|
|
{
|
|
|
|
self->walk();
|
|
|
|
self->dx = 0;
|
|
|
|
self->dy = 0;
|
|
|
|
self->thinkTime = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((self->flags & EF_WEIGHTLESS) && self->dy > 0)
|
|
|
|
{
|
|
|
|
x = self->x + self->dx + self->w;
|
|
|
|
y = self->y + self->dy + self->h;
|
|
|
|
|
|
|
|
mx = x / MAP_TILE_SIZE;
|
|
|
|
my = y / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
my++;
|
|
|
|
|
|
|
|
if (isLiquid(mx, my))
|
|
|
|
{
|
|
|
|
self->walk();
|
|
|
|
self->dx = 0;
|
|
|
|
self->dy = 0;
|
|
|
|
self->thinkTime = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int canWalkOnEntity(float x, float y)
|
|
|
|
{
|
2018-02-09 08:21:37 +01:00
|
|
|
int i;
|
2018-02-03 09:44:02 +01:00
|
|
|
Entity *e;
|
2018-02-09 08:42:36 +01:00
|
|
|
Entity **candidates;
|
2018-02-09 09:21:08 +01:00
|
|
|
SDL_Rect srcRect;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
srcRect.x = x;
|
2018-02-22 20:03:48 +01:00
|
|
|
srcRect.y = y;
|
2018-02-03 09:44:02 +01:00
|
|
|
srcRect.w = 8;
|
|
|
|
srcRect.h = MAP_TILE_SIZE * 4;
|
|
|
|
|
2018-02-09 08:21:37 +01:00
|
|
|
candidates = getAllEntsWithin(srcRect.x, srcRect.y, srcRect.w, srcRect.h, NULL);
|
|
|
|
|
|
|
|
for (i = 0, e = candidates[i] ; e != NULL ; e = candidates[++i])
|
2018-02-03 09:44:02 +01:00
|
|
|
{
|
|
|
|
if (self != e && e->isSolid && collision(x, y, 1, MAP_TILE_SIZE * 2, e->x, e->y, e->w, e->y))
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void moveToOthers(float dx, float dy, PointF *position)
|
|
|
|
{
|
2018-02-26 08:38:20 +01:00
|
|
|
Entity *e, *oldSelf;
|
2018-02-09 08:21:37 +01:00
|
|
|
Entity **candidates;
|
|
|
|
int clearTouched, hit, dirX, dirY, solidLoopHits, i;
|
2018-02-26 08:38:20 +01:00
|
|
|
SDL_Rect srcRect, destRect;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
2018-02-26 08:38:20 +01:00
|
|
|
self->getCollisionBounds(&srcRect);
|
2018-02-03 09:44:02 +01:00
|
|
|
srcRect.x = (int) position->x;
|
|
|
|
srcRect.y = (int) position->y;
|
|
|
|
|
|
|
|
clearTouched = 0;
|
|
|
|
hit = 0;
|
|
|
|
dirX = (dx > 0) ? 1 : -1;
|
|
|
|
dirY = (dy > 0) ? 1 : -1;
|
|
|
|
solidLoopHits = 0;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
hit = 0;
|
|
|
|
|
2018-02-09 08:21:37 +01:00
|
|
|
candidates = getAllEntsWithin(srcRect.x, srcRect.y, srcRect.w, srcRect.h, NULL);
|
|
|
|
|
|
|
|
for (i = 0, e = candidates[i] ; e != NULL ; e = candidates[++i])
|
2018-02-03 09:44:02 +01:00
|
|
|
{
|
|
|
|
if (e == self || e->owner == self || self->owner == e)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2018-02-26 08:38:20 +01:00
|
|
|
|
|
|
|
oldSelf = self;
|
|
|
|
self = e;
|
|
|
|
e->getCollisionBounds(&destRect);
|
|
|
|
self = oldSelf;
|
2018-02-03 09:44:02 +01:00
|
|
|
|
2018-02-26 08:38:20 +01:00
|
|
|
if (collision(srcRect.x, srcRect.y, srcRect.w, srcRect.h, destRect.x, destRect.y, destRect.w, destRect.h))
|
2018-02-03 09:44:02 +01:00
|
|
|
{
|
|
|
|
if (clearTouched)
|
|
|
|
{
|
|
|
|
memset(touched, 0, sizeof(Entity*) * MAX_TOUCHED);
|
|
|
|
clearTouched = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->type == ET_BOB && e->type == ET_PUSHBLOCK && dx != 0)
|
|
|
|
{
|
2018-02-25 18:31:32 +01:00
|
|
|
removeFromQuadtree(e, &world.quadtree);
|
|
|
|
|
2018-02-03 09:44:02 +01:00
|
|
|
if (!pushEntity(e, dx * 0.35, 0))
|
|
|
|
{
|
|
|
|
position->x = e->x;
|
|
|
|
position->x -= (dirX == 1) ? self->w : -e->w;
|
|
|
|
self->dx = self->bounce(self->dx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self->animate();
|
|
|
|
}
|
2018-02-25 18:31:32 +01:00
|
|
|
|
|
|
|
addToQuadtree(e, &world.quadtree);
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (e->isSolid && self->type != ET_LIFT)
|
|
|
|
{
|
|
|
|
if (dx != 0)
|
|
|
|
{
|
|
|
|
position->x = e->x;
|
|
|
|
position->x -= (dirX == 1) ? self->w : -e->w;
|
|
|
|
self->dx = self->bounce(self->dx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dy != 0)
|
|
|
|
{
|
|
|
|
if (e->y > self->y)
|
|
|
|
{
|
|
|
|
self->isOnGround = 1;
|
|
|
|
self->riding = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
position->y = e->y;
|
|
|
|
position->y -= (dirY == 1) ? self->h : -e->h;
|
|
|
|
self->dy = self->bounce(self->dy);
|
|
|
|
self->dy = limit(self->dy, JUMP_POWER, -JUMP_POWER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->isSolid && e->isSolid)
|
|
|
|
{
|
|
|
|
hit = 1;
|
|
|
|
|
2018-03-14 23:42:15 +01:00
|
|
|
/* infinite loop - remove these from the quadtree */
|
2018-02-03 09:44:02 +01:00
|
|
|
if (dx == 0 && dy == 0 && solidLoopHits++ > 1)
|
|
|
|
{
|
2018-03-14 23:42:15 +01:00
|
|
|
removeFromQuadtree(self, &world.quadtree);
|
|
|
|
removeFromQuadtree(e, &world.quadtree);
|
2018-02-03 09:44:02 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addTouched(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
clearTouched = 1;
|
2018-02-26 08:38:20 +01:00
|
|
|
|
|
|
|
self->getCollisionBounds(&srcRect);
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
|
|
|
while (hit);
|
|
|
|
}
|
|
|
|
|
2018-02-03 11:01:14 +01:00
|
|
|
static int pushEntity(Entity *e, float dx, float dy)
|
|
|
|
{
|
|
|
|
float expectedX, expectedY;
|
|
|
|
PointF position;
|
|
|
|
Entity *oldSelf;
|
|
|
|
|
|
|
|
expectedX = e->x + dx;
|
|
|
|
expectedY = e->y + dy;
|
|
|
|
|
|
|
|
position.x = e->x;
|
|
|
|
position.y = e->y;
|
2018-02-03 19:17:45 +01:00
|
|
|
|
|
|
|
oldSelf = self;
|
|
|
|
|
|
|
|
self = e;
|
2018-02-03 11:01:14 +01:00
|
|
|
|
|
|
|
if (dx != 0)
|
|
|
|
{
|
|
|
|
position.x += dx;
|
|
|
|
moveToOthers(dx, 0, &position);
|
|
|
|
moveToMap(dx, 0, &position);
|
|
|
|
e->x = position.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dy != 0)
|
|
|
|
{
|
|
|
|
position.y += dy;
|
|
|
|
moveToOthers(0, dy, &position);
|
|
|
|
moveToMap(0, dy, &position);
|
|
|
|
e->y = position.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
self = oldSelf;
|
|
|
|
|
|
|
|
return (e->x == expectedX && e->y == expectedY);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void moveToMap(float dx, float dy, PointF *position)
|
|
|
|
{
|
|
|
|
int i, mx, my, width, height, adjX, adjY, hit;
|
|
|
|
|
|
|
|
width = self->w;
|
|
|
|
height = self->h;
|
|
|
|
adjX = adjY = 0;
|
|
|
|
|
|
|
|
if (self->flags & EF_NO_CLIP)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dx != 0)
|
|
|
|
{
|
|
|
|
width = (dx > 0) ? self->w + 1 : -1;
|
|
|
|
adjX = (dx > 0) ? self->w : -MAP_TILE_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dy != 0)
|
|
|
|
{
|
|
|
|
height = (dy > 0) ? self->h + 1 : -1;
|
|
|
|
adjY = (dy > 0) ? self->h : -MAP_TILE_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
hit = 0;
|
|
|
|
|
|
|
|
mx = (position->x + width) / MAP_TILE_SIZE;
|
|
|
|
my = (position->y + height) / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
if (mx < 0 || my < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* X Axis */
|
|
|
|
|
|
|
|
if (dx != 0)
|
|
|
|
{
|
|
|
|
for (i = 0; i < self->h - 1; i += MAP_TILE_SIZE)
|
|
|
|
{
|
|
|
|
my = (position->y + i) / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
hit = (hasHitWorld(mx, my) ? 1 : hit);
|
|
|
|
}
|
|
|
|
|
|
|
|
my = (position->y + self->h - 1) / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
hit = (hasHitWorld(mx, my) ? 1 : hit);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Y Axis */
|
|
|
|
|
|
|
|
if (dy != 0)
|
|
|
|
{
|
|
|
|
for (i = 0; i < self->w - 1; i += MAP_TILE_SIZE)
|
|
|
|
{
|
|
|
|
mx = (position->x + i) / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
hit = (hasHitWorld(mx, my) ? 1 : hit);
|
|
|
|
}
|
|
|
|
|
|
|
|
mx = (position->x + self->w - 1) / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
hit = (hasHitWorld(mx, my) ? 1 : hit);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hit)
|
|
|
|
{
|
|
|
|
if (dx != 0)
|
|
|
|
{
|
|
|
|
position->x = (mx * MAP_TILE_SIZE) - adjX;
|
|
|
|
self->dx = self->bounce(self->dx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dy != 0)
|
|
|
|
{
|
|
|
|
if (dy > 0)
|
|
|
|
{
|
|
|
|
self->isOnGround = 1;
|
|
|
|
}
|
2018-02-03 12:32:03 +01:00
|
|
|
|
2018-02-03 11:01:14 +01:00
|
|
|
position->y = (my * MAP_TILE_SIZE) - adjY;
|
|
|
|
self->dy = self->bounce(self->dy);
|
|
|
|
self->dy = limit(self->dy, JUMP_POWER, -JUMP_POWER);
|
|
|
|
}
|
|
|
|
|
|
|
|
self->touch(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hasHitWorld(int mx, int my)
|
|
|
|
{
|
|
|
|
if (mx < 0 || mx >= MAP_WIDTH || my < 0 || my >= MAP_HEIGHT)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isSolid(mx, my))
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void compareEnvironments(void)
|
|
|
|
{
|
|
|
|
int prevEnv, x, y;
|
|
|
|
|
|
|
|
prevEnv = self->environment;
|
|
|
|
|
|
|
|
self->environment = ENV_AIR;
|
|
|
|
x = self->x / MAP_TILE_SIZE;
|
|
|
|
y = self->y / MAP_TILE_SIZE;
|
|
|
|
|
|
|
|
if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (world.map.data[x][y])
|
|
|
|
{
|
|
|
|
case MAP_TILE_LAVA:
|
|
|
|
self->environment = ENV_LAVA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MAP_TILE_WATER:
|
|
|
|
self->environment = ENV_WATER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MAP_TILE_SLIME:
|
|
|
|
self->environment = ENV_SLIME;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->environment == prevEnv)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (prevEnv)
|
|
|
|
{
|
|
|
|
case ENV_WATER:
|
2018-02-26 19:56:13 +01:00
|
|
|
playBattleSound(SND_WATER_OUT, self->uniqueId % MAX_SND_CHANNELS, self->x, self->y);
|
2018-02-03 11:01:14 +01:00
|
|
|
if ((self->environment == ENV_AIR) && (self->dy < 0))
|
|
|
|
{
|
|
|
|
self->dy = JUMP_POWER;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENV_AIR:
|
|
|
|
self->dx = 0;
|
|
|
|
self->dy = 0.25f;
|
|
|
|
if (self->environment == ENV_WATER)
|
|
|
|
{
|
2018-02-26 19:56:13 +01:00
|
|
|
playBattleSound(SND_WATER_IN, self->uniqueId % MAX_SND_CHANNELS, self->x, self->y);
|
2018-02-03 11:01:14 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-02-26 19:56:13 +01:00
|
|
|
playBattleSound(SND_SLIME, self->uniqueId % MAX_SND_CHANNELS, self->x, self->y);
|
2018-02-03 11:01:14 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->changeEnvironment();
|
|
|
|
}
|
|
|
|
|
2018-02-03 09:44:02 +01:00
|
|
|
static int isObserving(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_ENTS_TO_OBSERVE ; i++)
|
|
|
|
{
|
|
|
|
if (world.entitiesToObserve[i] == self)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-03 17:31:12 +01:00
|
|
|
void activateEntities(char *nameList, int active)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
2018-02-03 16:53:21 +01:00
|
|
|
Entity *oldSelf;
|
2018-02-03 17:31:12 +01:00
|
|
|
char *name, names[MAX_DESCRIPTION_LENGTH];
|
|
|
|
|
|
|
|
STRNCPY(names, nameList, MAX_DESCRIPTION_LENGTH);
|
2018-02-03 16:53:21 +01:00
|
|
|
|
|
|
|
oldSelf = self;
|
|
|
|
|
|
|
|
name = strtok(names, "|");
|
|
|
|
|
|
|
|
while (name)
|
|
|
|
{
|
2018-02-03 17:31:12 +01:00
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "Activate '%s'", name);
|
|
|
|
|
2018-02-03 16:53:21 +01:00
|
|
|
for (self = world.entityHead.next ; self != NULL ; self = self->next)
|
|
|
|
{
|
|
|
|
if (strcmp(self->name, name) == 0)
|
|
|
|
{
|
|
|
|
self->activate(active);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
name = strtok(NULL, "|");
|
|
|
|
}
|
|
|
|
|
|
|
|
self = oldSelf;
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void teleportEntity(Entity *e, float tx, float ty)
|
|
|
|
{
|
2018-02-03 17:13:37 +01:00
|
|
|
e->tx = tx;
|
|
|
|
e->ty = ty;
|
|
|
|
|
|
|
|
e->flags |= EF_TELEPORTING;
|
|
|
|
|
|
|
|
addTeleportStars(e);
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
|
2018-02-03 09:44:02 +01:00
|
|
|
static void handleTeleport(void)
|
|
|
|
{
|
|
|
|
float diffX, diffY;
|
|
|
|
|
|
|
|
diffX = abs(self->x - self->tx) / 20;
|
|
|
|
diffY = abs(self->y - self->ty) / 20;
|
|
|
|
|
|
|
|
addTeleportStar(self->x + rand() % self->w, self->y + rand() % self->h);
|
|
|
|
|
|
|
|
diffX = MAX(3, MIN(30, diffX));
|
|
|
|
diffY = MAX(3, MIN(30, diffY));
|
|
|
|
|
|
|
|
if (self->x > self->tx)
|
|
|
|
{
|
|
|
|
self->x -= diffX;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->x < self->tx)
|
|
|
|
{
|
|
|
|
self->x += diffX;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->y > self->ty)
|
|
|
|
{
|
|
|
|
self->y -= diffY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->y < self->ty)
|
|
|
|
{
|
|
|
|
self->y += diffY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (collision(self->x, self->y, self->w, self->h, self->tx, self->ty, self->w, self->h))
|
|
|
|
{
|
|
|
|
self->flags &= ~EF_TELEPORTING;
|
|
|
|
self->x = self->tx;
|
|
|
|
self->y = self->ty;
|
|
|
|
addTeleportStars(self);
|
|
|
|
self->dx = self->dy = 0;
|
|
|
|
self->environment = ENV_AIR;
|
2018-03-05 09:39:42 +01:00
|
|
|
self->changeEnvironment();
|
2018-02-26 19:56:13 +01:00
|
|
|
playBattleSound(SND_TELEPORT, self->uniqueId % MAX_SND_CHANNELS, self->x, self->y);
|
2018-03-18 13:48:31 +01:00
|
|
|
|
|
|
|
if (self == (Entity*)world.bob)
|
|
|
|
{
|
|
|
|
terminateJetpack();
|
|
|
|
}
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-01 22:51:43 +01:00
|
|
|
void dropCarriedItem(void)
|
|
|
|
{
|
|
|
|
EntityExt *e;
|
|
|
|
Item *i;
|
|
|
|
|
|
|
|
e = (EntityExt*)self;
|
|
|
|
|
|
|
|
if (e->carriedItem != NULL)
|
|
|
|
{
|
|
|
|
i = e->carriedItem;
|
|
|
|
|
|
|
|
i->x = (e->x + e->w / 2) - i->w / 2;
|
|
|
|
i->y = e->y;
|
|
|
|
|
2018-02-06 23:27:02 +01:00
|
|
|
i->dx = 0;
|
|
|
|
i->dy = -9;
|
|
|
|
i->flags &= ~EF_GONE;
|
2018-02-15 08:50:05 +01:00
|
|
|
i->thinkTime = FPS / 2;
|
2018-02-01 22:51:43 +01:00
|
|
|
|
|
|
|
e->carriedItem = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void teleport(Entity *e, float tx, float ty)
|
|
|
|
{
|
|
|
|
e->tx = tx;
|
|
|
|
e->ty = ty;
|
|
|
|
|
|
|
|
e->flags |= EF_TELEPORTING;
|
|
|
|
|
|
|
|
addTeleportStars(e);
|
2018-02-03 17:13:37 +01:00
|
|
|
|
|
|
|
if (e == (Entity*)world.bob)
|
|
|
|
{
|
|
|
|
terminateJetpack();
|
|
|
|
|
|
|
|
world.bob->flags &= ~(EF_WATER_BREATHING | EF_WEIGHTLESS);
|
|
|
|
}
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
2018-02-02 09:00:27 +01:00
|
|
|
|
|
|
|
Entity *findEntity(char *name)
|
|
|
|
{
|
|
|
|
Entity *e;
|
|
|
|
|
|
|
|
for (e = world.entityHead.next ; e != NULL ; e = e->next)
|
|
|
|
{
|
|
|
|
if (strcmp(e->name, name) == 0)
|
|
|
|
{
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-02-02 20:10:12 +01:00
|
|
|
|
|
|
|
Entity *getRandomObjectiveEntity(void)
|
|
|
|
{
|
|
|
|
Entity *rtn, *e;
|
|
|
|
|
|
|
|
rtn = (Entity*)world.bob;
|
|
|
|
|
|
|
|
for (e = world.entityHead.next ; e != NULL ; e = e->next)
|
|
|
|
{
|
2018-02-03 12:32:03 +01:00
|
|
|
if (e->isMissionTarget && rand() % 4 == 0)
|
2018-02-02 20:10:12 +01:00
|
|
|
{
|
2018-02-03 12:32:03 +01:00
|
|
|
return e;
|
2018-02-02 20:10:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rtn;
|
|
|
|
}
|
2018-02-03 09:44:02 +01:00
|
|
|
|
|
|
|
static void doMarker(Marker *m, int delta)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0 ; i < 3 ; i++)
|
|
|
|
{
|
2018-02-03 12:47:50 +01:00
|
|
|
m->value -= (0.05 * delta);
|
2018-02-03 09:44:02 +01:00
|
|
|
m->y = 15 + (float) sin(m->value) * 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void addRider(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_RIDERS ; i++)
|
|
|
|
{
|
|
|
|
if (!riders[i])
|
|
|
|
{
|
|
|
|
riders[i] = self;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-02-15 22:40:21 +01:00
|
|
|
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "Couldn't add rider: out of array space.");
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void addTouched(Entity *e)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0 ; i < MAX_TOUCHED ; i++)
|
|
|
|
{
|
|
|
|
if (!touched[i])
|
|
|
|
{
|
|
|
|
touched[i] = e;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-02-15 22:40:21 +01:00
|
|
|
|
|
|
|
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_WARN, "Couldn't add touched: out of array space.");
|
2018-02-03 09:44:02 +01:00
|
|
|
}
|
2018-02-05 09:37:07 +01:00
|
|
|
|
|
|
|
void swapSelf(Entity *e)
|
|
|
|
{
|
|
|
|
static Entity *oldSelf = NULL;
|
|
|
|
|
|
|
|
if (!oldSelf)
|
|
|
|
{
|
|
|
|
oldSelf = self;
|
|
|
|
self = e;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self = oldSelf;
|
|
|
|
oldSelf = NULL;
|
|
|
|
}
|
|
|
|
}
|
2018-02-05 23:06:00 +01:00
|
|
|
|
|
|
|
static int drawComparator(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
Entity *e1 = *((Entity**)a);
|
|
|
|
Entity *e2 = *((Entity**)b);
|
|
|
|
|
|
|
|
return e2->type - e1->type;
|
|
|
|
}
|
2018-02-17 17:53:30 +01:00
|
|
|
|
|
|
|
void destroyEntities(void)
|
|
|
|
{
|
|
|
|
Entity *e;
|
|
|
|
|
|
|
|
while (world.entityHead.next)
|
|
|
|
{
|
|
|
|
e = world.entityHead.next;
|
|
|
|
|
|
|
|
world.entityHead.next = e->next;
|
|
|
|
|
|
|
|
free(e);
|
|
|
|
}
|
|
|
|
}
|