1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-16 01:54:38 +02:00

#11 ammo counter

This commit is contained in:
XProger
2017-07-01 03:56:32 +03:00
parent 31a3bba123
commit 8776e04935
5 changed files with 173 additions and 85 deletions

View File

@@ -12,6 +12,8 @@
#define MAX_LAYERS 4
#define UNLIMITED_AMMO 10000
struct Controller;
struct IGame {
@@ -33,6 +35,7 @@ struct IGame {
virtual bool invUse(TR::Entity::Type item, TR::Entity::Type slot) { return false; }
virtual void invAdd(TR::Entity::Type type, int count = 1) {}
virtual int* invCount(TR::Entity::Type type) { return NULL; }
virtual Sound::Sample* playSound(int id, const vec3 &pos, int flags, int group = -1) const { return NULL; }
};

View File

@@ -93,10 +93,10 @@
E( INV_PASSPORT_CLOSED ) \
E( INV_MAP ) \
E( CRYSTAL ) \
E( WEAPON_PISTOLS ) \
E( WEAPON_SHOTGUN ) \
E( WEAPON_MAGNUMS ) \
E( WEAPON_UZIS ) \
E( PISTOLS ) \
E( SHOTGUN ) \
E( MAGNUMS ) \
E( UZIS ) \
E( AMMO_PISTOLS ) \
E( AMMO_SHOTGUN ) \
E( AMMO_MAGNUMS ) \
@@ -631,7 +631,7 @@ namespace TR {
}
int isItem() {
return (type >= WEAPON_PISTOLS && type <= AMMO_UZIS) ||
return (type >= PISTOLS && type <= AMMO_UZIS) ||
(type >= PUZZLE_1 && type <= PUZZLE_4) ||
(type >= KEY_1 && type <= KEY_4) ||
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG || type == SCION_1); // TODO: recheck all items

View File

@@ -129,7 +129,7 @@ struct Inventory {
if (anim) anim->setAnim(0, 0, false);
}
} items[INVENTORY_MAX_ITEMS];
} *items[INVENTORY_MAX_ITEMS];
Inventory(IGame *game) : game(game), active(false), chosen(false), index(0), targetIndex(0), page(PAGE_OPTION), targetPage(PAGE_OPTION), itemsCount(0) {
TR::Level *level = game->getLevel();
@@ -145,16 +145,16 @@ struct Inventory {
if (level->extra.inv.gamma != -1)
add(TR::Entity::INV_GAMMA);
add(TR::Entity::INV_PISTOLS, 999);
add(TR::Entity::INV_PISTOLS, UNLIMITED_AMMO);
add(TR::Entity::INV_SHOTGUN, 999);
add(TR::Entity::INV_MAGNUMS, 999);
add(TR::Entity::INV_UZIS, 999);
add(TR::Entity::INV_MEDIKIT_SMALL, 999);
add(TR::Entity::INV_MEDIKIT_BIG, 999);
add(TR::Entity::INV_UZIS, 10);
// add(TR::Entity::INV_MEDIKIT_SMALL, 999);
// add(TR::Entity::INV_MEDIKIT_BIG, 999);
add(TR::Entity::INV_SCION, 1);
add(TR::Entity::INV_KEY_1, 1);
add(TR::Entity::INV_PUZZLE_1, 1);
// add(TR::Entity::INV_SCION, 1);
// add(TR::Entity::INV_KEY_1, 1);
// add(TR::Entity::INV_PUZZLE_1, 1);
phaseRing = phasePage = phaseChoose = phaseSelect = 0.0f;
memset(pageItemIndex, 0, sizeof(pageItemIndex));
@@ -164,6 +164,9 @@ struct Inventory {
}
~Inventory() {
for (int i = 0; i < itemsCount; i++)
delete items[i];
for (int i = 0; i < COUNT(background); i++)
delete background[i];
}
@@ -172,9 +175,41 @@ struct Inventory {
return active || phaseRing > 0.0f;
}
TR::Entity::Type convToInv(TR::Entity::Type type) {
switch (type) {
case TR::Entity::PISTOLS : return TR::Entity::INV_PISTOLS;
case TR::Entity::SHOTGUN : return TR::Entity::INV_SHOTGUN;
case TR::Entity::MAGNUMS : return TR::Entity::INV_MAGNUMS;
case TR::Entity::UZIS : return TR::Entity::INV_UZIS;
case TR::Entity::AMMO_PISTOLS : return TR::Entity::INV_AMMO_PISTOLS;
case TR::Entity::AMMO_SHOTGUN : return TR::Entity::INV_AMMO_SHOTGUN;
case TR::Entity::AMMO_MAGNUMS : return TR::Entity::INV_AMMO_MAGNUMS;
case TR::Entity::AMMO_UZIS : return TR::Entity::INV_AMMO_UZIS;
case TR::Entity::MEDIKIT_SMALL : return TR::Entity::INV_MEDIKIT_SMALL;
case TR::Entity::MEDIKIT_BIG : return TR::Entity::INV_MEDIKIT_BIG;
case TR::Entity::PUZZLE_1 : return TR::Entity::INV_PUZZLE_1;
case TR::Entity::PUZZLE_2 : return TR::Entity::INV_PUZZLE_2;
case TR::Entity::PUZZLE_3 : return TR::Entity::INV_PUZZLE_3;
case TR::Entity::PUZZLE_4 : return TR::Entity::INV_PUZZLE_4;
case TR::Entity::KEY_1 : return TR::Entity::INV_KEY_1;
case TR::Entity::KEY_2 : return TR::Entity::INV_KEY_2;
case TR::Entity::KEY_3 : return TR::Entity::INV_KEY_3;
case TR::Entity::KEY_4 : return TR::Entity::INV_KEY_4;
case TR::Entity::LEADBAR : return TR::Entity::INV_LEADBAR;
//case TR::Entity::SCION : return TR::Entity::INV_SCION;
}
return type;
}
int contains(TR::Entity::Type type) {
type = convToInv(type);
for (int i = 0; i < itemsCount; i++)
if (items[i].type == type)
if (items[i]->type == type)
return i;
return -1;
}
@@ -182,15 +217,17 @@ struct Inventory {
void add(TR::Entity::Type type, int count = 1) {
int i = contains(type);
if (i > -1) {
items[i].count += count;
items[i]->count += count;
return;
}
ASSERT(itemsCount < INVENTORY_MAX_ITEMS);
type = convToInv(type);
int pos = 0;
for (int pos = 0; pos < itemsCount; pos++)
if (items[pos].type > type)
if (items[pos]->type > type)
break;
if (pos - itemsCount) {
@@ -198,36 +235,52 @@ struct Inventory {
items[i] = items[i - 1];
}
Item it(game->getLevel(), type, count);
items[pos] = it;
items[pos] = new Item(game->getLevel(), type, count);
itemsCount++;
}
int getCount(TR::Entity::Type type) {
int* getCountPtr(TR::Entity::Type type) {
int i = contains(type);
if (i < 0) return 0;
return items[i].count;
if (i < 0) return NULL;
return &items[i]->count;
}
void remove(TR::Entity::Type type, int count = 1) {
int i = contains(type);
if (i > -1)
items[i].count -= count;
int idx = contains(type);
if (idx > -1) {
items[idx]->count -= count;
if (!items[idx]->count) {
delete items[idx];
for (int i = idx; i < itemsCount - 1; i++) {
items[i] = items[i + 1];
}
itemsCount--;
}
}
}
bool use(TR::Entity::Type item, TR::Entity::Type slot) {
if (item == TR::Entity::NONE) {
switch (slot) {
case TR::Entity::KEY_HOLE_1 : item = TR::Entity::KEY_1; break; // TODO: 1-4
case TR::Entity::PUZZLE_HOLE_1 : item = TR::Entity::PUZZLE_1; break;
default : return false;
}
switch (slot) {
case TR::Entity::PUZZLE_HOLE_1 : item = TR::Entity::INV_PUZZLE_1; break;
case TR::Entity::PUZZLE_HOLE_2 : item = TR::Entity::INV_PUZZLE_2; break;
case TR::Entity::PUZZLE_HOLE_3 : item = TR::Entity::INV_PUZZLE_3; break;
case TR::Entity::PUZZLE_HOLE_4 : item = TR::Entity::INV_PUZZLE_4; break;
case TR::Entity::KEY_HOLE_1 : item = TR::Entity::INV_KEY_1; break;
case TR::Entity::KEY_HOLE_2 : item = TR::Entity::INV_KEY_2; break;
case TR::Entity::KEY_HOLE_3 : item = TR::Entity::INV_KEY_3; break;
case TR::Entity::KEY_HOLE_4 : item = TR::Entity::INV_KEY_4; break;
case TR::Entity::INV_PISTOLS :
case TR::Entity::INV_SHOTGUN :
case TR::Entity::INV_MAGNUMS :
case TR::Entity::INV_UZIS : return false;
default : return false;
}
if (getCount(item) > 0) {
if (getCountPtr(item)) {
remove(item);
return true;
}
return false;
}
@@ -240,7 +293,7 @@ struct Inventory {
if (active) {
for (int i = 0; i < itemsCount; i++)
items[i].reset();
items[i]->reset();
phasePage = 1.0f;
phaseSelect = 1.0f;
@@ -269,7 +322,7 @@ struct Inventory {
int getItemIndex(Page page, int index) {
for (int i = 0; i < itemsCount; i++)
if (items[i].desc.page == page) {
if (items[i]->desc.page == page) {
if (!index)
return i;
index--;
@@ -285,7 +338,7 @@ struct Inventory {
int count = 0;
for (int i = 0; i < itemsCount; i++)
if (items[i].desc.page == page)
if (items[i]->desc.page == page)
count++;
return count;
@@ -293,7 +346,7 @@ struct Inventory {
bool showHealthBar() {
int idx = getItemIndex(page, index);
TR::Entity::Type type = items[idx].type;
TR::Entity::Type type = items[idx]->type;
return active && phaseRing == 1.0f && index == targetIndex && phasePage == 1.0f && (type == TR::Entity::INV_MEDIKIT_SMALL || type == TR::Entity::INV_MEDIKIT_BIG);
}
@@ -329,15 +382,15 @@ struct Inventory {
vec3 p;
Item &item = items[getItemIndex(page, index)];
Item *item = items[getItemIndex(page, index)];
if (index == targetIndex && ready) {
if (Input::state[cAction] && (phaseChoose == 0.0f || (phaseChoose == 1.0f && item.anim->isEnded))) {
if (Input::state[cAction] && (phaseChoose == 0.0f || (phaseChoose == 1.0f && item->anim->isEnded))) {
chosen = !chosen;
if (!chosen) {
item.angle = 0.0f;
item->angle = 0.0f;
} else {
switch (item.type) {
switch (item->type) {
case TR::Entity::INV_COMPASS : game->playSound(TR::SND_INV_COMPASS, p, 0, 0); break;
case TR::Entity::INV_HOME : game->playSound(TR::SND_INV_HOME, p, 0, 0); break;
case TR::Entity::INV_CONTROLS : game->playSound(TR::SND_INV_CONTROLS, p, 0, 0); break;
@@ -347,7 +400,7 @@ struct Inventory {
case TR::Entity::INV_UZIS : game->playSound(TR::SND_INV_WEAPON, p, 0, 0); break;
default : game->playSound(TR::SND_INV_SHOW, p, 0, 0); break;
}
item.choose();
item->choose();
}
}
}
@@ -357,8 +410,8 @@ struct Inventory {
int itemIndex = index == targetIndex ? getItemIndex(page, index) : -1;
for (int i = 0; i < itemsCount; i++) {
items[i].update();
float &angle = items[i].angle;
items[i]->update();
float &angle = items[i]->angle;
if (itemIndex != i || chosen) {
if (angle == 0.0f) {
@@ -378,8 +431,8 @@ struct Inventory {
angle = clampAngle(angle);
}
if (ready && chosen && phaseChoose == 1.0f && item.anim->isEnded) {
TR::Entity::Type type = item.type;
if (ready && chosen && phaseChoose == 1.0f && item->anim->isEnded) {
TR::Entity::Type type = item->type;
if (type == TR::Entity::INV_PISTOLS || type == TR::Entity::INV_SHOTGUN || type == TR::Entity::INV_MAGNUMS || type == TR::Entity::INV_UZIS ||
type == TR::Entity::INV_MEDIKIT_SMALL || type == TR::Entity::INV_MEDIKIT_BIG) {
@@ -420,26 +473,29 @@ struct Inventory {
Core::setDepthTest(true);
}
void renderItemText(const Item &item, float width) {
UI::textOut(game, vec2(0, 480 - 16), item.desc.name, UI::aCenter, width);
if (item.count > 1) {
char spec;
switch (item.type) {
case TR::Entity::INV_SHOTGUN : spec = 12; break;
case TR::Entity::INV_MAGNUMS : spec = 13; break;
case TR::Entity::INV_UZIS : spec = 14; break;
default : spec = 0;
}
void renderItemCount(const Item *item, const vec2 &pos, float width) {
char spec;
switch (item->type) {
case TR::Entity::INV_SHOTGUN : spec = 12; break;
case TR::Entity::INV_MAGNUMS : spec = 13; break;
case TR::Entity::INV_UZIS : spec = 14; break;
default : spec = 0;
}
if ((item->count > 1 || spec) && item->count < UNLIMITED_AMMO) {
char buf[16];
sprintf(buf, "%d %c", item.count, spec);
sprintf(buf, "%d %c", item->count, spec);
for (int i = 0; buf[i] != ' '; i++)
buf[i] -= 47;
UI::textOut(game, vec2(width / 2 - 160, 480 - 96), buf, UI::aRight, 320);
UI::textOut(game, pos, buf, UI::aRight, width);
}
}
void renderItemText(const Item *item, float width) {
UI::textOut(game, vec2(0, 480 - 16), item->desc.name, UI::aCenter, width);
renderItemCount(item, vec2(width / 2 - 160, 480 - 96), 320);
}
void renderPage(int page) {
float phase = page == targetPage ? phasePage : (1.0f - phasePage);
@@ -462,13 +518,13 @@ struct Inventory {
int itemIndex = 0;
for (int i = 0; i < itemsCount; i++) {
Item &item = items[i];
Item *item = items[i];
if (item.desc.page != page)
if (item->desc.page != page)
continue;
float a = getAngle(itemIndex, count) - angle - collapseAngle;
float ia = item.angle;
float ia = item->angle;
float ra = ringTilt;
float rd = radius;
float rh = ringHeight;
@@ -480,9 +536,9 @@ struct Inventory {
}
Basis basis = Basis(quat(vec3(1, 0, 0), ra), vec3(0.0f));
basis = basis * Basis(quat(vec3(0, 1, 0), PI + ia - a), vec3(sinf(a), 0, -cosf(a)) * rd - vec3(0, item.desc.page * INVENTORY_HEIGHT - rh, 0));
basis = basis * Basis(quat(vec3(0, 1, 0), PI + ia - a), vec3(sinf(a), 0, -cosf(a)) * rd - vec3(0, item->desc.page * INVENTORY_HEIGHT - rh, 0));
item.render(game, basis);
item->render(game, basis);
itemIndex++;
}

View File

@@ -197,13 +197,12 @@ struct Lara : Character {
enum Type { EMPTY = -1, PISTOLS, SHOTGUN, MAGNUMS, UZIS, MAX };
enum State { IS_HIDDEN, IS_ARMED, IS_FIRING };
enum Anim { NONE, PREPARE, UNHOLSTER, HOLSTER, HOLD, AIM, FIRE };
int ammo; // if -1 weapon is not available
} weapons[Weapon::MAX];
};
Weapon::Type wpnCurrent;
Weapon::Type wpnNext;
Weapon::State wpnState;
int *wpnAmmo;
vec3 chestOffset;
struct Arm {
@@ -406,18 +405,9 @@ struct Lara : Character {
getEntity().flags.active = 1;
initMeshOverrides();
weapons[Weapon::PISTOLS].ammo = -1;
weapons[Weapon::SHOTGUN].ammo = -1;
weapons[Weapon::MAGNUMS].ammo = -1;
weapons[Weapon::UZIS ].ammo = -1;
if (!home) {
weapons[Weapon::PISTOLS].ammo = 0;
weapons[Weapon::SHOTGUN].ammo = 9000;
weapons[Weapon::MAGNUMS].ammo = 9000;
weapons[Weapon::UZIS ].ammo = 9000;
if (!home)
wpnSet(Weapon::PISTOLS);
} else
else
meshSwap(1, TR::MODEL_LARA_SPEC, BODY_UPPER | BODY_LOWER);
for (int i = 0; i < 2; i++) {
@@ -433,12 +423,12 @@ struct Lara : Character {
//reset(14, vec3(40448, 3584, 60928), PI * 0.5f, true); // gym (pool)
//reset(14, vec3(20215, 6656, 52942), PI); // level 1 (bridge)
reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool)
//reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool)
//reset(61, vec3(27221, -1024, 29205), PI * 0.5f); // level 2 (blade)
//reset(43, vec3(31400, -2560, 25200), PI); // level 2 (reach)
//reset(16, vec3(60907, 0, 39642), PI * 3 / 2); // level 2 (hang & climb)
//reset(19, vec3(60843, 1024, 30557), PI); // level 2 (block)
//reset(1, vec3(62630, -1280, 19633), 0); // level 2 (dark medikit)
reset(1, vec3(62630, -1280, 19633), 0); // level 2 (dark medikit)
//reset(7, vec3(64108, -512, 16514), -PI * 0.5f); // level 2 (bat trigger)
//reset(15, vec3(70082, -512, 26935), PI * 0.5f); // level 2 (bear)
//reset(63, vec3(31390, -2048, 33472), 0.0f); // level 2 (trap floor)
@@ -498,10 +488,24 @@ struct Lara : Character {
updateLights(false);
}
TR::Entity::Type getCurrentWeaponInv() {
switch (wpnCurrent) {
case Weapon::Type::PISTOLS : return TR::Entity::PISTOLS;
case Weapon::Type::SHOTGUN : return TR::Entity::SHOTGUN;
case Weapon::Type::MAGNUMS : return TR::Entity::MAGNUMS;
case Weapon::Type::UZIS : return TR::Entity::UZIS;
default : return TR::Entity::NONE;
}
}
void wpnSet(Weapon::Type wType) {
wpnCurrent = wType;
wpnState = Weapon::IS_FIRING;
TR::Entity::Type invType = getCurrentWeaponInv();
wpnAmmo = game->invCount(invType);
arms[0].animation = arms[1].animation = Animation(level, &level->models[wType == Weapon::SHOTGUN ? TR::MODEL_SHOTGUN : TR::MODEL_PISTOLS]);
wpnSetAnim(arms[0], Weapon::IS_HIDDEN, Weapon::Anim::NONE, 0.0f, 0.0f);
@@ -576,7 +580,7 @@ struct Lara : Character {
if (wpnCurrent != Weapon::SHOTGUN) {
meshSwap(1, level->extra.weapons[wpnCurrent], mask);
// have a shotgun in inventory place it on the back if another weapon is in use
meshSwap(2, level->extra.weapons[Weapon::SHOTGUN], (weapons[Weapon::SHOTGUN].ammo != -1) ? BODY_CHEST : 0);
meshSwap(2, level->extra.weapons[Weapon::SHOTGUN], game->invCount(TR::Entity::INV_SHOTGUN) ? BODY_CHEST : 0);
} else {
meshSwap(2, level->extra.weapons[wpnCurrent], mask);
}
@@ -755,7 +759,7 @@ struct Lara : Character {
float nearDist = 32.0f * 1024.0f;
vec3 nearPos;
bool hasShot = false;
int shots = 0;
for (int i = 0; i < count; i++) {
int armIndex;
@@ -768,8 +772,15 @@ struct Lara : Character {
}
Arm *arm = &arms[armIndex];
if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO) {
if (*wpnAmmo <= 0)
continue;
if (wpnCurrent != Weapon::SHOTGUN)
*wpnAmmo -= 1;
}
arm->shotTimer = 0.0f;
hasShot = true;
shots++;
int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8);
@@ -798,9 +809,16 @@ struct Lara : Character {
Core::lightColor[1 + armIndex] = FLASH_LIGHT_COLOR;
}
if (hasShot) {
if (shots) {
playSound(wpnGetSound(), pos, Sound::Flags::PAN);
playSound(TR::SND_RICOCHET, nearPos, Sound::Flags::PAN);
if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && wpnCurrent == Weapon::SHOTGUN)
*wpnAmmo -= 1;
}
if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && *wpnAmmo <= 0) {
wpnChange(Weapon::PISTOLS);
}
}

View File

@@ -136,6 +136,10 @@ struct Level : IGame {
inventory.add(type, count);
}
virtual int* invCount(TR::Entity::Type type) {
return inventory.getCountPtr(type);
}
virtual Sound::Sample* playSound(int id, const vec3 &pos, int flags, int group = -1) const {
if (level.version == TR::Level::VER_TR1_PSX && id == TR::SND_SECRET)
return NULL;
@@ -979,11 +983,18 @@ struct Level : IGame {
if (oxygen <= 0.2f) oxygen = 0.0f;
}
if (inventory.showHealthBar() || (!inventory.active && (!lara->emptyHands() || lara->damageTime > 0.0f || health <= 0.2f)))
UI::renderBar(0, vec2(32, 32), size, health);
if (inventory.showHealthBar() || (!inventory.active && (!lara->emptyHands() || lara->damageTime > 0.0f || health <= 0.2f))) {
UI::renderBar(0, vec2(UI::width - 32 - size.x, 32), size, health);
if (!inventory.active && !lara->emptyHands()) { // ammo
int index = inventory.contains(lara->getCurrentWeaponInv());
if (index > -1)
inventory.renderItemCount(inventory.items[index], vec2(UI::width - 32 - size.x, 64), size.x);
}
}
if (lara->stand == Lara::STAND_ONWATER || lara->stand == Character::STAND_UNDERWATER)
UI::renderBar(1, vec2(UI::width - 32 - size.x, 32), size, oxygen);
UI::renderBar(1, vec2(32, 32), size, oxygen);
inventory.renderUI();