Files
RustRaptor/raptor/src/enemy.cpp
T
raptorreplica 214789fe87 Fork the vendored engine into the playable replica
Engine changes vs the vendored upstream (full ledger in
docs/upstream-patches.md):

- Assets: replace the encrypted FILE000N.GLB archives with a catalog of
  pre-decrypted loose files (data/INDEX); GLB_SlotItems() drives
  mission-pack / registered-version detection; payloads never run the
  GLB cipher at runtime.
- Platform: Linux/SDL2/ALSA only; Android, MSVC/MinGW, macOS packaging,
  bundled Windows SDL2 SDKs and the ENDOOM exit screen are gone;
  EXIT_Error now prints before exiting.
- Rendering: decouple presentation from simulation -- sim stays at the
  original 70/3 Hz, frames render interpolated at display rate (player,
  map scroll, enemies, shots, bonuses, anims, shadows); menus keep 70 Hz
  pacing; vsync with a delay-guard fallback.
- QoL: borderless window mode (Video/borderless), no menu cursor
  snapping onto default buttons.

Fix four regressions from the render split:

- Shield bars never redrew: RAP_ThinkStats latches g_oldshield each sim
  step before RAP_DisplayStats' dirty check could fire. Bars now draw
  unconditionally per rendered frame; g_oldsuper deleted, g_oldshield
  kept as sim-side damage-detection state only.
- Dying ships despawned without exploding: the render loop never called
  ANIMS_DisplayHigh(); most air-death explosions register HIGH_AIR.
  Restored between the player and enemy shots, as upstream orders it.
- Laser turret broke: reusing doneflag as a draw marker (it is
  hit/retire game state, and the turret retire path never frees its
  shot) hid the beam on hitting ticks and leaked one SHOTS pool slot
  per fire until no weapon could shoot. S_LINE tracers now carry an age
  counter: one Think pass, every render of their birth step, purged at
  the top of the next Think.
- Muzzle flash was cleared per rendered frame, making it invisible at
  display refresh rates; now cleared once per simulation step.
2026-08-26 18:45:07 +10:00

1259 lines
36 KiB
C++

