318 lines
7.3 KiB
C
318 lines
7.3 KiB
C
/*
|
|
Copyright (C) 2003 Parallel Realities
|
|
Copyright (C) 2011, 2012 Guus Sliepen
|
|
Copyright (C) 2015-2019 Julie Marchant <onpon4@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_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!
|
|
|
|
// 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))
|
|
{
|
|
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
|
|
{
|
|
// 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);
|
|
if (collectable->dx != 0)
|
|
collectable->dx /= 100;
|
|
|
|
collectable->dy = RANDRANGE(-100, 100);
|
|
if (collectable->dy != 0)
|
|
collectable->dy /= 100;
|
|
|
|
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);
|
|
}
|