/* Copyright (C) 2003 Parallel Realities Copyright (C) 2011, 2012 Guus Sliepen Copyright (C) 2015-2020 The Diligent Circle 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 . */ #include #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! 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 !(x1x, 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; }