#include <stdio.h>
#include <string.h>
#include "common.h"
#include "enemy.h"
#include "gfxapi.h"
#include "rap.h"
#include "glbapi.h"
#include "tile.h"
#include "loadsave.h"
#include "fx.h"
#include "eshot.h"
#include "shadow.h"
#include "anims.h"
#include "objects.h"
#include "bonus.h"
#include "flame.h"
#include "joyapi.h"
#include "input.h"
#include "fileids.h"
#include "entypes.h"
#define NORM_SHOOT -1
#define START_SHOOT 0
int spriteflag[4];
int spriteitm[4] = {
FILE1fc_SPRITE1_ITM, FILE200_SPRITE2_ITM, FILE300_SPRITE3_ITM, FILE400_SPRITE4_ITM
};
SPRITE *slib[4];
int cur_visable;
int boss_sound;
int g_numslibs;
int numslibs[4];
int numboss, numships;
int end_waveflag;
SPRITE_SHIP ships[30];
SPRITE_SHIP last_enemy, first_enemy;
SPRITE_SHIP *free_enemy;
CSPRITE *end_enemy, *cur_enemy;
int tiley;
SPRITE_SHIP *onscreen[MAX_ONSCREEN], *rscreen[MAX_ONSCREEN];
/***************************************************************************
MoveEobj() - gets next postion for an Object at speed
***************************************************************************/
int
MoveEobj(
MOVEOBJ *cur, // INPUT : pointer to MOVEOBJ
int speed // INPUT : speed to plot at
)
{
if (!speed)
return 0;
if (cur->delx >= cur->dely)
{
while (speed)
{
speed--;
cur->maxloop--;
if (cur->maxloop == 0)
{
cur->done = 1;
return speed;
}
cur->x += cur->addx;
cur->err += cur->dely;
if (cur->err > 0)
{
cur->y += cur->addy;
cur->err -= cur->delx;
}
}
}
else
{
while (speed)
{
speed--;
cur->maxloop--;
if (cur->maxloop == 0)
{
cur->done = 1;
return speed;
}
cur->y += cur->addy;
cur->err += cur->delx;
if (cur->err > 0)
{
cur->x += cur->addx;
cur->err -= cur->dely;
}
}
}
if (cur->maxloop < 1)
cur->done = 1;
return speed;
}
/***************************************************************************
ENEMY_FreeSprites () - Free Memory Used by Sprites use in last level
***************************************************************************/
void
ENEMY_FreeSprites(
void
)
{
int loop, i;
CSPRITE *curfld;
SPRITE *curlib;
for (loop = 0; loop < 4; loop++)
{
if (spriteflag[loop])
GLB_FreeItem(spriteitm[loop]);
}
for (loop = 0; loop < LE_LONG(mapmem->numsprites); loop++)
{
curfld = &csprite[loop];
curlib = &slib[LE_LONG(csprite[loop].game)][LE_LONG(csprite[loop].slib)];
if (cur_diff & curfld->level)
{
for (i = 0; i < LE_LONG(curlib->num_frames); i++)
{
GLB_FreeItem(LE_LONG(curlib->item) + i);
}
}
}
}
/***************************************************************************
ENEMY_LoadSprites() -
***************************************************************************/
void
ENEMY_LoadSprites(
void
)
{
int loop, i, item;
CSPRITE *curfld;
SPRITE *curlib;
ENEMY_Clear();
cur_visable = 0;
boss_sound = 0;
for (loop = 0; loop < LE_LONG(mapmem->numsprites); loop++)
{
curfld = &csprite[loop];
curlib = &slib[LE_LONG(csprite[loop].game)][LE_LONG(csprite[loop].slib)];
curlib->item = LE_LONG(GLB_GetItemID(curlib->iname));
switch (LE_LONG(curfld->level))
{
default:
curfld->level = EB_NOT_USED;
break;
case E_SECRET_1:
curfld->level = EB_SECRET_1;
break;
case E_SECRET_2:
curfld->level = EB_SECRET_2;
break;
case E_SECRET_3:
curfld->level = EB_SECRET_3;
break;
case E_EASY_LEVEL:
curfld->level = EB_EASY_LEVEL;
break;
case E_MED_LEVEL:
curfld->level = EB_MED_LEVEL;
break;
case E_HARD_LEVEL:
curfld->level = EB_HARD_LEVEL;
break;
}
if (cur_diff & curfld->level)
{
if (LE_LONG(curlib->item) != -1)
{
for (i = 0; i < LE_LONG(curlib->num_frames); i++)
{
item = LE_LONG(curlib->item) + i;
GLB_CacheItem(item);
}
}
else
{
curfld->level = EB_NOT_USED;
}
}
else
curfld->level = EB_NOT_USED;
}
}
/***************************************************************************
ENEMY_LoadLib () - Loads and Locks spritelib's MUST becalled b4 LoadSprites
***************************************************************************/
void ENEMY_LoadLib(
void
)
{
int loop;
memset(spriteflag, 0, sizeof(spriteflag));
for (loop = 0; loop < LE_LONG(mapmem->numsprites); loop++)
{
spriteflag[LE_LONG(csprite[loop].game)] = 1;
}
g_numslibs = 0;
for (loop = 0; loop < 4; loop++)
{
slib[loop] = NULL;
numslibs[loop] = 0;
if (spriteflag[loop])
g_numslibs++;
}
if (g_numslibs > 1 && !gameflag[2] && !gameflag[3])
EXIT_Error("ENEMY_LoadSprites() - F:%d G1:%d G2:%d G3:%d G4:%d", g_numslibs, spriteflag[0], spriteflag[1], spriteflag[2], spriteflag[3]);
for (loop = 0; loop < 4; loop++)
{
if (spriteflag[loop])
{
slib[loop] = (SPRITE*)GLB_LockItem(spriteitm[loop]);
if (!slib[loop])
EXIT_Error("ENEMY_LoadSprites() - memory");
numslibs[loop] = GLB_ItemSize(spriteitm[loop]);
numslibs[loop] /= sizeof(SPRITE);
}
}
}
/***************************************************************************
ENEMY_Clear()
***************************************************************************/
void ENEMY_Clear(
void
)
{
int loop;
numboss = 0;
numships = 0;
end_waveflag = 0;
first_enemy.prev = NULL;
first_enemy.next = &last_enemy;
last_enemy.prev = &first_enemy;
last_enemy.next = NULL;
free_enemy = ships;
memset(ships, 0, sizeof(ships));
for (loop = 0; loop < MAX_ONSCREEN - 1; loop++)
{
ships[loop].next = &ships[loop + 1];
}
if (LE_LONG(mapmem->numsprites))
{
end_enemy = csprite + LE_LONG(mapmem->numsprites) - 1;
cur_enemy = csprite;
}
else
{
end_enemy = NULL;
cur_enemy = NULL;
}
}
/*-------------------------------------------------------------------------*
ENEMY_Get() - Gets An Free Enemy from link list
*-------------------------------------------------------------------------*/
SPRITE_SHIP
*ENEMY_Get(
void
)
{
SPRITE_SHIP *sh;
if (!free_enemy)
EXIT_Error("ENEMY_Get() - Max Sprites");
numships++;
sh = free_enemy;
free_enemy = free_enemy->next;
memset(sh, 0, sizeof(SPRITE_SHIP));
sh->next = &last_enemy;
sh->prev = last_enemy.prev;
last_enemy.prev = sh;
sh->prev->next = sh;
return sh;
}
/*-------------------------------------------------------------------------*
ENEMY_Remove () - Removes an Enemy OBJECT from linklist
*-------------------------------------------------------------------------*/
SPRITE_SHIP
*ENEMY_Remove(
SPRITE_SHIP *sh
)
{
SPRITE_SHIP *next;
if (LE_LONG(sh->lib->bossflag))
numboss--;
numships--;
if (end_waveflag && numships < 1)
startendwave = END_DURATION;
next = sh->prev;
sh->next->prev = sh->prev;
sh->prev->next = sh->next;
memset(sh, 0, sizeof(SPRITE_SHIP));
sh->item = -1;
sh->next = free_enemy;
free_enemy = sh;
return next;
}
/*-------------------------------------------------------------------------*
ENEMY_Add () - Adds Enemy to attack player
*-------------------------------------------------------------------------*/
void
ENEMY_Add(
CSPRITE *sprite
)
{
SPRITE *curlib;
SPRITE_SHIP *newe;
char *pic;
GFX_PIC *h;
curlib = &slib[LE_LONG(sprite->game)][LE_LONG(sprite->slib)];
newe = ENEMY_Get();
pic = GLB_GetItem(LE_LONG(curlib->item));
h = (GFX_PIC*)pic;
newe->item = LE_LONG(curlib->item);
newe->width = LE_LONG(h->width);
newe->height = LE_LONG(h->height);
newe->hlx = LE_LONG(h->width) >> 1;
newe->hly = LE_LONG(h->height) >> 1;
newe->kami = KAMI_FLY;
newe->hits = LE_LONG(curlib->hits);
newe->lib = &slib[LE_LONG(sprite->game)][LE_LONG(sprite->slib)];
newe->y = tileyoff - (tiley - LE_LONG(sprite->y)) * 32 - 97;
newe->x = LE_LONG(sprite->x) * 32 + MAP_LEFT;
newe->edir = E_FORWARD;
newe->x += 16;
newe->y += 16;
newe->x -= newe->hlx;
newe->y -= newe->hly;
newe->x2 = newe->x + newe->width;
newe->y2 = newe->y + newe->height;
newe->move.x = newe->sx = newe->x;
newe->move.y = newe->sy = newe->y;
newe->frame_rate = LE_LONG(curlib->frame_rate);
newe->speed = LE_LONG(curlib->movespeed);
newe->countdown = LE_LONG(curlib->countdown) - newe->move.y;
newe->shoot_disable = 0;
newe->shoot_on = 0;
newe->shootagain = NORM_SHOOT;
newe->shootcount = LE_LONG(curlib->shootcnt);
newe->shootflag = LE_LONG(curlib->shootstart);
if (LE_LONG(curlib->bossflag) && curplr_diff <= DIFF_1)
{
newe->hits -= newe->hits >> 1;
newe->shootcount -= newe->shootcount >> 2;
}
switch (LE_LONG(curlib->animtype))
{
default:
EXIT_Error("ENEMY_Add() - Invalid ANIMTYPE");
break;
case GANIM_NORM:
newe->anim_on = 1;
newe->num_frames = LE_LONG(curlib->num_frames);
break;
case GANIM_SHOOT:
newe->anim_on = 0;
newe->num_frames = LE_LONG(curlib->num_frames);
break;
case GANIM_MULTI:
newe->anim_on = 1;
newe->num_frames = LE_LONG(curlib->rewind);
break;
}
switch (LE_LONG(curlib->flighttype))
{
case F_REPEAT:
case F_LINEAR:
case F_KAMI:
newe->groundflag = 0;
newe->sy = 100 - newe->hly;
newe->move.x2 = newe->sx + LE_SHORT(curlib->flightx[0]);
newe->move.y2 = newe->sy + LE_SHORT(curlib->flighty[0]);
newe->movepos = 1;
InitMobj(&newe->move);
MoveMobj(&newe->move);
break;
case F_GROUND:
newe->groundflag = 1;
newe->move.x2 = newe->x;
newe->move.y2 = 211;
break;
case F_GROUNDRIGHT:
newe->x -= newe->width;
newe->move.x = newe->sx = newe->x;
newe->groundflag = 1;
newe->move.x2 = 335;
newe->move.y2 = 211;
break;
case F_GROUNDLEFT:
newe->x += newe->width;
newe->move.x = newe->sx = newe->x;
newe->groundflag = 1;
newe->move.x2 = -newe->hlx;
newe->move.y2 = 211;
break;
}
newe->suckagain = LE_LONG(curlib->hits) >> 4;
if (LE_LONG(curlib->song) != -1)
boss_sound = 1;
}
/***************************************************************************
ENEMY_GetRandom () - Returns a random ship thats visable
***************************************************************************/
SPRITE_SHIP
*ENEMY_GetRandom(
void
)
{
int pos;
if (!cur_visable)
return NULL;
pos = wrand() % cur_visable;
return onscreen[pos];
}
/***************************************************************************
ENEMY_GetRandomAir () - Returns a random ship thats visable
***************************************************************************/
SPRITE_SHIP
*ENEMY_GetRandomAir(
void
)
{
int pos;
int loop;
if (!cur_visable)
return NULL;
pos = 0;
for (loop = 0; loop < cur_visable; loop++)
{
if (onscreen[loop]->groundflag)
continue;
rscreen[pos] = onscreen[loop];
pos++;
}
if (pos > 0)
{
pos = wrand() % pos;
return rscreen[pos];
}
return NULL;
}
/***************************************************************************
ENEMY_DamageAll () - Tests to see if hit occured at x/y and applys damage
***************************************************************************/
int
ENEMY_DamageAll(
int x, // INPUT : x position
int y, // INPUT : y position
int damage // INPUT : damage
)
{
int loop;
SPRITE_SHIP *cur;
for (loop = 0; loop < cur_visable; loop++)
{
cur = onscreen[loop];
if (x > cur->x && x < cur->x2 && y > cur->y && y < cur->y2)
{
cur->hits -= damage;
return 1;
}
}
return 0;
}
/***************************************************************************
ENEMY_DamageGround () - Tests to see if hit occured at x/y and applys damage
***************************************************************************/
int
ENEMY_DamageGround(
int x, // INPUT : x position
int y, // INPUT : y position
int damage // INPUT : damage
)
{
int loop;
SPRITE_SHIP *cur;
for (loop = 0; loop < cur_visable; loop++)
{
cur = onscreen[loop];
if (!cur->groundflag)
continue;
if (x > cur->x && x < cur->x2 && y > cur->y && y < cur->y2)
{
cur->hits -= damage;
if (curplr_diff == DIFF_0)
cur->hits -= damage;
return 1;
}
}
return 0;
}
/***************************************************************************
ENEMY_DamageAir () - Tests to see if hit occured at x/y and applys damage
***************************************************************************/
int
ENEMY_DamageAir(
int x, // INPUT : x position
int y, // INPUT : y position
int damage // INPUT : damage
)
{
int loop;
SPRITE_SHIP *cur;
for (loop = 0; loop < cur_visable; loop++)
{
cur = onscreen[loop];
if (cur->groundflag)
continue;
if (x > cur->x && x < cur->x2 && y > cur->y && y < cur->y2)
{
cur->hits -= damage;
if (curplr_diff == DIFF_0)
cur->hits -= damage;
return 1;
}
}
return 0;
}
/***************************************************************************
ENEMY_DamageEnergy () - Tests to see if hit occured at x/y and applys damage
***************************************************************************/
SPRITE_SHIP
*ENEMY_DamageEnergy(
int x, // INPUT : x position
int y, // INPUT : y position
int damage // INPUT : damage
)
{
int loop;
SPRITE_SHIP *cur;
for (loop = 0; loop < cur_visable; loop++)
{
cur = onscreen[loop];
if (cur->groundflag)
continue;
if (x > cur->x&& x < cur->x2 && y > cur->y && y < cur->y2)
{
cur->hits--;
if (LE_LONG(cur->lib->suck))
{
if (cur->suckagain > 0)
{
cur->suckagain -= damage;
}
else
{
cur->shoot_on = 0;
cur->shoot_disable = 1;
cur->shootagain = NORM_SHOOT;
SND_3DPatch(FX_EGRAB, cur->x + cur->hlx, cur->y + cur->hlx);
}
}
return cur;
}
}
return NULL;
}
/***************************************************************************
ENEMY_Think() - Does all thinking for enemy ships ( ground/air )
***************************************************************************/
void ENEMY_Think(
void
)
{
CSPRITE *old_enemy;
SPRITE_SHIP *sprite;
SPRITE *curlib;
int speed, loop, x, y, suben, area;
if (boss_sound)
{
if (!SND_IsPatchPlaying(FX_BOSS1))
SND_Patch(FX_BOSS1, 127);
}
tiley = tilepos / MAP_COLS - 3;
while (!end_waveflag && LE_LONG(cur_enemy->y) == tiley)
{
do
{
old_enemy = cur_enemy;
if (cur_enemy->level != EB_NOT_USED)
ENEMY_Add(cur_enemy);
if (cur_enemy == end_enemy)
{
end_waveflag = 1;
break;
}
cur_enemy++;
} while (LE_LONG(old_enemy->link) != -1 && LE_LONG(old_enemy->link) != 1);
}
cur_visable = 0;
for (sprite = first_enemy.next; &last_enemy != sprite; sprite = sprite->next)
{
curlib = sprite->lib;
sprite->px = sprite->x;
sprite->py = sprite->y;
sprite->eframe ^= 1;
if (LE_LONG(curlib->num_frames) > 1)
{
sprite->item = LE_LONG(curlib->item) + sprite->curframe;
if (sprite->frame_rate < 1)
{
sprite->frame_rate = LE_LONG(curlib->frame_rate);
if (sprite->anim_on)
{
sprite->curframe++;
if ((unsigned int)sprite->curframe >= (unsigned int)sprite->num_frames)
{
sprite->curframe -= LE_LONG(curlib->rewind);
switch (LE_LONG(curlib->animtype))
{
default:
EXIT_Error("ENEMY_Think() - Invalid ANIMTYPE1");
case GANIM_NORM:
break;
case GANIM_SHOOT:
sprite->anim_on = 0;
sprite->shoot_on = 1;
break;
case GANIM_MULTI:
switch (sprite->multi)
{
case MULTI_START:
sprite->num_frames = LE_LONG(curlib->num_frames);
sprite->multi = 2;
break;
case MULTI_END:
sprite->shoot_on = 1;
break;
}
break;
}
}
}
}
else
sprite->frame_rate--;
if (sprite->countdown < 1)
{
switch (LE_LONG(curlib->animtype))
{
default:
EXIT_Error("ENEMY_Think() - Invalid ANIMTYPE2");
case GANIM_NORM:
sprite->shoot_on = 1;
break;
case GANIM_SHOOT:
sprite->anim_on = 1;
break;
case GANIM_MULTI:
if (!sprite->multi)
sprite->multi = MULTI_START;
break;
}
}
else
sprite->countdown -= LE_LONG(curlib->movespeed);
}
else
{
if (sprite->countdown < 1)
{
sprite->countdown = -1;
sprite->shoot_on = 1;
}
else
sprite->countdown -= LE_LONG(curlib->movespeed);
}
switch (LE_LONG(curlib->flighttype))
{
case F_REPEAT:
sprite->x = sprite->move.x;
sprite->y = sprite->move.y;
sprite->x2 = sprite->x + sprite->width - 1;
sprite->y2 = sprite->y + sprite->height - 1;
speed = LE_LONG(curlib->movespeed);
speed = MoveEobj(&sprite->move, speed);
if (sprite->move.done)
{
sprite->move.x = sprite->move.x2;
sprite->move.y = sprite->move.y2;
sprite->move.x2 = sprite->sx + LE_SHORT(curlib->flightx[sprite->movepos]);
sprite->move.y2 = sprite->sy + LE_SHORT(curlib->flighty[sprite->movepos]);
InitMobj(&sprite->move);
MoveMobj(&sprite->move);
speed = MoveEobj(&sprite->move, speed);
if (!sprite->edir)
{
sprite->movepos++;
if (sprite->movepos >= LE_LONG(curlib->numflight))
{
sprite->edir = E_BACKWARD;
sprite->movepos = LE_LONG(curlib->numflight) - 1;
}
}
else
{
sprite->movepos--;
if (sprite->movepos <= LE_LONG(curlib->repos))
{
sprite->edir = E_FORWARD;
sprite->movepos = LE_LONG(curlib->repos);
}
}
}
break;
case F_KAMI:
sprite->x = sprite->move.x;
sprite->y = sprite->move.y;
sprite->x2 = sprite->x + sprite->width - 1;
sprite->y2 = sprite->y + sprite->height - 1;
speed = LE_LONG(curlib->movespeed);
speed = MoveEobj(&sprite->move, speed);
if (sprite->kami == KAMI_END)
{
if (sprite->move.y > 0xc9)
sprite->doneflag = 1;
if (sprite->hlx + 320 < sprite->move.x)
sprite->doneflag = 1;
if (sprite->move.y + sprite->width < 0)
sprite->doneflag = 1;
if (sprite->move.x + sprite->width < 0)
sprite->doneflag = 1;
}
if (sprite->move.done && sprite->kami != KAMI_END)
{
sprite->move.x = sprite->move.x2;
sprite->move.y = sprite->move.y2;
sprite->x2 = sprite->x + sprite->width - 1;
sprite->y2 = sprite->y + sprite->height - 1;
if (sprite->kami == KAMI_CHASE)
{
sprite->move.x2 = player_cx;
sprite->move.y2 = player_cy;
sprite->kami = KAMI_END;
}
else
{
sprite->move.x2 = sprite->sx + LE_SHORT(curlib->flightx[sprite->movepos]);
sprite->move.y2 = sprite->sy + LE_SHORT(curlib->flighty[sprite->movepos]);
}
InitMobj(&sprite->move);
MoveMobj(&sprite->move);
speed = MoveEobj(&sprite->move, speed);
if (LE_LONG(curlib->numflight) - 1 > sprite->movepos)
sprite->movepos++;
else if (sprite->kami == KAMI_FLY)
sprite->kami = KAMI_CHASE;
}
break;
case F_LINEAR:
sprite->x = sprite->move.x;
sprite->y = sprite->move.y;
sprite->x2 = sprite->x + sprite->width - 1;
sprite->y2 = sprite->y + sprite->height - 1;
speed = LE_LONG(curlib->movespeed);
speed = MoveEobj(&sprite->move, speed);
if (sprite->move.done)
{
sprite->move.x = sprite->move.x2;
sprite->move.y = sprite->move.y2;
sprite->move.x2 = sprite->sx + LE_SHORT(curlib->flightx[sprite->movepos]);
sprite->move.y2 = sprite->sy + LE_SHORT(curlib->flighty[sprite->movepos]);
InitMobj(&sprite->move);
MoveMobj(&sprite->move);
speed = MoveEobj(&sprite->move, speed);
sprite->movepos++;
if (sprite->movepos > LE_LONG(curlib->numflight))
sprite->doneflag = 1;
}
break;
case F_GROUND:
if (scroll_flag)
sprite->y++;
if (sprite->y > sprite->move.y2)
sprite->doneflag = 1;
sprite->x2 = sprite->x + sprite->width - 1;
sprite->y2 = sprite->y + sprite->height - 1;
break;
case F_GROUNDRIGHT:
if (scroll_flag)
sprite->y++;
if (sprite->y >= 0)
{
sprite->x += LE_LONG(curlib->movespeed);
if (sprite->x > sprite->move.x2)
sprite->doneflag = 1;
else if (sprite->y > sprite->move.y2)
sprite->doneflag = 1;
}
sprite->x2 = sprite->x + sprite->width - 1;
sprite->y2 = sprite->y + sprite->height - 1;
break;
case F_GROUNDLEFT:
if (scroll_flag)
sprite->y++;
if (sprite->y >= 0)
{
sprite->x -= LE_LONG(curlib->movespeed);
if (sprite->x < sprite->move.x2)
sprite->doneflag = 1;
else if (sprite->y > sprite->move.y2)
sprite->doneflag = 1;
}
sprite->x2 = sprite->x + sprite->width - 1;
sprite->y2 = sprite->y + sprite->height - 1;
break;
}
if (sprite->shoot_on)
{
switch (sprite->shootagain)
{
case NORM_SHOOT:
sprite->shootflag--;
if (sprite->shootflag < 0)
{
sprite->shootflag = LE_LONG(curlib->shootspace);
if (sprite->shoot_disable == 0)
{
for (loop = 0; loop < LE_LONG(curlib->numguns); loop++)
{
ESHOT_Shoot(sprite, loop);
}
}
sprite->shootcount--;
if (sprite->shootcount < 1)
sprite->shootagain = LE_LONG(curlib->shootframe);
}
break;
case START_SHOOT:
sprite->shootagain = NORM_SHOOT;
sprite->shootcount = LE_LONG(curlib->shootcnt);
sprite->shootflag = LE_LONG(curlib->shootspace);
break;
default:
sprite->shootagain--;
break;
}
}
if (sprite->doneflag)
{
sprite = ENEMY_Remove(sprite);
continue;
}
if (LE_LONG(curlib->shadow))
{
if (sprite->groundflag)
{
SHADOW_GAdd(sprite->item, sprite->x, sprite->y);
}
else
{
SHADOW_Add(sprite->item, sprite->x, sprite->y);
}
}
if (LE_LONG(curlib->bossflag))
{
numboss++;
if (sprite->hits < 50 && (gl_cnt & 2) != 0)
{
x = sprite->x + (wrand() % sprite->width);
y = sprite->y + (wrand() % sprite->height);
ANIMS_StartAnim(A_SMALL_AIR_EXPLO, x, y);
}
}
if (!sprite->groundflag)
{
if (player_cx > sprite->x && player_cx < sprite->x2)
{
if (player_cy > sprite->y && player_cy < sprite->y2)
{
if ((haptic) && (control == 2))
{
IPT_CalJoyRumbleMedium(); //Rumble when enemy hit
}
sprite->hits -= (PLAYERWIDTH / 2);
if (sprite->width > sprite->height)
suben = sprite->width;
else
suben = sprite->height;
OBJS_SubEnergy(suben >> 2);
x = player_cx + (wrand() % 8) - 4;
y = player_cy + (wrand() % 8) - 4;
ANIMS_StartAnim(A_SMALL_AIR_EXPLO, x, y);
SND_Patch(FX_CRASH, 127);
}
}
}
if (sprite->hits <= 0)
{
plr.score += LE_LONG(curlib->money);
SND_3DPatch(FX_AIREXPLO, sprite->x + sprite->hlx, sprite->x + sprite->hlx);
switch (LE_LONG(curlib->exptype))
{
case EXP_ENERGY:
ANIMS_StartAnim(A_ENERGY_AIR_EXPLO, sprite->x + sprite->hlx, sprite->y + sprite->hly);
BONUS_Add(S_ITEMBUY6, sprite->x, sprite->y);
break;
case EXP_AIRSMALL1:
ANIMS_StartAnim(A_MED_AIR_EXPLO, sprite->x + sprite->hlx, sprite->y + sprite->hly);
break;
case EXP_AIRSMALL2:
ANIMS_StartAnim(A_MED_AIR_EXPLO2, sprite->x + sprite->hlx, sprite->y + sprite->hly);
break;
case EXP_AIRMED:
ANIMS_StartAnim(A_LARGE_AIR_EXPLO, sprite->x + sprite->hlx, sprite->y + sprite->hly);
break;
case EXP_AIRLARGE:
ANIMS_StartAnim(A_LARGE_AIR_EXPLO, sprite->x + sprite->hlx, sprite->y + sprite->hly);
area = (sprite->width>>4) * (sprite->height>>4);
for (loop = 0; loop < area; loop++)
{
x = sprite->x + (wrand() % sprite->width);
y = sprite->y + (wrand() % sprite->height);
if (loop & 1)
ANIMS_StartAnim(A_MED_AIR_EXPLO, x, y);
else
ANIMS_StartAAnim(A_MED_AIR_EXPLO2, x, y);
}
break;
case EXP_GRDSMALL:
ANIMS_StartAnim(A_SMALL_GROUND_EXPLO, sprite->x + sprite->hlx, sprite->y + sprite->hly);
break;
case EXP_GRDMED:
x = sprite->x + (wrand() % sprite->width);
y = sprite->y + (wrand() % sprite->height);
if ((wrand() % 2) == 0)
ANIMS_StartAnim(A_GROUND_SPARKLE, x, y);
else
ANIMS_StartAnim(A_GROUND_FLARE, x, y);
ANIMS_StartAnim(A_LARGE_GROUND_EXPLO1, sprite->x + sprite->hlx, sprite->y + sprite->hly);
break;
case EXP_GRDLARGE:
ANIMS_StartAnim(A_LARGE_GROUND_EXPLO1, sprite->x + sprite->hlx, sprite->y + sprite->hly);
area = (sprite->width>>4) * (sprite->height>>4);
for (loop = 0; loop < area; loop++)
{
x = sprite->x + (wrand() % sprite->width);
y = sprite->y + (wrand() % sprite->height);
if ((wrand()%2) == 0)
ANIMS_StartAnim(A_GROUND_FLARE, x, y);
else
ANIMS_StartAnim(A_GROUND_SPARKLE, x, y);
ANIMS_StartAnim(A_SMALL_GROUND_EXPLO, x, y);
}
break;
case EXP_BOSS:
ANIMS_StartAnim(A_LARGE_AIR_EXPLO, sprite->x + sprite->hlx, sprite->y + sprite->hly);
area = (sprite->width>>4) * (sprite->height>>4);
for (loop = 0; loop < area; loop++)
{
x = sprite->x + (wrand() % sprite->width);
y = sprite->y + (wrand() % sprite->height);
ANIMS_StartAAnim(A_GROUND_FLARE, x, y);
if (loop & 1)
ANIMS_StartAnim(A_LARGE_AIR_EXPLO, x, y);
else
ANIMS_StartAnim(A_MED_AIR_EXPLO2, x, y);
}
break;
case EXP_PERSON:
ANIMS_StartAnim(A_PERSON, sprite->x + sprite->hlx, sprite->y + sprite->hly);
break;
case EXP_PLATOON:
ANIMS_StartAnim(A_PLATOON, sprite->x + sprite->hlx, sprite->y + sprite->hly);
break;
}
if (LE_LONG(curlib->bonus) != -1)
BONUS_Add(LE_LONG(curlib->bonus), sprite->x, sprite->y);
sprite = ENEMY_Remove(sprite);
continue;
}
y = sprite->y + sprite->height;
if (y > 0 && sprite->y < 200)
{
x = sprite->x + sprite->width;
if (x > 0 && sprite->x < 320)
{
onscreen[cur_visable] = sprite;
cur_visable++;
}
}
}
}
/***************************************************************************
ENEMY_DisplayGround () - Displays Ground ENEMY pics
***************************************************************************/
void
ENEMY_DisplayGround(
void
)
{
SPRITE_SHIP *spt;
int ix, iy;
for (spt = first_enemy.next; &last_enemy != spt; spt = spt->next)
{
if (!spt->groundflag)
continue;
ix = GFX_InterpCoord(spt->x, spt->px);
iy = GFX_InterpCoord(spt->y, spt->py);
GFX_PutSprite((char*)GLB_GetItem(spt->item), ix, iy);
}
}
/***************************************************************************
ENEMY_DisplaySky () - Displays AIR ENEMY SHIPS
***************************************************************************/
void
ENEMY_DisplaySky(
void
)
{
int i;
int ix, iy;
SPRITE_SHIP *spt;
for (spt = first_enemy.next; &last_enemy != spt; spt = spt->next)
{
if (spt->groundflag)
continue;
ix = GFX_InterpCoord(spt->x, spt->px);
iy = GFX_InterpCoord(spt->y, spt->py);
GFX_PutSprite((char*)GLB_GetItem(spt->item), ix, iy);
for (i = 0; i < LE_LONG(spt->lib->numengs); i++)
{
FLAME_Up(ix + LE_SHORT(spt->lib->engx[i]), iy + LE_SHORT(spt->lib->engy[i]), LE_SHORT(spt->lib->englx[i]), spt->eframe);
}
}
}
/***************************************************************************
ENEMY_GetBaseDamage() - Gets Base Ship damage
***************************************************************************/
int
ENEMY_GetBaseDamage(
void
)
{
static int nums;
int total, damage;
SPRITE_SHIP *spt;
total = 0;
nums = 0;
for (spt = first_enemy.next; &last_enemy != spt; spt = spt->next)
{
if (!LE_LONG(spt->lib->bossflag))
continue;
if (spt->y + spt->hly >= 0)
{
damage = (spt->hits * 100) / spt->lib->hits;
total += damage;
nums++;
}
}
if (nums)
total /= nums;
return total;
}