2018-01-26 20:14:18 +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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "bob.h"
|
|
|
|
|
2018-01-31 23:28:00 +01:00
|
|
|
static SDL_Rect *getCurrentSprite(void);
|
2018-01-31 23:54:14 +01:00
|
|
|
static void (*superAnimate)(void);
|
|
|
|
static void animate(void);
|
2018-02-01 23:29:51 +01:00
|
|
|
static void init(void);
|
2018-02-01 22:51:43 +01:00
|
|
|
static void tick(void);
|
|
|
|
static void doAlive(void);
|
|
|
|
static void doDying(void);
|
|
|
|
static void doBobInWater(void);
|
|
|
|
static void doBobInAir(void);
|
|
|
|
static void handeImmunity(void);
|
|
|
|
static void changeSprite(Sprite **sprite);
|
2018-01-31 22:50:49 +01:00
|
|
|
static void load(cJSON *root);
|
|
|
|
static void save(cJSON *root);
|
2018-02-01 22:51:43 +01:00
|
|
|
static int completelyOnGround(void);
|
|
|
|
void resetAtCheckpoint(void);
|
|
|
|
void terminateJetpack(void);
|
|
|
|
static void bobFly(void);
|
|
|
|
static void bobWalk(void);
|
|
|
|
static void bobSwim(void);
|
|
|
|
static void fireWeapon(void);
|
|
|
|
static void activate(int activate);
|
|
|
|
static void changeEnvironment(void);
|
2018-02-08 23:24:19 +01:00
|
|
|
static void applyDamage(int damage);
|
|
|
|
static void die(void);
|
2018-02-01 22:51:43 +01:00
|
|
|
|
|
|
|
static Sprite *walkSprite[3];
|
|
|
|
static Sprite *swimSprite[3];
|
|
|
|
static Sprite *flySprite[3];
|
|
|
|
static int checkpointTimer;
|
2018-01-31 22:50:49 +01:00
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
Entity *initBob(void)
|
2018-01-28 10:34:14 +01:00
|
|
|
{
|
2018-02-01 23:29:51 +01:00
|
|
|
Bob *b;
|
2018-01-29 23:12:18 +01:00
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
b = malloc(sizeof(Bob));
|
|
|
|
memset(b, 0, sizeof(Bob));
|
2018-01-29 23:12:18 +01:00
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
initEntity((Entity*)b);
|
2018-01-31 22:50:49 +01:00
|
|
|
|
2018-02-11 12:08:21 +01:00
|
|
|
STRNCPY(b->name, "Bob", MAX_NAME_LENGTH);
|
2018-02-01 23:29:51 +01:00
|
|
|
b->type = ET_BOB;
|
2018-02-01 22:51:43 +01:00
|
|
|
|
|
|
|
walkSprite[FACING_LEFT] = getSprite("BobLeft");
|
|
|
|
walkSprite[FACING_RIGHT] = getSprite("BobRight");
|
|
|
|
walkSprite[FACING_DIE] = getSprite("BobSpin");
|
|
|
|
|
|
|
|
swimSprite[FACING_LEFT] = getSprite("BobAquaLeft");
|
|
|
|
swimSprite[FACING_RIGHT] = getSprite("BobAquaRight");
|
|
|
|
swimSprite[FACING_DIE] = getSprite("BobAquaSpin");
|
|
|
|
|
|
|
|
flySprite[FACING_LEFT] = getSprite("BobJPLeft");
|
|
|
|
flySprite[FACING_RIGHT] = getSprite("BobJPRight");
|
|
|
|
flySprite[FACING_DIE] = getSprite("BobSpin");
|
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
b->health = b->healthMax = game.hearts;
|
|
|
|
b->power = b->powerMax = game.cells;
|
2018-02-15 23:56:34 +01:00
|
|
|
b->oxygen = MAX_OXYGEN;
|
2018-02-01 22:51:43 +01:00
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
b->weaponType = WPN_PISTOL;
|
|
|
|
b->reload = 0;
|
2018-02-01 22:51:43 +01:00
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
b->flags |= EF_SWIMS | EF_BOMB_SHIELD;
|
|
|
|
|
|
|
|
superAnimate = b->animate;
|
2018-01-31 22:50:49 +01:00
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
b->tick = tick;
|
|
|
|
b->init = init;
|
2018-02-08 23:24:19 +01:00
|
|
|
b->applyDamage = applyDamage;
|
2018-02-01 23:29:51 +01:00
|
|
|
b->getCurrentSprite = getCurrentSprite;
|
|
|
|
b->animate = animate;
|
|
|
|
b->activate = activate;
|
|
|
|
b->changeEnvironment = changeEnvironment;
|
2018-02-08 23:24:19 +01:00
|
|
|
b->die = die;
|
2018-02-01 23:29:51 +01:00
|
|
|
b->load = load;
|
|
|
|
b->save = save;
|
2018-01-31 23:54:14 +01:00
|
|
|
|
2018-02-01 23:29:51 +01:00
|
|
|
return (Entity*)b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init(void)
|
|
|
|
{
|
|
|
|
changeSprite(walkSprite);
|
2018-02-03 00:01:51 +01:00
|
|
|
|
|
|
|
world.bob->checkpoints[0].x = world.bob->x;
|
|
|
|
world.bob->checkpoints[0].y = world.bob->y;
|
2018-02-03 14:07:56 +01:00
|
|
|
|
|
|
|
superAnimate();
|
2018-01-28 10:34:14 +01:00
|
|
|
}
|
|
|
|
|
2018-02-01 22:51:43 +01:00
|
|
|
static void tick(void)
|
|
|
|
{
|
2018-02-14 08:26:51 +01:00
|
|
|
if (world.missionType == MT_TRAINING || dev.cheatHealth)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->alive = ALIVE_ALIVE;
|
|
|
|
world.bob->health = world.bob->healthMax;
|
|
|
|
}
|
|
|
|
|
2018-02-14 08:26:51 +01:00
|
|
|
if (world.missionType == MT_TRAINING)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->power = MIN(world.bob->power + 0.01, world.bob->powerMax);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
world.bob->power = MIN(world.bob->power + 0.00065, world.bob->powerMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev.cheatPower)
|
|
|
|
{
|
|
|
|
world.bob->power = world.bob->powerMax;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev.cheatReload && world.bob->reload > 4)
|
|
|
|
{
|
|
|
|
world.bob->reload = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (world.bob->alive)
|
|
|
|
{
|
|
|
|
case ALIVE_ALIVE:
|
|
|
|
doAlive();
|
|
|
|
break;
|
|
|
|
case ALIVE_DYING:
|
|
|
|
doDying();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void doAlive(void)
|
|
|
|
{
|
|
|
|
handeImmunity();
|
|
|
|
|
|
|
|
world.bob->checkpointTimer = MAX(world.bob->checkpointTimer - 1, 0);
|
|
|
|
|
|
|
|
world.bob->reload = limit(world.bob->reload - 1, 0, FPS);
|
|
|
|
|
|
|
|
switch (world.bob->environment)
|
|
|
|
{
|
|
|
|
case ENV_AIR:
|
|
|
|
doBobInAir();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENV_WATER:
|
|
|
|
doBobInWater();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENV_SLIME:
|
|
|
|
if (world.bob->alive == ALIVE_ALIVE && world.bob->outTimer == 0)
|
|
|
|
{
|
|
|
|
world.bob->outTimer = FPS * 3;
|
|
|
|
world.bob->stunTimer = 1;
|
|
|
|
if (world.bob->flags & EF_IMMUNE)
|
|
|
|
{
|
|
|
|
world.bob->health--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENV_LAVA:
|
|
|
|
if (world.bob->alive == ALIVE_ALIVE && world.bob->outTimer == 0)
|
|
|
|
{
|
|
|
|
world.bob->outTimer = FPS * 3;
|
|
|
|
world.bob->stunTimer = 1;
|
|
|
|
if (world.bob->flags & EF_IMMUNE)
|
|
|
|
{
|
|
|
|
world.bob->health -= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handeImmunity(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (world.bob->outTimer > 0)
|
|
|
|
{
|
|
|
|
world.bob->outTimer--;
|
|
|
|
if (world.bob->outTimer <= 0)
|
|
|
|
{
|
|
|
|
world.betweenTimer = FPS / 2;
|
|
|
|
resetAtCheckpoint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (world.bob->stunTimer > 0)
|
|
|
|
{
|
|
|
|
world.bob->stunTimer--;
|
|
|
|
if (world.bob->stunTimer <= 0)
|
|
|
|
{
|
|
|
|
if (!world.bob->isOnGround && world.bob->environment != ENV_WATER)
|
|
|
|
{
|
|
|
|
world.bob->stunTimer = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
world.bob->spriteFrame = 0;
|
|
|
|
world.bob->spriteTime = 0;
|
|
|
|
world.bob->dy = 0;
|
|
|
|
world.bob->flags &= ~EF_BOUNCES;
|
|
|
|
world.bob->flags |= EF_FLICKER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (world.bob->immuneTimer > 0)
|
|
|
|
{
|
|
|
|
world.bob->immuneTimer--;
|
|
|
|
if (world.bob->immuneTimer <= 0)
|
|
|
|
{
|
|
|
|
world.bob->flags &= ~(EF_FLICKER | EF_IMMUNE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (world.bob->checkpointTimer == 0 && world.bob->isOnGround && completelyOnGround() && world.bob->environment == ENV_AIR && world.bob->riding == NULL)
|
|
|
|
{
|
|
|
|
for (i = MAX_CHECKPOINTS - 1 ; i > 0 ; i--)
|
|
|
|
{
|
|
|
|
world.bob->checkpoints[i].x = world.bob->checkpoints[i - 1].x;
|
|
|
|
world.bob->checkpoints[i].y = world.bob->checkpoints[i - 1].y;
|
|
|
|
}
|
|
|
|
|
|
|
|
world.bob->checkpoints[0].x = world.bob->x;
|
|
|
|
world.bob->checkpoints[0].y = world.bob->y;
|
|
|
|
checkpointTimer = FPS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int completelyOnGround(void)
|
|
|
|
{
|
|
|
|
int x1, x2, y;
|
|
|
|
|
|
|
|
x1 = (world.bob->x / MAP_TILE_SIZE);
|
|
|
|
x2 = ((world.bob->x + world.bob->w) / MAP_TILE_SIZE);
|
|
|
|
y = ((world.bob->y + world.bob->h + 8) / MAP_TILE_SIZE);
|
|
|
|
|
|
|
|
return isSolid(x1, y) && isSolid(x2, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void activate(int activate)
|
|
|
|
{
|
|
|
|
if (world.bob->stunTimer <= 0)
|
|
|
|
{
|
|
|
|
if (world.bob->flags & (EF_WATER_BREATHING | EF_WEIGHTLESS))
|
|
|
|
{
|
|
|
|
world.bob->flags &= ~(EF_WATER_BREATHING | EF_WEIGHTLESS);
|
|
|
|
changeSprite(walkSprite);
|
|
|
|
}
|
|
|
|
else if (world.bob->power > 0)
|
|
|
|
{
|
|
|
|
if (world.bob->environment == ENV_AIR)
|
|
|
|
{
|
|
|
|
world.bob->flags |= EF_WEIGHTLESS;
|
|
|
|
changeSprite(flySprite);
|
|
|
|
}
|
|
|
|
else if (world.bob->environment == ENV_WATER)
|
|
|
|
{
|
|
|
|
world.bob->flags |= EF_WATER_BREATHING;
|
|
|
|
changeSprite(swimSprite);
|
|
|
|
}
|
|
|
|
|
|
|
|
world.bob->dy = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (world.bob->environment == ENV_AIR)
|
|
|
|
{
|
|
|
|
setGameplayMessage(MSG_STANDARD, _("Not enough power for jetpack"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setGameplayMessage(MSG_STANDARD, _("Not enough power for aqualung"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void doBobInAir(void)
|
|
|
|
{
|
|
|
|
world.bob->oxygen = limit(world.bob->oxygen + 4, 0, MAX_OXYGEN);
|
|
|
|
|
|
|
|
if (world.bob->flags & EF_WEIGHTLESS)
|
|
|
|
{
|
|
|
|
if (!dev.cheatPower)
|
|
|
|
{
|
|
|
|
world.bob->power = limit(world.bob->power - (1.0 / FPS), 0, world.bob->powerMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (--world.bob->jpEffectTimer <= 0)
|
|
|
|
{
|
|
|
|
addFlameParticles(world.bob->x + (world.bob->facing * 25) + rrnd(-1, 1), world.bob->y + 25 + rrnd(-1, 1));
|
|
|
|
world.bob->jpEffectTimer = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (world.bob->power == 0)
|
|
|
|
{
|
|
|
|
terminateJetpack();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void terminateJetpack(void)
|
|
|
|
{
|
|
|
|
changeSprite(walkSprite);
|
|
|
|
|
|
|
|
world.bob->flags &= ~EF_WEIGHTLESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void doBobInWater(void)
|
|
|
|
{
|
|
|
|
if (world.bob->flags & EF_WATER_BREATHING)
|
|
|
|
{
|
|
|
|
if (!dev.cheatPower)
|
|
|
|
{
|
|
|
|
world.bob->power = limit(world.bob->power - (1.0 / FPS), 0, world.bob->powerMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (world.bob->power == 0)
|
|
|
|
{
|
|
|
|
world.bob->flags &= ~EF_WATER_BREATHING;
|
|
|
|
changeSprite(walkSprite);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
world.bob->oxygen = limit(--world.bob->oxygen, 0, MAX_OXYGEN);
|
|
|
|
|
|
|
|
if (world.bob->oxygen == 0 && (world.frameCounter % 30) == 0)
|
|
|
|
{
|
|
|
|
world.bob->health--;
|
|
|
|
playSound(SND_DROWN, CH_BOB);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void doDying(void)
|
|
|
|
{
|
|
|
|
world.bob->health--;
|
|
|
|
|
|
|
|
if (world.bob->health <= -(FPS * 2))
|
|
|
|
{
|
|
|
|
world.bob->alive = ALIVE_DEAD;
|
|
|
|
|
|
|
|
throwFleshChunks(world.bob->x + world.bob->w / 2, world.bob->y + world.bob->h / 2, rrnd(3, 6));
|
|
|
|
|
|
|
|
world.state = WS_GAME_OVER;
|
|
|
|
|
|
|
|
playSound(SND_SPLAT, CH_BOB);
|
|
|
|
|
2018-02-13 22:36:42 +01:00
|
|
|
game.stats[STAT_DEATHS]++;
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void stunBob(void)
|
|
|
|
{
|
|
|
|
terminateJetpack();
|
|
|
|
|
|
|
|
world.bob->stunTimer = FPS;
|
|
|
|
world.bob->immuneTimer = FPS * 2;
|
|
|
|
world.bob->spriteFrame = 0;
|
|
|
|
world.bob->spriteTime = 0;
|
|
|
|
world.bob->flags &= ~EF_FLICKER;
|
|
|
|
world.bob->flags |= (EF_BOUNCES | EF_IMMUNE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-08 23:24:19 +01:00
|
|
|
static void applyDamage(int damage)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
2018-02-14 08:26:51 +01:00
|
|
|
if (!(world.bob->flags & EF_IMMUNE) && world.missionType != MT_TRAINING && !dev.cheatHealth && world.bob->alive != ALIVE_DEAD)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
if (world.bob->health < 0)
|
|
|
|
{
|
|
|
|
world.bob->health = 0;
|
|
|
|
world.bob->alive = ALIVE_ALIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
world.bob->health -= damage;
|
|
|
|
|
|
|
|
if (world.bob->health > 0)
|
|
|
|
{
|
|
|
|
world.bob->immuneTimer = FPS * 2;
|
|
|
|
|
|
|
|
world.bob->flags |= EF_FLICKER | EF_IMMUNE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void doBob(void)
|
|
|
|
{
|
|
|
|
if (world.bob->stunTimer <= 0 && world.bob->health > 0)
|
|
|
|
{
|
|
|
|
if (world.bob->flags & EF_WEIGHTLESS)
|
|
|
|
{
|
|
|
|
bobFly();
|
|
|
|
}
|
|
|
|
else if (world.bob->environment == ENV_AIR)
|
|
|
|
{
|
|
|
|
bobWalk();
|
|
|
|
}
|
|
|
|
else if (world.bob->environment == ENV_WATER)
|
|
|
|
{
|
|
|
|
bobSwim();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bobWalk(void)
|
|
|
|
{
|
|
|
|
world.bob->dx = 0;
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_LEFT))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dx = -WALK_SPEED;
|
|
|
|
world.bob->facing = FACING_LEFT;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_RIGHT))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dx = WALK_SPEED;
|
|
|
|
world.bob->facing = FACING_RIGHT;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_JUMP) && world.bob->isOnGround)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dy = JUMP_POWER;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_FIRE) && world.bob->reload == 0)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
fireWeapon();
|
|
|
|
}
|
2018-02-07 19:27:42 +01:00
|
|
|
|
|
|
|
if (isControl(CONTROL_JETPACK))
|
|
|
|
{
|
|
|
|
activate(1);
|
|
|
|
clearControl(CONTROL_JETPACK);
|
|
|
|
}
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bobSwim(void)
|
|
|
|
{
|
|
|
|
world.bob->dx = 0;
|
|
|
|
world.bob->dy = 0.5f;
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_LEFT))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dx = -SWIM_SPEED;
|
|
|
|
world.bob->facing = FACING_LEFT;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_RIGHT))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dx = SWIM_SPEED;
|
|
|
|
world.bob->facing = FACING_RIGHT;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_JUMP) || isControl(CONTROL_UP))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dy = -SWIM_SPEED;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_DOWN))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dy = SWIM_SPEED;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_FIRE) && world.bob->reload == 0 && world.bob->weaponType == WPN_PISTOL)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
firePistol();
|
|
|
|
}
|
2018-02-07 19:27:42 +01:00
|
|
|
|
|
|
|
if (isControl(CONTROL_JETPACK))
|
|
|
|
{
|
|
|
|
activate(1);
|
|
|
|
clearControl(CONTROL_JETPACK);
|
|
|
|
}
|
2018-02-13 22:36:42 +01:00
|
|
|
|
|
|
|
game.stats[STAT_SWIM_TIME]++;
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bobFly(void)
|
|
|
|
{
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_LEFT))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dx -= FLY_ACCEL;
|
|
|
|
world.bob->facing = FACING_LEFT;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_RIGHT))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dx += FLY_ACCEL;
|
|
|
|
world.bob->facing = FACING_RIGHT;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_UP))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dy -= FLY_ACCEL;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_DOWN))
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->dy += FLY_ACCEL;
|
|
|
|
}
|
|
|
|
|
2018-02-07 19:27:42 +01:00
|
|
|
if (isControl(CONTROL_FIRE) && world.bob->reload == 0)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
fireWeapon();
|
|
|
|
}
|
2018-02-07 19:27:42 +01:00
|
|
|
|
|
|
|
if (isControl(CONTROL_JETPACK))
|
|
|
|
{
|
|
|
|
activate(1);
|
|
|
|
clearControl(CONTROL_JETPACK);
|
|
|
|
}
|
2018-02-01 22:51:43 +01:00
|
|
|
|
|
|
|
world.bob->dx = MIN(FLY_SPEED, MAX(world.bob->dx, -FLY_SPEED));
|
|
|
|
world.bob->dy = MIN(FLY_SPEED, MAX(world.bob->dy, -FLY_SPEED));
|
2018-02-13 22:36:42 +01:00
|
|
|
|
|
|
|
game.stats[STAT_FLY_TIME]++;
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void fireWeapon(void)
|
|
|
|
{
|
|
|
|
switch (world.bob->weaponType)
|
|
|
|
{
|
|
|
|
case WPN_PISTOL:
|
|
|
|
firePistol();
|
|
|
|
break;
|
|
|
|
case WPN_PLASMA:
|
|
|
|
firePlasma((Entity*)world.bob);
|
|
|
|
break;
|
|
|
|
case WPN_SPREAD:
|
|
|
|
fireSpread((Entity*)world.bob, 3);
|
|
|
|
break;
|
|
|
|
case WPN_LASER:
|
|
|
|
fireLaser((Entity*)world.bob);
|
|
|
|
break;
|
|
|
|
case WPN_GRENADES:
|
|
|
|
fireGrenade((Entity*)world.bob);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-02-13 22:36:42 +01:00
|
|
|
game.stats[STAT_SHOTS_FIRED]++;
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void changeEnvironment(void)
|
|
|
|
{
|
|
|
|
switch (world.bob->environment)
|
|
|
|
{
|
|
|
|
case ENV_AIR:
|
|
|
|
case ENV_LAVA:
|
|
|
|
case ENV_SLIME:
|
|
|
|
world.bob->flags &= ~EF_WATER_BREATHING;
|
|
|
|
changeSprite(walkSprite);
|
|
|
|
break;
|
|
|
|
case ENV_WATER:
|
|
|
|
terminateJetpack();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void changeSprite(Sprite **sprite)
|
|
|
|
{
|
|
|
|
world.bob->sprite[0] = sprite[0];
|
|
|
|
world.bob->sprite[1] = sprite[1];
|
|
|
|
world.bob->sprite[2] = sprite[2];
|
|
|
|
world.bob->spriteFrame = 0;
|
|
|
|
world.bob->spriteTime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void resetAtCheckpoint(void)
|
|
|
|
{
|
|
|
|
world.bob->x = world.bob->checkpoints[0].x;
|
|
|
|
world.bob->y = world.bob->checkpoints[0].y;
|
|
|
|
|
|
|
|
world.bob->outTimer = 0;
|
|
|
|
world.bob->flags |= EF_FLICKER;
|
|
|
|
world.bob->flags &= ~EF_WEIGHTLESS;
|
|
|
|
changeSprite(walkSprite);
|
|
|
|
world.bob->environment = ENV_AIR;
|
|
|
|
|
2018-02-18 12:25:00 +01:00
|
|
|
world.bob->immuneTimer = FPS * 2;
|
|
|
|
addTeleportStars((Entity*)world.bob);
|
|
|
|
if (world.state == WS_IN_PROGRESS)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
2018-02-18 12:25:00 +01:00
|
|
|
playSound(SND_APPEAR, CH_ANY);
|
2018-02-01 22:51:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-08 23:24:19 +01:00
|
|
|
static void die(void)
|
2018-02-01 22:51:43 +01:00
|
|
|
{
|
|
|
|
world.bob->flags &= ~EF_WEIGHTLESS;
|
|
|
|
|
|
|
|
world.bob->flags |= EF_BOUNCES;
|
|
|
|
|
|
|
|
if (world.bob->environment == ENV_AIR)
|
|
|
|
{
|
|
|
|
world.bob->dy = -9;
|
|
|
|
}
|
|
|
|
|
|
|
|
world.bob->dx = (randF() - randF()) * 5;
|
|
|
|
|
|
|
|
switch (rand() % 3)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
playSound(SND_DEATH_1, CH_DEATH);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
playSound(SND_DEATH_2, CH_DEATH);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
playSound(SND_DEATH_3, CH_DEATH);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-31 23:28:00 +01:00
|
|
|
static SDL_Rect *getCurrentSprite(void)
|
|
|
|
{
|
|
|
|
if (world.bob->alive == ALIVE_ALIVE && world.bob->stunTimer <= 0)
|
|
|
|
{
|
2018-02-03 00:01:51 +01:00
|
|
|
return &world.bob->sprite[world.bob->facing]->frames[world.bob->spriteFrame]->rect;
|
2018-01-31 23:28:00 +01:00
|
|
|
}
|
|
|
|
|
2018-02-03 00:01:51 +01:00
|
|
|
return &world.bob->sprite[FACING_DIE]->frames[world.bob->spriteFrame]->rect;
|
2018-01-31 23:28:00 +01:00
|
|
|
}
|
|
|
|
|
2018-01-31 23:54:14 +01:00
|
|
|
static void animate(void)
|
|
|
|
{
|
|
|
|
if (world.bob->dx != 0 || world.bob->stunTimer > 0 || world.bob->flags & EF_WEIGHTLESS || world.bob->health <= 0)
|
|
|
|
{
|
|
|
|
superAnimate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-31 22:50:49 +01:00
|
|
|
static void load(cJSON *root)
|
|
|
|
{
|
|
|
|
world.bob->x = cJSON_GetObjectItem(root, "x")->valueint;
|
|
|
|
world.bob->y = cJSON_GetObjectItem(root, "y")->valueint;
|
|
|
|
world.bob->facing = lookup(cJSON_GetObjectItem(root, "facing")->valuestring);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void save(cJSON *root)
|
|
|
|
{
|
|
|
|
cJSON_AddStringToObject(root, "type", "Bob");
|
2018-02-15 22:39:46 +01:00
|
|
|
cJSON_AddNumberToObject(root, "x", world.bob->checkpoints[0].x);
|
|
|
|
cJSON_AddNumberToObject(root, "y", world.bob->checkpoints[0].y);
|
2018-01-31 22:50:49 +01:00
|
|
|
cJSON_AddStringToObject(root, "facing", getLookupName("FACING_", world.bob->facing));
|
|
|
|
}
|