starfighter/src/collectable.c

371 lines
8.8 KiB
C

/*
Copyright (C) 2003 Parallel Realities
Copyright (C) 2011, 2012 Guus Sliepen
Copyright (C) 2015-2020 Layla Marchant <diligentcircle@riseup.net>
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 3
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, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "defs.h"
#include "structs.h"
#include "audio.h"
#include "collectable.h"
#include "engine.h"
#include "explosion.h"
#include "game.h"
#include "gfx.h"
#include "player.h"
#include "weapons.h"
/*
Create a new collectable item based on supplied arguments.
*/
void collectable_add(float x, float y, int type, int value, int life)
{
int r;
Collectable *collectable;
int plasma_useless, shield_useless, rockets_useless;
plasma_useless = (((weapons[W_PLAYER_WEAPON].reload[0] >= rate2reload[game.minPlasmaRate])
&& (weapons[W_PLAYER_WEAPON].ammo[0] <= game.minPlasmaOutput)
&& (weapons[W_PLAYER_WEAPON].damage <= game.minPlasmaDamage))
|| (player.ammo[0] >= game.maxPlasmaAmmo));
shield_useless = ((game.difficulty == DIFFICULTY_NIGHTMARE)
|| (player.shield >= player.maxShield));
rockets_useless = ((player.weaponType[1] == W_CHARGER)
|| (player.weaponType[1] == W_LASER)
|| (game.maxRocketAmmo <= 0)
|| (player.ammo[1] >= game.maxRocketAmmo));
if (type == P_ANYTHING)
{
type = P_CASH;
r = rand() % 9;
switch (r)
{
case 0:
if ((game.difficulty == DIFFICULTY_ORIGINAL)
|| (game.difficulty == DIFFICULTY_NIGHTMARE))
{
type = P_PLASMA_AMMO;
}
else
{
if ((!shield_useless)
&& (CHANCE(2 * (player.maxShield - player.shield) / player.maxShield)))
{
type = P_SHIELD;
}
else if ((!rockets_useless)
&& (game.maxRocketAmmo > 0)
&& (CHANCE((game.maxRocketAmmo - player.ammo[1]) / game.maxRocketAmmo)))
{
type = P_ROCKET;
}
else
{
type = P_PLASMA_AMMO;
}
}
break;
case 1:
type = P_SHIELD;
break;
case 2:
if ((game.difficulty == DIFFICULTY_ORIGINAL)
|| (game.difficulty == DIFFICULTY_NIGHTMARE))
{
type = P_ROCKET;
}
else
{
if ((!shield_useless)
&& (CHANCE(2 * (player.maxShield - player.shield) / player.maxShield)))
{
type = P_SHIELD;
}
else if ((!plasma_useless)
&& (game.maxPlasmaAmmo > 0)
&& (CHANCE((game.maxPlasmaAmmo - player.ammo[0]) / game.maxPlasmaAmmo)))
{
type = P_PLASMA_AMMO;
}
else
{
type = P_ROCKET;
}
}
break;
}
}
else if (type == P_WEAPONS)
{
type = P_PLASMA_RATE;
if ((game.difficulty == DIFFICULTY_NIGHTMARE)
|| ((game.difficulty != DIFFICULTY_SUPEREASY)
&& (game.difficulty != DIFFICULTY_EASY)
&& (game.difficulty != DIFFICULTY_ORIGINAL)
&& ((game.area == MISN_MOEBO)
|| (game.area == MISN_ELAMALE)
|| (game.area == MISN_ELLESH)
|| (game.area == MISN_EARTH))))
{
// Deny the Super Charge in Nightmare difficulty, and on bosses.
r = rand() % 59;
}
else
r = rand() % 61;
if (r <= 19)
type = P_PLASMA_DAMAGE;
else if (r <= 39)
type = P_PLASMA_SHOT;
else if (r <= 59)
type = P_PLASMA_RATE;
else
type = P_SUPER;
}
if (type == P_SUPER)
value = MAX(value, 1);
if (value == 0)
return; // don't bother!
if ((game.difficulty == DIFFICULTY_SUPEREASY)
&& (type != P_CARGO))
value *= 2;
// No point in giving the player plasma ammo if the weapons aren't
// upgraded! Give them money instead. (Except in Classic difficulty.)
if ((type == P_PLASMA_AMMO) && (game.difficulty != DIFFICULTY_ORIGINAL))
{
if (plasma_useless)
{
type = P_CASH;
}
}
// If the player has a charge cannon or a laser cannon, don't give them
// rockets. Causes problems otherwise :)
if (type == P_ROCKET)
{
if ((player.weaponType[1] == W_CHARGER)
|| (player.weaponType[1] == W_LASER)
|| (value < 10))
{
type = P_CASH;
}
else
{
value /= 10;
}
}
// Shield bonus is useless if you can't heal; give cash instead.
if ((type == P_SHIELD) && (game.difficulty != DIFFICULTY_ORIGINAL))
{
if ((game.difficulty == DIFFICULTY_NIGHTMARE)
|| (player.shield >= player.maxShield))
{
type = P_CASH;
}
}
if (game.difficulty == DIFFICULTY_ORIGINAL)
{
// Cash is just rare in the original game. You can still grind,
// just not as much.
if ((game.area == MISN_INTERCEPTION) && (type == P_CASH))
{
if (rand() % 10 > 0)
return;
}
}
else if (game.difficulty != DIFFICULTY_SUPEREASY)
{
// No cash or ammo on interceptions. Completely stops grinding.
if ((game.area == MISN_INTERCEPTION)
&& ((type == P_CASH) || (type == P_PLASMA_AMMO)
|| (type == P_ROCKET)))
{
return;
}
}
collectable = malloc(sizeof(*collectable));
if (collectable == NULL)
{
engine_warn("Failed to allocate memory for collectable");
return;
}
collectable->next = NULL;
collectable->active = 1;
collectable->x = x;
collectable->y = y;
collectable->dx = RANDRANGE(-100, 100) / 100.;
collectable->dy = RANDRANGE(-100, 100) / 100.;
if ((game.difficulty != DIFFICULTY_ORIGINAL) && (type != P_MINE))
{
collectable->dx /= 2;
collectable->dy /= 2;
}
collectable->type = type;
collectable->value = value;
collectable->life = life;
switch(type)
{
case P_CASH:
collectable->image = gfx_sprites[SP_PICKUP_MONEY];
break;
case P_ROCKET:
collectable->image = gfx_sprites[SP_PICKUP_ROCKETS];
break;
case P_PLASMA_AMMO:
collectable->image = gfx_sprites[SP_PICKUP_PLASMA];
break;
case P_SHIELD:
collectable->image = gfx_sprites[SP_PICKUP_SHIELD];
break;
case P_PLASMA_SHOT:
collectable->image = gfx_sprites[SP_PICKUP_PLASMA_OUTPUT];
break;
case P_PLASMA_RATE:
collectable->image = gfx_sprites[SP_PICKUP_PLASMA_RATE];
break;
case P_PLASMA_DAMAGE:
collectable->image = gfx_sprites[SP_PICKUP_PLASMA_POWER];
break;
case P_CARGO:
collectable->image = gfx_sprites[SP_CARGO];
break;
case P_SUPER:
collectable->image = gfx_sprites[SP_SUPERCHARGE];
break;
case P_MINE:
collectable->image = gfx_sprites[SP_MINE];
break;
case P_SLAVES:
case P_ESCAPEPOD:
collectable->image = gfx_sprites[SP_ESCAPE_POD];
break;
case P_ORE:
collectable->image = gfx_sprites[RANDRANGE(SP_ORE, SP_ORE_L)];
break;
}
engine.collectableTail->next = collectable;
engine.collectableTail = collectable;
}
int collectable_collision(Collectable *collectable, Object *ship)
{
float x0 = collectable->x;
float y0 = collectable->y;
float w0 = collectable->image->w;
float h0 = collectable->image->h;
float x2 = ship->x;
float y2 = ship->y;
float w1 = ship->image[0]->w;
float h1 = ship->image[0]->h;
float x1 = x0 + w0;
float y1 = y0 + h0;
float x3 = x2 + w1;
float y3 = y2 + h1;
return !(x1<x2 || x3<x0 || y1<y2 || y3<y0);
}
void collectable_explode(Collectable *collectable)
{
audio_playSound(SFX_EXPLOSION, collectable->x, collectable->y);
for (int i = 0 ; i < 10 ; i++)
explosion_add(RANDRANGE(collectable->x - 25, collectable->x + 25),
RANDRANGE(collectable->y - 25, collectable->y + 25), SP_BIG_EXPLOSION);
player_checkShockDamage(collectable->x, collectable->y);
}
/*
Returns the number of "good" collectables (all collectables excluding
mines and unneeded refills) that exist.
*/
int collectable_numGood()
{
Collectable *col = engine.collectableHead->next;
int n = 0;
while (col != NULL)
{
if ((col->type != P_MINE) && (col->type != P_ORE)
&& ((col->type != P_SHIELD)
|| (player.shield < player.maxShield))
&& ((col->type != P_ROCKET)
|| (player.ammo[1] < game.maxRocketAmmo))
&& ((col->type != P_PLASMA_AMMO)
|| (player.ammo[0] < game.maxPlasmaAmmo))
&& ((col->type != P_PLASMA_SHOT)
|| (player.ammo[0] < game.maxPlasmaAmmo)
|| (weapons[W_PLAYER_WEAPON].ammo[0] < game.maxPlasmaOutput))
&& ((col->type != P_PLASMA_DAMAGE)
|| (player.ammo[0] < game.maxPlasmaAmmo)
|| (weapons[W_PLAYER_WEAPON].damage < game.maxPlasmaDamage))
&& ((col->type != P_PLASMA_RATE)
|| (player.ammo[0] < game.maxPlasmaAmmo)
|| (weapons[W_PLAYER_WEAPON].reload[0] > rate2reload[game.maxPlasmaRate]))
&& ((col->type != P_SUPER)
|| (player.ammo[0] < game.maxPlasmaAmmo)
|| (weapons[W_PLAYER_WEAPON].ammo[0] < game.maxPlasmaOutput)
|| (weapons[W_PLAYER_WEAPON].damage < game.maxPlasmaDamage)
|| (weapons[W_PLAYER_WEAPON].reload[0] > rate2reload[game.maxPlasmaRate])))
{
n++;
}
col = col->next;
}
return n;
}