From 8776e04935ab4114b26191d3476d74b912dad6c2 Mon Sep 17 00:00:00 2001 From: XProger Date: Sat, 1 Jul 2017 03:56:32 +0300 Subject: [PATCH] #11 ammo counter --- src/controller.h | 3 + src/format.h | 10 +-- src/inventory.h | 170 +++++++++++++++++++++++++++++++---------------- src/lara.h | 58 ++++++++++------ src/level.h | 17 ++++- 5 files changed, 173 insertions(+), 85 deletions(-) diff --git a/src/controller.h b/src/controller.h index c4a8a13..cf70cf5 100644 --- a/src/controller.h +++ b/src/controller.h @@ -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; } }; diff --git a/src/format.h b/src/format.h index 47a33b5..804a317 100644 --- a/src/format.h +++ b/src/format.h @@ -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 diff --git a/src/inventory.h b/src/inventory.h index 71867ac..6f7fda8 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -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++; } diff --git a/src/lara.h b/src/lara.h index b4676b7..0ed9c93 100644 --- a/src/lara.h +++ b/src/lara.h @@ -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); } } diff --git a/src/level.h b/src/level.h index be64e13..37cfc5b 100644 --- a/src/level.h +++ b/src/level.h @@ -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();