From d5c48f936acc0a55ba547b0be1095e2275f0a182 Mon Sep 17 00:00:00 2001 From: XProger Date: Thu, 22 Jun 2017 04:03:08 +0300 Subject: [PATCH 1/7] #11 health & oxygen bars; #23 auto-retarget when enemy died; remove unused shaders; fix underwater sound stop in Web version --- src/cache.h | 31 ++--- src/character.h | 8 +- src/core.h | 16 ++- src/enemy.h | 10 +- src/format.h | 3 +- src/game.h | 10 +- src/inventory.h | 78 +++++------- src/lara.h | 50 ++++++-- src/level.h | 65 ++++++++-- src/mesh.h | 148 ++++++++++++++-------- src/platform/win/OpenLara.vcxproj | 3 - src/platform/win/OpenLara.vcxproj.filters | 9 -- src/platform/win/OpenLara.vcxproj.user | 3 + src/shaders/depth.glsl | 80 ------------ src/shaders/gui.glsl | 7 +- src/shaders/shadow.glsl | 135 -------------------- src/shaders/volume.glsl | 45 ------- src/ui.h | 36 +++++- 18 files changed, 299 insertions(+), 438 deletions(-) delete mode 100644 src/shaders/depth.glsl delete mode 100644 src/shaders/shadow.glsl delete mode 100644 src/shaders/volume.glsl diff --git a/src/cache.h b/src/cache.h index 670d756..14c0260 100644 --- a/src/cache.h +++ b/src/cache.h @@ -27,10 +27,6 @@ const char FILTER[] = #include "shaders/filter.glsl" ; -const char VOLUME[] = - #include "shaders/volume.glsl" -; - const char GUI[] = #include "shaders/gui.glsl" ; @@ -45,15 +41,15 @@ struct ShaderCache { memset(shaders, 0, sizeof(shaders)); LOG("shader: cache warm up...\n"); - if (Core::settings.shadows) + if (Core::settings.detail.shadows) compile(Core::passShadow, Shader::ENTITY, FX_NONE); - if (Core::settings.ambient) { + if (Core::settings.detail.ambient) { compile(Core::passAmbient, Shader::ROOM, FX_NONE); compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST); compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER); compile(Core::passAmbient, Shader::SPRITE, FX_ALPHA_TEST); - if (Core::settings.water) { + if (Core::settings.detail.water) { compile(Core::passAmbient, Shader::ROOM, FX_CLIP_PLANE); compile(Core::passAmbient, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); compile(Core::passAmbient, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); @@ -61,7 +57,7 @@ struct ShaderCache { } } - if (Core::settings.water) { + if (Core::settings.detail.water) { compile(Core::passWater, Shader::WATER_MASK, FX_NONE); compile(Core::passWater, Shader::WATER_STEP, FX_NONE); compile(Core::passWater, Shader::WATER_CAUSTICS, FX_NONE); @@ -83,7 +79,7 @@ struct ShaderCache { compile(Core::passCompose, Shader::SPRITE, FX_ALPHA_TEST); compile(Core::passCompose, Shader::SPRITE, FX_UNDERWATER | FX_ALPHA_TEST); compile(Core::passCompose, Shader::FLASH, FX_ALPHA_TEST); - if (Core::settings.water) { + if (Core::settings.detail.water) { compile(Core::passCompose, Shader::ROOM, FX_CLIP_PLANE); compile(Core::passCompose, Shader::ROOM, FX_ALPHA_TEST | FX_CLIP_PLANE); compile(Core::passCompose, Shader::ROOM, FX_UNDERWATER | FX_CLIP_PLANE); @@ -108,7 +104,7 @@ struct ShaderCache { Shader* compile(Core::Pass pass, Shader::Type type, int fx) { char def[1024], ext[255]; ext[0] = 0; - if (Core::settings.shadows) { + if (Core::settings.detail.shadows) { if (Core::support.shadowSampler) { #ifdef MOBILE strcat(ext, "#extension GL_EXT_shadow_samplers : require\n"); @@ -139,10 +135,10 @@ struct ShaderCache { if (fx & FX_UNDERWATER) strcat(def, "#define UNDERWATER\n" UNDERWATER_COLOR); if (fx & FX_ALPHA_TEST) strcat(def, "#define ALPHA_TEST\n"); if (fx & FX_CLIP_PLANE) strcat(def, "#define CLIP_PLANE\n"); - if (Core::settings.ambient) strcat(def, "#define OPT_AMBIENT\n"); - if (Core::settings.lighting) strcat(def, "#define OPT_LIGHTING\n"); - if (Core::settings.shadows) strcat(def, "#define OPT_SHADOW\n"); - if (Core::settings.water) strcat(def, "#define OPT_WATER\n"); + if (Core::settings.detail.ambient) strcat(def, "#define OPT_AMBIENT\n"); + if (Core::settings.detail.lighting) strcat(def, "#define OPT_LIGHTING\n"); + if (Core::settings.detail.shadows) strcat(def, "#define OPT_SHADOW\n"); + if (Core::settings.detail.water) strcat(def, "#define OPT_WATER\n"); break; } case Core::passWater : { @@ -162,13 +158,6 @@ struct ShaderCache { sprintf(def, "%s#define PASS_%s\n#define FILTER_%s\n", ext, passNames[pass], typ); break; } - case Core::passVolume : { - static const char *typeNames[] = { "DEFAULT" }; - src = VOLUME; - typ = typeNames[type]; - sprintf(def, "%s#define PASS_%s\n", ext, passNames[pass]); - break; - } case Core::passGUI : { static const char *typeNames[] = { "DEFAULT" }; src = GUI; diff --git a/src/character.h b/src/character.h index fb54a25..45635ed 100644 --- a/src/character.h +++ b/src/character.h @@ -5,7 +5,7 @@ #include "trigger.h" struct Character : Controller { - int health; + float health; float tilt; quat rotHead, rotChest; @@ -37,7 +37,7 @@ struct Character : Controller { Collision collision; - Character(IGame *game, int entity, int health) : Controller(game, entity), health(health), tilt(0.0f), stand(STAND_GROUND), lastInput(0), velocity(0.0f), angleExt(0.0f) { + Character(IGame *game, int entity, float health) : Controller(game, entity), health(health), tilt(0.0f), stand(STAND_GROUND), lastInput(0), velocity(0.0f), angleExt(0.0f) { animation.initOverrides(); rotHead = rotChest = quat(0, 0, 0, 1); @@ -68,8 +68,8 @@ struct Character : Controller { angle.x = clamp(angle.x + delta, -PI * 0.49f, PI * 0.49f); } - virtual void hit(int damage, Controller *enemy = NULL) { - health -= damage; + virtual void hit(float damage, Controller *enemy = NULL) { + health = max(0.0f, health - damage); }; virtual void checkRoom() { diff --git a/src/core.h b/src/core.h index 4b908c4..6ee1269 100644 --- a/src/core.h +++ b/src/core.h @@ -308,7 +308,7 @@ namespace Core { Texture *blackTex, *whiteTex; - enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passVolume, passGUI, passMAX } pass; + enum Pass { passCompose, passShadow, passAmbient, passWater, passFilter, passGUI, passMAX } pass; GLuint FBO, defaultFBO; Texture *defaultTarget; @@ -362,10 +362,16 @@ namespace Core { } stats; struct { - bool ambient; - bool lighting; - bool shadows; - bool water; + struct { + bool ambient; + bool lighting; + bool shadows; + bool water; + } detail; + + struct { + bool retarget; + } controls; } settings; } diff --git a/src/enemy.h b/src/enemy.h index 3aaf10e..7505cf8 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -72,7 +72,7 @@ struct Enemy : Character { bool targetFromView; // enemy in target view zone bool targetCanAttack; - Enemy(IGame *game, int entity, int health, int radius, float length, float aggression) : Character(game, entity, health), ai(AI_RANDOM), mood(MOOD_SLEEP), wound(false), nextState(0), targetBox(-1), thinkTime(1.0f / 30.0f), length(length), aggression(aggression), radius(radius), target(NULL), path(NULL) { + Enemy(IGame *game, int entity, float health, int radius, float length, float aggression) : Character(game, entity, health), ai(AI_RANDOM), mood(MOOD_SLEEP), wound(false), nextState(0), targetBox(-1), thinkTime(1.0f / 30.0f), length(length), aggression(aggression), radius(radius), target(NULL), path(NULL) { stepHeight = 256; dropHeight = -256; @@ -257,12 +257,12 @@ struct Enemy : Character { return 0; } - virtual void hit(int damage, Controller *enemy = NULL) { + virtual void hit(float damage, Controller *enemy = NULL) { Character::hit(damage, enemy); wound = true; }; - void bite(const vec3 &pos, int damage) { + void bite(const vec3 &pos, float damage) { ASSERT(target); target->hit(damage, this); Sprite::add(game, TR::Entity::BLOOD, target->getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, Sprite::FRAME_ANIMATED); @@ -605,7 +605,7 @@ struct Wolf : Enemy { case STATE_ATTACK : case STATE_BITE : if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { - bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_ATTACK ? 50 : 100); + bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_ATTACK ? 50.0f : 100.0f); nextState = state == STATE_ATTACK ? STATE_RUN : STATE_GROWL; } return state == STATE_ATTACK ? STATE_RUN : state; @@ -753,7 +753,7 @@ struct Bear : Enemy { case STATE_BITE : case STATE_ATTACK : if (nextState == STATE_NONE && (collide(target) & HIT_MASK)) { - bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_BITE ? 200 : 400); + bite(animation.getJoints(getMatrix(), jointHead, true).pos, state == STATE_BITE ? 200.0f : 400.0f); nextState = state == STATE_BITE ? STATE_STOP : STATE_HOWL; } break; diff --git a/src/format.h b/src/format.h index 429b28e..47a33b5 100644 --- a/src/format.h +++ b/src/format.h @@ -393,12 +393,13 @@ namespace TR { Color32(uint8 r, uint8 g, uint8 b, uint8 a) : r(r), g(g), b(b), a(a) {} }; - struct Color24 { uint8 r, g, b; Color24() {} Color24(uint8 r, uint8 g, uint8 b) : r(r), g(g), b(b) {} + + operator Color32() const { return Color32(r, g, b, 255); } }; struct Color16 { diff --git a/src/game.h b/src/game.h index 05b41c9..6d88517 100644 --- a/src/game.h +++ b/src/game.h @@ -19,10 +19,12 @@ namespace Game { void init(Stream *lvl, Stream *snd) { Core::init(); - Core::settings.ambient = true; - Core::settings.lighting = true; - Core::settings.shadows = true; - Core::settings.water = Core::support.texFloat || Core::support.texHalf; + Core::settings.detail.ambient = true; + Core::settings.detail.lighting = true; + Core::settings.detail.shadows = true; + Core::settings.detail.water = Core::support.texFloat || Core::support.texHalf; + + Core::settings.controls.retarget = true; level = NULL; startLevel(lvl, snd, false, false); diff --git a/src/inventory.h b/src/inventory.h index 3b5c3e0..71867ac 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -277,6 +277,26 @@ struct Inventory { return 0; } + float getAngle(int index, int count) { + return PI * 2.0f / float(count) * index; + } + + int getItemsCount(int page) { + int count = 0; + + for (int i = 0; i < itemsCount; i++) + if (items[i].desc.page == page) + count++; + + return count; + } + + bool showHealthBar() { + int idx = getItemIndex(page, index); + 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); + } + void update() { doPhase(active, 2.0f, phaseRing); doPhase(true, 1.6f, phasePage); @@ -334,7 +354,7 @@ struct Inventory { float w = 90.0f * DEG2RAD * Core::deltaTime; - int itemIndex = getItemIndex(page, index); + int itemIndex = index == targetIndex ? getItemIndex(page, index) : -1; for (int i = 0; i < itemsCount; i++) { items[i].update(); @@ -420,20 +440,6 @@ struct Inventory { } } - float getAngle(int index, int count) { - return PI * 2.0f / float(count) * index; - } - - int getItemsCount(int page) { - int count = 0; - - for (int i = 0; i < itemsCount; i++) - if (items[i].desc.page == page) - count++; - - return count; - } - void renderPage(int page) { float phase = page == targetPage ? phasePage : (1.0f - phasePage); @@ -465,15 +471,16 @@ struct Inventory { float ia = item.angle; float ra = ringTilt; float rd = radius; + float rh = ringHeight; if (itemIndex == pageItemIndex[page] && (chosen || phaseChoose > 0.0f)) { ia *= 1.0f - phaseChoose; - ra *= 1.0f - phaseChoose; + rh -= 128 * phaseChoose; rd += 296 * phaseChoose; } 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 - ringHeight, 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); @@ -531,46 +538,27 @@ struct Inventory { renderPage(page); if (page != targetPage) renderPage(targetPage); + } - if (phaseRing < 1.0f) - return; - - Core::setDepthTest(false); - Core::setBlending(bmAlpha); - Core::setCulling(cfNone); - game->setupBinding(); - - float w = 480 * aspect; - Core::mViewProj = mat4(0.0f, w, 480, 0.0f, 0.0f, 1.0f); - - game->setShader(Core::passGUI, Shader::DEFAULT); - Core::active.shader->setParam(uMaterial, vec4(1.0f)); - Core::active.shader->setParam(uPosScale, vec4(0.0f, 0.0f, 1.0f, 1.0f)); - - UI::textBegin(); + void renderUI() { + if (!active || phaseRing < 1.0f) return; static const char* pageTitle[PAGE_MAX] = { "OPTION", "INVENTORY", "ITEMS" }; - UI::textOut(game, vec2( 0, 32), pageTitle[page], UI::aCenter, w); + UI::textOut(game, vec2( 0, 32), pageTitle[page], UI::aCenter, UI::width); if (page < PAGE_ITEMS && getItemsCount(page + 1)) { - UI::textOut(game, vec2(16, 32), "\x5B", UI::aLeft, w); - UI::textOut(game, vec2( 0, 32), "\x5B", UI::aRight, w - 20); + UI::textOut(game, vec2(16, 32), "\x5B", UI::aLeft, UI::width); + UI::textOut(game, vec2( 0, 32), "\x5B", UI::aRight, UI::width - 20); } if (page > PAGE_OPTION && getItemsCount(page - 1)) { - UI::textOut(game, vec2(16, 480 - 16), "\x5D", UI::aLeft, w); - UI::textOut(game, vec2(0, 480 - 16), "\x5D", UI::aRight, w - 20); + UI::textOut(game, vec2(16, 480 - 16), "\x5D", UI::aLeft, UI::width); + UI::textOut(game, vec2(0, 480 - 16), "\x5D", UI::aRight, UI::width - 20); } if (index == targetIndex) - renderItemText(items[getItemIndex(page, index)], w); - - UI::textEnd(game); - - Core::setCulling(cfFront); - Core::setBlending(bmNone); - Core::setDepthTest(true); + renderItemText(items[getItemIndex(page, index)], UI::width); } }; diff --git a/src/lara.h b/src/lara.h index efe94e2..b4676b7 100644 --- a/src/lara.h +++ b/src/lara.h @@ -20,7 +20,8 @@ #define LARA_TILT_SPEED (DEG2RAD * 37.5f) #define LARA_TILT_MAX (DEG2RAD * 10.0f) -#define LARA_MAX_HEALTH 1000 +#define LARA_MAX_HEALTH 1000.0f +#define LARA_MAX_OXYGEN 60.0f #define LARA_HANG_OFFSET 724 #define LARA_HEIGHT 762 @@ -36,6 +37,8 @@ #define LARA_WET_SPECULAR 0.5f #define LARA_WET_TIMER (LARA_WET_SPECULAR / 16.0f) // 4 sec +#define LARA_DAMAGE_TIME (40.0f / 30.0f) + #define PICKUP_FRAME_GROUND 40 #define PICKUP_FRAME_UNDERWATER 18 #define PUZZLE_FRAME 80 @@ -219,6 +222,8 @@ struct Lara : Character { int roomPrev; // water out from room vec2 rotFactor; + float oxygen; + float damageTime; float hitTime; int hitDir; vec3 collisionOffset; @@ -393,8 +398,10 @@ struct Lara : Character { animation.setAnim(ANIM_STAND); } - hitDir = -1; - hitTime = 0.0f; + oxygen = LARA_MAX_OXYGEN; + hitDir = -1; + damageTime = LARA_DAMAGE_TIME; + hitTime = 0.0f; getEntity().flags.active = 1; initMeshOverrides(); @@ -426,7 +433,7 @@ 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) @@ -520,7 +527,7 @@ struct Lara : Character { wpnSetState(wState); } - int wpnGetDamage() { + float wpnGetDamage() { switch (wpnCurrent) { case Weapon::PISTOLS : return 1; case Weapon::SHOTGUN : return 1; @@ -1059,8 +1066,18 @@ struct Lara : Character { return; } + // auto retarget + bool retarget = false; + if (Core::settings.controls.retarget) { + for (int i = 0; i < 2; i++) + if (arms[i].tracking == -1 || ((Character*)level->entities[arms[i].tracking].controller)->health <= 0.0f) { + retarget = true; + break; + } + } + int count = wpnCurrent != Weapon::SHOTGUN ? 2 : 1; - if (!(input & ACTION)) { + if (!(input & ACTION) || retarget) { getTargets(arms[0].tracking, arms[1].tracking); if (count == 1) arms[1].tracking = -1; @@ -1219,8 +1236,10 @@ struct Lara : Character { } } - virtual void hit(int damage, Controller *enemy = NULL) { - health -= damage; + virtual void hit(float damage, Controller *enemy = NULL) { + damageTime = LARA_DAMAGE_TIME; + + Character::hit(damage, enemy); if (damage == 10000) { // T-Rex attack (fatal) pos = enemy->pos; angle = enemy->angle; @@ -1244,6 +1263,7 @@ struct Lara : Character { case TR::Entity::INV_UZIS : wpnChange(Lara::Weapon::UZIS); break; case TR::Entity::INV_MEDIKIT_SMALL : case TR::Entity::INV_MEDIKIT_BIG : + damageTime = LARA_DAMAGE_TIME; health = min(LARA_MAX_HEALTH, health + (item == TR::Entity::INV_MEDIKIT_SMALL ? LARA_MAX_HEALTH / 2 : LARA_MAX_HEALTH)); playSound(TR::SND_HEALTH, pos, Sound::PAN); break; @@ -1866,7 +1886,7 @@ struct Lara : Character { virtual int getStateDeath() { velocity = vec3(0.0f); - return STATE_DEATH; + return (stand == STAND_UNDERWATER || stand == STAND_ONWATER) ? STATE_UNDERWATER_DEATH : STATE_DEATH; } virtual int getStateDefault() { @@ -1952,6 +1972,18 @@ struct Lara : Character { virtual void update() { Character::update(); + + if (damageTime > 0.0f) + damageTime = max(0.0f, damageTime - Core::deltaTime); + + if (stand == STAND_UNDERWATER) { + if (oxygen > 0.0f) + oxygen -= Core::deltaTime; + else + hit(Core::deltaTime * 150.0f); + } else + if (oxygen < LARA_MAX_OXYGEN) + oxygen = min(LARA_MAX_OXYGEN, oxygen += Core::deltaTime * 10.0f); } virtual void updateAnimation(bool commands) { diff --git a/src/level.h b/src/level.h index 896765e..be64e13 100644 --- a/src/level.h +++ b/src/level.h @@ -295,10 +295,10 @@ struct Level : IGame { level.cameraController = camera; - ambientCache = Core::settings.ambient ? new AmbientCache(this) : NULL; - waterCache = Core::settings.water ? new WaterCache(this) : NULL; + ambientCache = Core::settings.detail.ambient ? new AmbientCache(this) : NULL; + waterCache = Core::settings.detail.water ? new WaterCache(this) : NULL; zoneCache = new ZoneCache(this); - shadow = Core::settings.shadows ? new Texture(SHADOW_TEX_SIZE, SHADOW_TEX_SIZE, Texture::SHADOW, false) : NULL; + shadow = Core::settings.detail.shadows ? new Texture(SHADOW_TEX_SIZE, SHADOW_TEX_SIZE, Texture::SHADOW, false) : NULL; initReflections(); @@ -379,11 +379,29 @@ struct Level : IGame { int i = y * 1024 + x; data[i].r = data[i].g = data[i].b = data[i].a = 255; // white texel for colored triangles } -/* + +// uint32 healthBar[1+5+1] = { 0xFF2C5C70, 0xFF2C5C70, 0xFF4878A4, 0xFF2C5C70, 0xFF004458, 0xFF143050, 0xFF143050 }; + uint32 healthBar[1+5+1] = { 0xFF2C5D71, 0xFF2C5D71, 0xFF5E81AE, 0xFF2C5D71, 0xFF1B4557, 0xFF16304F, 0xFF16304F }; + + for (int y = 0; y < COUNT(healthBar); y++) + for (int x = 0; x < 2; x++) { + int i = (TEX_HEALTH_BAR_Y + y) * 1024 + (TEX_HEALTH_BAR_X + x); + *((uint32*)&data[i]) = healthBar[y]; + } + + uint32 oxygenBar[1+5+1] = { 0xFF647464, 0xFF647464, 0xFFA47848, 0xFF647464, 0xFF4C504C, 0xFF303030, 0xFF303030 }; + for (int y = 0; y < COUNT(oxygenBar); y++) + for (int x = 0; x < 2; x++) { + int i = (TEX_OXYGEN_BAR_Y + y) * 1024 + (TEX_OXYGEN_BAR_X + x); + *((uint32*)&data[i]) = oxygenBar[y]; + } + + + /* FILE *f = fopen("atlas.raw", "wb"); fwrite(data, 1024 * 1024 * 4, 1, f); fclose(f); -*/ + */ atlas = new Texture(1024, 1024, Texture::RGBA, false, data); PROFILE_LABEL(TEXTURE, atlas->ID, "atlas"); @@ -590,7 +608,7 @@ struct Level : IGame { if (isModel) { // model vec3 pos = controller->getPos(); - if (Core::settings.ambient) { + if (Core::settings.detail.ambient) { AmbientCache::Cube cube; if (Core::stats.frame != controller->frameIndex) { ambientCache->getAmbient(entity.room, pos, cube); @@ -647,11 +665,9 @@ struct Level : IGame { sndCurrent = camera->isUnderwater() ? sndUnderwater : sndSoundtrack; - if (sndCurrent) { - if (sndSoundtrack && sndCurrent != sndSoundtrack) sndSoundtrack->volume = 0.0f; - if (sndUnderwater && sndCurrent != sndUnderwater) sndUnderwater->volume = 0.0f; - sndCurrent->volume = 1.0f; - } + if (sndSoundtrack && sndCurrent != sndSoundtrack) sndSoundtrack->volume = 0.0f; + if (sndUnderwater && sndCurrent != sndUnderwater) sndUnderwater->volume = 0.0f; + if (sndCurrent) sndCurrent->volume = 1.0f; if (waterCache) waterCache->update(); @@ -949,6 +965,31 @@ struct Level : IGame { inventory.render(); } + void renderUI() { + UI::begin(); + + // render health & oxygen bars + vec2 size = vec2(180, 10); + + float health = lara->health / float(LARA_MAX_HEALTH); + float oxygen = lara->oxygen / float(LARA_MAX_OXYGEN); + + if ((params->time - int(params->time)) < 0.5f) { // blinking + if (health <= 0.2f) health = 0.0f; + 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 (lara->stand == Lara::STAND_ONWATER || lara->stand == Character::STAND_UNDERWATER) + UI::renderBar(1, vec2(UI::width - 32 - size.x, 32), size, oxygen); + + inventory.renderUI(); + + UI::end(); + } + void render() { bool title = isTitle(); bool copyBg = title && lastTitle != title; @@ -970,6 +1011,8 @@ struct Level : IGame { if (title) renderInventory(); + renderUI(); + lastTitle = title; } diff --git a/src/mesh.h b/src/mesh.h index cf2a008..c311917 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -4,6 +4,12 @@ #include "core.h" #include "format.h" +#define TEX_HEALTH_BAR_X 1000 +#define TEX_HEALTH_BAR_Y 1000 + +#define TEX_OXYGEN_BAR_X 1002 +#define TEX_OXYGEN_BAR_Y 1000 + typedef unsigned short Index; struct Vertex { @@ -184,7 +190,6 @@ struct MeshBuilder { MeshRange *sequences; // procedured MeshRange shadowBlob, shadowBox; - MeshRange bar; MeshRange quad, circle; MeshRange plane; @@ -227,7 +232,7 @@ struct MeshBuilder { iCount += d.rCount * 6 + d.tCount * 3; vCount += d.rCount * 4 + d.tCount * 3; - if (Core::settings.water) + if (Core::settings.detail.water) roomRemoveWaterSurfaces(r, iCount, vCount); for (int j = 0; j < r.meshesCount; j++) { @@ -288,13 +293,6 @@ struct MeshBuilder { iCount += shadowBox.iCount; vCount += 4 * 6; - // bar (health, oxygen) - bar.vStart = vCount; - bar.iStart = iCount; - bar.iCount = 2 * 3; - iCount += bar.iCount; - vCount += 4; - // quad (post effect filter) quad.vStart = vCount; quad.iStart = iCount; @@ -498,22 +496,6 @@ struct MeshBuilder { aCount++; } - // white bar - addQuad(indices, iCount, vCount, bar.vStart, vertices, &whiteTile); - vertices[vCount + 0].coord = { 0, 0, 0, 0 }; - vertices[vCount + 1].coord = { 1, 0, 0, 0 }; - vertices[vCount + 2].coord = { 1, 1, 0, 0 }; - vertices[vCount + 3].coord = { 0, 1, 0, 0 }; - - for (int i = 0; i < 4; i++) { - Vertex &v = vertices[vCount + i]; - v.normal = { 0, 0, 0, 0 }; - v.color = { 255, 255, 255, 255 }; - v.texCoord = { 32688, 32688, 0, 0 }; - } - vCount += 4; - aCount++; - // quad addQuad(indices, iCount, vCount, quad.vStart, vertices, &whiteTile); vertices[vCount + 3].coord = { -1, -1, 0, 0 }; @@ -595,7 +577,6 @@ struct MeshBuilder { mesh->initRange(models[i].geometry); mesh->initRange(shadowBlob); mesh->initRange(shadowBox); - mesh->initRange(bar); mesh->initRange(quad); mesh->initRange(circle); mesh->initRange(plane); @@ -978,6 +959,93 @@ struct MeshBuilder { vCount += 4; } + void addBar(Index *indices, Vertex *vertices, int &iCount, int &vCount, int type, const vec2 &pos, const vec2 &size, uint32 color) { + addQuad(indices, iCount, vCount, 0, vertices, NULL); + + int16 minX = int16(pos.x); + int16 minY = int16(pos.y); + int16 maxX = int16(size.x) + minX; + int16 maxY = int16(size.y) + minY; + + vertices[vCount + 0].coord = { minX, minY, 0, 0 }; + vertices[vCount + 1].coord = { maxX, minY, 0, 0 }; + vertices[vCount + 2].coord = { maxX, maxY, 0, 0 }; + vertices[vCount + 3].coord = { minX, maxY, 0, 0 }; + + for (int i = 0; i < 4; i++) { + Vertex &v = vertices[vCount + i]; + v.normal = { 0, 0, 0, 0 }; + v.color = *((ubyte4*)&color); + + int16 s, t; + + if (type == 0) { // health bar + s = TEX_HEALTH_BAR_X + 1; + t = TEX_HEALTH_BAR_Y + 1; + } else { // oxygen bar + s = TEX_OXYGEN_BAR_X + 1; + t = TEX_OXYGEN_BAR_Y + 1; + } + + if (i > 1) t += 5; + + s = int(s) * 32767 / 1024; + t = int(t) * 32767 / 1024; + + v.texCoord = { s, t, 0, 0 }; + } + + vCount += 4; + } + + void addFrame(Index *indices, Vertex *vertices, int &iCount, int &vCount, const vec2 &pos, const vec2 &size, uint32 color1, uint32 color2) { + int16 minX = int16(pos.x); + int16 minY = int16(pos.y); + int16 maxX = int16(size.x) + minX; + int16 maxY = int16(size.y) + minY; + + vertices[vCount + 0].coord = { minX, minY, 0, 0 }; + vertices[vCount + 1].coord = { maxX, minY, 0, 0 }; + vertices[vCount + 2].coord = { maxX, int16(minY + 1), 0, 0 }; + vertices[vCount + 3].coord = { minX, int16(minY + 1), 0, 0 }; + + vertices[vCount + 4].coord = { minX, minY, 0, 0 }; + vertices[vCount + 5].coord = { int16(minX + 1), minY, 0, 0 }; + vertices[vCount + 6].coord = { int16(minX + 1), maxY, 0, 0 }; + vertices[vCount + 7].coord = { minX, maxY, 0, 0 }; + + for (int i = 0; i < 8; i++) { + Vertex &v = vertices[vCount + i]; + v.normal = { 0, 0, 0, 0 }; + v.color = *((ubyte4*)&color1); + v.texCoord = { 32688, 32688, 0, 0 }; + } + + addQuad(indices, iCount, vCount, 0, vertices, NULL); vCount += 4; + addQuad(indices, iCount, vCount, 0, vertices, NULL); vCount += 4; + + vertices[vCount + 0].coord = { minX, int16(maxY - 1), 0, 0 }; + vertices[vCount + 1].coord = { maxX, int16(maxY - 1), 0, 0 }; + vertices[vCount + 2].coord = { maxX, maxY, 0, 0 }; + vertices[vCount + 3].coord = { minX, maxY, 0, 0 }; + + vertices[vCount + 4].coord = { int16(maxX - 1), minY, 0, 0 }; + vertices[vCount + 5].coord = { maxX, minY, 0, 0 }; + vertices[vCount + 6].coord = { maxX, maxY, 0, 0 }; + vertices[vCount + 7].coord = { int16(maxX - 1), maxY, 0, 0 }; + + for (int i = 0; i < 8; i++) { + Vertex &v = vertices[vCount + i]; + v.normal = { 0, 0, 0, 0 }; + v.color = *((ubyte4*)&color2); + v.texCoord = { 32688, 32688, 0, 0 }; + } + + addQuad(indices, iCount, vCount, 0, vertices, NULL); vCount += 4; + addQuad(indices, iCount, vCount, 0, vertices, NULL); vCount += 4; + } + + void bind() { mesh->bind(); } @@ -1029,34 +1097,6 @@ struct MeshBuilder { void renderPlane() { mesh->render(plane); } - - void renderBar(const vec2 &size, float value) { - /* - float w = size.y / 9.0f; - // health bar - enum Colors { - clBlack = 0xFF000000, - clGrayL = 0xFF748474, - clGrayD = 0xFF4C504C, - clRed1 = 0xFF705C2C, - clRed2 = 0xFFA47848, - clRed3 = 0xFF705C2C, - clRed4 = 0xFF584400, - clRed5 = 0xFF503014, - }; - - uint32 *d = (uint32*)&data[0]; - d[0] = clGrayD; d[1] = clGrayD; d[2] = clGrayD; d[3] = clGrayD; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clBlack; d[2] = clBlack; d[3] = clBlack; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clBlack; d[2] = clRed1; d[3] = clBlack; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clBlack; d[2] = clRed2; d[3] = clBlack; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clBlack; d[2] = clRed3; d[3] = clBlack; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clBlack; d[2] = clRed4; d[3] = clBlack; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clBlack; d[2] = clRed5; d[3] = clBlack; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clBlack; d[2] = clBlack; d[3] = clBlack; d[4] = clGrayL; d+= 1024; - d[0] = clGrayD; d[1] = clGrayL; d[2] = clGrayL; d[3] = clGrayL; d[4] = clGrayL; d+= 1024; - */ - } }; #endif \ No newline at end of file diff --git a/src/platform/win/OpenLara.vcxproj b/src/platform/win/OpenLara.vcxproj index 55723da..9483e76 100644 --- a/src/platform/win/OpenLara.vcxproj +++ b/src/platform/win/OpenLara.vcxproj @@ -215,12 +215,9 @@ - - - diff --git a/src/platform/win/OpenLara.vcxproj.filters b/src/platform/win/OpenLara.vcxproj.filters index 3226bd0..7b33c53 100644 --- a/src/platform/win/OpenLara.vcxproj.filters +++ b/src/platform/win/OpenLara.vcxproj.filters @@ -42,9 +42,6 @@ - - shaders - shaders @@ -54,12 +51,6 @@ shaders - - shaders - - - shaders - shaders diff --git a/src/platform/win/OpenLara.vcxproj.user b/src/platform/win/OpenLara.vcxproj.user index a4941d7..a2075f5 100644 --- a/src/platform/win/OpenLara.vcxproj.user +++ b/src/platform/win/OpenLara.vcxproj.user @@ -3,10 +3,12 @@ ../../../bin WindowsLocalDebugger + data/LEVEL3A.PSX ../../../bin WindowsLocalDebugger + data/LEVEL2.PHD ../../../bin @@ -15,5 +17,6 @@ ../../../bin WindowsLocalDebugger + data/LEVEL2.PSX \ No newline at end of file diff --git a/src/shaders/depth.glsl b/src/shaders/depth.glsl deleted file mode 100644 index 859efcd..0000000 --- a/src/shaders/depth.glsl +++ /dev/null @@ -1,80 +0,0 @@ -R"====( -#ifdef GL_ES - precision lowp int; - precision highp float; -#endif - -#if defined(ALPHA_TEST) - varying vec2 vTexCoord; -#endif - -#define TEXCOORD_SCALE (1.0 / 32767.0) - -#ifdef VERTEX - attribute vec4 aCoord; - - #if defined(TYPE_SPRITE) || defined(ALPHA_TEST) - attribute vec4 aTexCoord; - #endif - - #ifdef TYPE_SPRITE - uniform mat4 uViewInv; - #endif - - uniform mat4 uViewProj; - - #ifdef TYPE_ENTITY - uniform vec4 uBasis[32 * 2]; - #else - uniform vec4 uBasis[2]; - #endif - - vec3 mulQuat(vec4 q, vec3 v) { - return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + v * q.w); - } - - vec3 mulBasis(vec4 rot, vec4 pos, vec3 v) { - return mulQuat(rot, v) + pos.xyz; - } - - vec4 _transform() { - #ifdef TYPE_ENTITY - int index = int(aCoord.w * 2.0); - vec4 rBasisRot = uBasis[index]; - vec4 rBasisPos = uBasis[index + 1]; - #else - vec4 rBasisRot = uBasis[0]; - vec4 rBasisPos = uBasis[1]; - #endif - - vec4 coord = vec4(mulBasis(rBasisRot, rBasisPos, aCoord.xyz), rBasisPos.w); - - #ifdef TYPE_SPRITE - coord.xyz += uViewInv[0].xyz * aTexCoord.z - uViewInv[1].xyz * aTexCoord.w; - #endif - - #if defined(ALPHA_TEST) - vTexCoord = aTexCoord.xy * TEXCOORD_SCALE; - #endif - - return coord; - } - - void main() { - gl_Position = uViewProj * _transform(); - } -#else - #ifdef ALPHA_TEST - uniform sampler2D sDiffuse; - #endif - - void main() { - #ifdef ALPHA_TEST - if (texture2D(sDiffuse, vTexCoord).w <= 0.5) - discard; - #endif - - gl_FragColor = vec4(0.0); - } -#endif -)====" \ No newline at end of file diff --git a/src/shaders/gui.glsl b/src/shaders/gui.glsl index 4d157a3..4764b7c 100644 --- a/src/shaders/gui.glsl +++ b/src/shaders/gui.glsl @@ -5,26 +5,29 @@ R"====( #endif varying vec2 vTexCoord; +varying vec4 vColor; #ifdef VERTEX uniform mat4 uViewProj; uniform vec4 uPosScale; + uniform vec4 uMaterial; attribute vec4 aCoord; attribute vec4 aTexCoord; + attribute vec4 aColor; #define TEXCOORD_SCALE (1.0 / 32767.0) void main() { vTexCoord = aTexCoord.xy * TEXCOORD_SCALE; + vColor = aColor * uMaterial; gl_Position = uViewProj * vec4(aCoord.xy * uPosScale.zw + uPosScale.xy, 0.0, 1.0); } #else uniform sampler2D sDiffuse; - uniform vec4 uMaterial; void main() { - gl_FragColor = texture2D(sDiffuse, vTexCoord) * uMaterial; + gl_FragColor = texture2D(sDiffuse, vTexCoord) * vColor; } #endif )====" \ No newline at end of file diff --git a/src/shaders/shadow.glsl b/src/shaders/shadow.glsl deleted file mode 100644 index d06affb..0000000 --- a/src/shaders/shadow.glsl +++ /dev/null @@ -1,135 +0,0 @@ -R"====( -#ifdef GL_ES - precision lowp int; - precision highp float; -#endif - -varying vec3 vNormal; -varying vec4 vLightProj; -varying vec3 vLightVec; - -uniform vec3 uLightPos[MAX_LIGHTS]; -uniform vec4 uLightColor[MAX_LIGHTS]; // xyz - color, w - radius * intensity - -#ifdef VERTEX - attribute vec4 aCoord; - attribute vec4 aNormal; - - uniform mat4 uViewProj; - uniform mat4 uLightProj; - - #ifdef TYPE_ENTITY - uniform vec4 uBasis[32 * 2]; - #else - uniform vec4 uBasis[2]; - #endif - - vec3 mulQuat(vec4 q, vec3 v) { - return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + v * q.w); - } - - vec3 mulBasis(vec4 rot, vec4 pos, vec3 v) { - return mulQuat(rot, v) + pos.xyz; - } - - void main() { - #ifdef TYPE_ENTITY - int index = int(aCoord.w * 2.0); - vec4 rBasisRot = uBasis[index]; - vec4 rBasisPos = uBasis[index + 1]; - #else - vec4 rBasisRot = uBasis[0]; - vec4 rBasisPos = uBasis[1]; - #endif - - vNormal = normalize(mulQuat(rBasisRot, aNormal.xyz)); - - vec4 coord = vec4(mulBasis(rBasisRot, rBasisPos, aCoord.xyz), rBasisPos.w); - - vLightProj = uLightProj * coord; - vLightVec = (uLightPos[0].xyz - coord.xyz) * uLightColor[0].w; - - gl_Position = uViewProj * coord; - } -#else - #ifdef SHADOW_SAMPLER - uniform sampler2DShadow sShadow; - #ifdef GL_ES - #define SHADOW(V) (shadow2DEXT(sShadow, V)) - #else - #define SHADOW(V) (shadow2D(sShadow, V).x) - #endif - #else - uniform sampler2D sShadow; - #define CMP(a,b) step(b, a) - - #ifdef SHADOW_DEPTH - #define compare(p, z) CMP(texture2D(sShadow, (p)).x, (z)); - #elif defined(SHADOW_COLOR) - float unpack(vec4 value) { - vec4 bitSh = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0); - return dot(value, bitSh); - } - #define compare(p, z) CMP(unpack(texture2D(sShadow, (p))), (z)); - #endif - - float SHADOW(vec3 p) { - return compare(p.xy, p.z); - } - #endif - - #define SHADOW_TEXEL (2.0 / 1024.0) - - float random(vec3 seed, float freq) { - float dt = dot(floor(seed * freq), vec3(53.1215, 21.1352, 9.1322)); - return fract(sin(dt) * 2105.2354); - } - - float randomAngle(vec3 seed, float freq) { - return random(seed, freq) * 6.283285; - } - - vec3 rotate(vec2 sc, vec2 v) { - return vec3(v.x * sc.y + v.y * sc.x, v.x * -sc.x + v.y * sc.y, 0.0); - } - - float getShadow(vec4 lightProj) { - vec3 p = lightProj.xyz / lightProj.w; - - float rShadow = SHADOW(SHADOW_TEXEL * vec3(-0.93289, -0.03146, 0.0) + p) + - SHADOW(SHADOW_TEXEL * vec3( 0.81628, -0.05965, 0.0) + p) + - SHADOW(SHADOW_TEXEL * vec3(-0.18455, 0.97225, 0.0) + p) + - SHADOW(SHADOW_TEXEL * vec3( 0.04032, -0.85898, 0.0) + p); - - if (rShadow > 0.1 && rShadow < 3.9) { - float angle = randomAngle(gl_FragCoord.xyz, 15.0); - vec2 sc = vec2(sin(angle), cos(angle)); - - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54316, 0.21186)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.03925, -0.34345)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.07695, 0.40667)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.66378, -0.54068)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2(-0.54130, 0.66730)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.69301, 0.46990)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.37228, 0.03811)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.28597, 0.80228)) + p); - rShadow += SHADOW(SHADOW_TEXEL * rotate(sc, vec2( 0.44801, -0.43844)) + p); - rShadow /= 13.0; - } else - rShadow /= 4.0; - - float fade = clamp(dot(vLightVec, vLightVec), 0.0, 1.0); - - return mix(rShadow, 1.0, fade); - } - - float getShadow() { - return min(dot(vNormal.xyz, vLightVec), vLightProj.w) > 0.0 ? getShadow(vLightProj) : 1.0; - } - - void main() { - float s = getShadow(); - gl_FragColor = vec4(s, s, s, 1.0); - } -#endif -)====" \ No newline at end of file diff --git a/src/shaders/volume.glsl b/src/shaders/volume.glsl deleted file mode 100644 index 10ca26b..0000000 --- a/src/shaders/volume.glsl +++ /dev/null @@ -1,45 +0,0 @@ -R"====( -#ifdef GL_ES - precision lowp int; - precision highp float; -#endif - - -varying vec3 color; - -#ifdef VERTEX - attribute vec4 aCoord; - attribute vec4 aNormal; - attribute vec4 aColor; - - uniform mat4 uViewProj; - uniform vec4 uBasis[2]; // quat rot, pos - uniform vec3 uPosScale; // xyz - scale - uniform vec4 uLightPos; // xyz - pos, w - radius - - vec3 mulQuat(vec4 q, vec3 v) { - return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + v * q.w); - } - - vec3 mulBasis(vec4 rot, vec4 pos, vec3 v) { - return mulQuat(rot, v) + pos.xyz; - } - - void main() { - vec4 c = vec4(mulBasis(uBasis[0], uBasis[1], aCoord.xyz * uPosScale), 1.0); - vec3 n = mulQuat(uBasis[0], normalize(aNormal.xyz)); - vec3 lv = uLightPos.xyz - c.xyz; - - if (dot(lv, n) < 0.0) - c.xyz -= normalize(lv) * uLightPos.w; - - color = aColor.xyz; - - gl_Position = uViewProj * c; - } -#else - void main() { - gl_FragColor = vec4(color, 1.0); - } -#endif -)====" \ No newline at end of file diff --git a/src/ui.h b/src/ui.h index 8d4df2e..84f6379 100644 --- a/src/ui.h +++ b/src/ui.h @@ -6,6 +6,7 @@ namespace UI { IGame *game; + float width; const static uint8 char_width[110] = { 14, 11, 11, 11, 11, 11, 11, 13, 8, 11, 12, 11, 13, 13, 12, 11, 12, 12, 11, 12, 13, 13, 13, 12, @@ -43,17 +44,37 @@ namespace UI { int vCount; } buffer; - void textBegin() { + void begin() { + Core::setDepthTest(false); + Core::setBlending(bmAlpha); + Core::setCulling(cfNone); + game->setupBinding(); + + float aspect = float(Core::width) / float(Core::height); + width = 480 * aspect; + Core::mViewProj = mat4(0.0f, width, 480, 0.0f, 0.0f, 1.0f); + + game->setShader(Core::passGUI, Shader::DEFAULT); + Core::active.shader->setParam(uMaterial, vec4(1)); + Core::active.shader->setParam(uPosScale, vec4(0, 0, 1, 1)); + buffer.iCount = buffer.vCount = 0; } - void textEnd(IGame *game) { + void flush() { if (buffer.iCount > 0) { game->getMesh()->renderBuffer(buffer.indices, buffer.iCount, buffer.vertices, buffer.vCount); buffer.iCount = buffer.vCount = 0; } } + void end() { + flush(); + Core::setCulling(cfFront); + Core::setBlending(bmNone); + Core::setDepthTest(true); + } + void textOut(IGame *game, const vec2 &pos, const char *text, Align align = aLeft, float width = 0) { if (!text) return; @@ -80,7 +101,7 @@ namespace UI { int frame = charRemap(c); if (buffer.iCount == MAX_CHARS * 6) - textEnd(game); + flush(); TR::SpriteTexture &sprite = level->spriteTextures[level->spriteSequences[seq].sStart + frame]; mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, x, y, 0, sprite, 255, true); @@ -137,8 +158,13 @@ namespace UI { Core::setDepthTest(true); } - void renderBar(const vec2 &pos, const vec2 &size, float value) { - // + void renderBar(int type, const vec2 &pos, const vec2 &size, float value) { + MeshBuilder *mesh = game->getMesh(); + + mesh->addFrame(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, pos - 2.0f, size + 4.0f, 0xFF4C504C, 0xFF748474); + mesh->addBar(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, type, pos - 1.0f, size + 2.0f, 0x80000000); + if (value > 0.0f) + mesh->addBar(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, type, pos, vec2(size.x * value, size.y), 0xFFFFFFFF); } }; From 851a15b4188818c38e412a69bbfeb5536369a259 Mon Sep 17 00:00:00 2001 From: XProger Date: Fri, 23 Jun 2017 00:53:29 +0300 Subject: [PATCH 2/7] #23 fix underwater fog --- src/cache.h | 2 +- src/shaders/shader.glsl | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/cache.h b/src/cache.h index 14c0260..ed40536 100644 --- a/src/cache.h +++ b/src/cache.h @@ -11,7 +11,7 @@ #define SHADOW_TEX_SIZE 1024 #define FOG_DIST (18 * 1024) -#define WATER_FOG_DIST (8 * 1024) +#define WATER_FOG_DIST (6 * 1024) //#define WATER_USE_GRID #define UNDERWATER_COLOR "#define UNDERWATER_COLOR vec3(0.6, 0.9, 0.9)\n" diff --git a/src/shaders/shader.glsl b/src/shaders/shader.glsl index 2900fdb..4345cf3 100644 --- a/src/shaders/shader.glsl +++ b/src/shaders/shader.glsl @@ -119,14 +119,17 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords float fog; #ifdef UNDERWATER - float d = abs((vViewVec.w - max(uViewPos.y, uParam.y)) / normalize(vViewVec.xyz).y); - d *= step(0.0, vViewVec.w - uParam.y); + float d; + if (uViewPos.y < uParam.y) + d = abs((coord.y - uParam.y) / normalize(vViewVec.xyz).y); + else + d = length(uViewPos - coord.xyz); fog = d * WATER_FOG_DIST; #else fog = length(vViewVec.xyz); #endif - vNormal.w = 1.0 - clamp(1.0 / exp(fog), 0.0, 1.0); + vNormal.w = clamp(1.0 / exp(fog), 0.0, 1.0); #endif return coord; @@ -457,9 +460,9 @@ varying vec4 vTexCoord; // xy - atlas coords, zw - caustics coords #if defined(PASS_COMPOSE) && !defined(TYPE_FLASH) #ifdef UNDERWATER - color.xyz = mix(color.xyz, UNDERWATER_COLOR * 0.2, vNormal.w); + color.xyz = mix(UNDERWATER_COLOR * 0.2, color.xyz, vNormal.w); #else - color.xyz = mix(color.xyz, vec3(0.0), vNormal.w); + color.xyz = mix(vec3(0.0), color.xyz, vNormal.w); #endif #endif From 238e474f9fef27f800eaaa8f08bea3f157c382f9 Mon Sep 17 00:00:00 2001 From: Pablo Date: Sat, 24 Jun 2017 00:40:10 -0300 Subject: [PATCH 3/7] Fix lack of water effects when using an AMD graphics card under Linux. There were two instances in the code in which the water texture was left uninitialized and this caused the caustics and reflections not to show. Apparently this only happens with the open source AMD driver on Linux. --- src/cache.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cache.h b/src/cache.h index ed40536..ecde068 100644 --- a/src/cache.h +++ b/src/cache.h @@ -410,6 +410,9 @@ struct WaterCache { caustics = new Texture(512, 512, Texture::RGB16, false); mask = new Texture(w, h, Texture::RGB16, false, m, false); delete[] m; + + Core::setTarget(data[0], true); + Core::setTarget(NULL); blank = false; @@ -571,7 +574,7 @@ struct WaterCache { while (item.timer >= SIMULATE_TIMESTEP) { // water step item.data[0]->bind(sDiffuse); - Core::setTarget(item.data[1], 0, true); + Core::setTarget(item.data[1], true); Core::setViewport(0, 0, int(item.size.x * DETAIL * 2.0f + 0.5f), int(item.size.z * DETAIL * 2.0f + 0.5f)); game->getMesh()->renderQuad(); Core::invalidateTarget(false, true); @@ -879,4 +882,4 @@ struct ZoneCache { #undef UNDERWATER_COLOR -#endif \ No newline at end of file +#endif From 8776e04935ab4114b26191d3476d74b912dad6c2 Mon Sep 17 00:00:00 2001 From: XProger Date: Sat, 1 Jul 2017 03:56:32 +0300 Subject: [PATCH 4/7] #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(); From 0ea27ca3d8ab4b5e530db4b64251283e2068f419 Mon Sep 17 00:00:00 2001 From: XProger Date: Sat, 1 Jul 2017 19:21:00 +0300 Subject: [PATCH 5/7] #11 ammo & weapon pickup --- src/inventory.h | 69 +++++++++++++++++++++++++++++++++++-------------- src/ui.h | 29 ++++++++++++++++----- 2 files changed, 72 insertions(+), 26 deletions(-) diff --git a/src/inventory.h b/src/inventory.h index 6f7fda8..2da92fd 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -5,7 +5,7 @@ #include "controller.h" #include "ui.h" -#define INVENTORY_MAX_ITEMS 64 +#define INVENTORY_MAX_ITEMS 32 #define INVENTORY_MAX_RADIUS 688.0f #define INVENTORY_BG_SIZE 512 #define INVENTORY_HEIGHT 2048.0f @@ -138,17 +138,17 @@ struct Inventory { add(TR::Entity::INV_DETAIL); add(TR::Entity::INV_SOUND); add(TR::Entity::INV_CONTROLS); + /* add(TR::Entity::INV_COMPASS); - if (level->extra.inv.map != -1) add(TR::Entity::INV_MAP); if (level->extra.inv.gamma != -1) add(TR::Entity::INV_GAMMA); - + */ add(TR::Entity::INV_PISTOLS, UNLIMITED_AMMO); - add(TR::Entity::INV_SHOTGUN, 999); - add(TR::Entity::INV_MAGNUMS, 999); - add(TR::Entity::INV_UZIS, 10); + add(TR::Entity::INV_SHOTGUN, 10); + add(TR::Entity::INV_MAGNUMS, 10); + add(TR::Entity::INV_UZIS, 50); // add(TR::Entity::INV_MEDIKIT_SMALL, 999); // add(TR::Entity::INV_MEDIKIT_BIG, 999); @@ -214,7 +214,38 @@ struct Inventory { return -1; } + void addAmmo(TR::Entity::Type &type, int &count, int clip, TR::Entity::Type wpnType, TR::Entity::Type ammoType) { + count *= clip; + if (type == wpnType) { + int index = contains(ammoType); + if (index > -1) { + count += items[index]->count; + remove(index); + } + } else { + if (contains(wpnType) > -1) + type = wpnType; + } + } + void add(TR::Entity::Type type, int count = 1) { + type = convToInv(type); + + switch (type) { + case TR::Entity::INV_SHOTGUN : + case TR::Entity::INV_AMMO_SHOTGUN : + addAmmo(type, count, 12, TR::Entity::INV_SHOTGUN, TR::Entity::INV_AMMO_SHOTGUN); + break; + case TR::Entity::INV_MAGNUMS : + case TR::Entity::INV_AMMO_MAGNUMS : + addAmmo(type, count, 50, TR::Entity::INV_MAGNUMS, TR::Entity::INV_AMMO_MAGNUMS); + break; + case TR::Entity::INV_UZIS : + case TR::Entity::INV_AMMO_UZIS : + addAmmo(type, count, 100, TR::Entity::INV_UZIS, TR::Entity::INV_AMMO_UZIS); + break; + } + int i = contains(type); if (i > -1) { items[i]->count += count; @@ -223,8 +254,6 @@ struct Inventory { ASSERT(itemsCount < INVENTORY_MAX_ITEMS); - type = convToInv(type); - int pos = 0; for (int pos = 0; pos < itemsCount; pos++) if (items[pos]->type > type) @@ -246,17 +275,19 @@ struct Inventory { } void remove(TR::Entity::Type type, int count = 1) { - 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--; - } - } + int index = contains(type); + if (index == -1) return; + + items[index]->count -= count; + if (items[index]->count <= 0) + remove(index); + } + + void remove(int index) { + delete items[index]; + for (int i = index; i < itemsCount - 1; i++) + items[i] = items[i + 1]; + itemsCount--; } bool use(TR::Entity::Type item, TR::Entity::Type slot) { diff --git a/src/ui.h b/src/ui.h index 84f6379..907d756 100644 --- a/src/ui.h +++ b/src/ui.h @@ -26,13 +26,22 @@ namespace UI { return c > 10 ? (c > 15 ? char_map[c - 32] : c + 91) : c + 81; } - int getTextWidth(const char *text) { - int width = 0; + vec2 getTextSize(const char *text) { + int x = 0, w = 0, h = 16; - while (char c = *text++) - width += (c == ' ' || c == '_') ? 6 : (char_width[charRemap(c)] + 1); + while (char c = *text++) { + if (c == ' ' || c == '_') { + x += 6; + } else if (c == '@') { + w = max(w, x); + h += 16; + x = 0; + } else + x += char_width[charRemap(c)] + 1; + } + w = max(w, x); - return width - 1; + return vec2(float(w), float(h)); } #define MAX_CHARS DYN_MESH_QUADS @@ -87,10 +96,10 @@ namespace UI { int y = int(pos.y); if (align == aCenter) - x += (int(width) - getTextWidth(text)) / 2; + x += int((width - getTextSize(text).x) / 2); if (align == aRight) - x += int(width) - getTextWidth(text); + x += int(width - getTextSize(text).x); while (char c = *text++) { if (c == ' ' || c == '_') { @@ -98,6 +107,12 @@ namespace UI { continue; } + if (c == '@') { + x = int(pos.x); + y += 16; + continue; + } + int frame = charRemap(c); if (buffer.iCount == MAX_CHARS * 6) From 4364f6d65141bc8f8381c417d012fb1209c07442 Mon Sep 17 00:00:00 2001 From: XProger Date: Sun, 2 Jul 2017 18:59:40 +0300 Subject: [PATCH 6/7] #3 dozy mode, #11 help text, key and puzzle items selector --- bin/OpenLara.exe | Bin 198144 -> 212992 bytes src/controller.h | 3 +- src/format.h | 58 ++++++++++++- src/inventory.h | 215 +++++++++++++++++++++++------------------------ src/lara.h | 114 +++++++++++++++++++------ src/level.h | 19 +++-- src/ui.h | 50 ++++++++++- 7 files changed, 311 insertions(+), 148 deletions(-) diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 11683791574bf6bbc31cc032e95060cad898a88b..eca9d299f1f0f97c59333416ad4a821875fbb2ea 100644 GIT binary patch delta 81430 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ȢF9yQG=JScQm-*yW34c!4996mDBrRoL{N~q(g`)QhM z<%E&^NRb`;MGFE?tWHJI--tERfARwsgst$%7u;<9U45}yDhlZtee#h zmnATPI;r*CjJc~=uAGz`N^reNFT{gav}1?%!*l8el<4f%La-rKe+4ka`2h91^GCxN zzy_OCjSi~&o6UG9rUg!_4d z=G6_`^hi!-3u@GpLa;dXId#I5LPX?nUj|!Hm-6a;<@HqUPYN+nbg-A!w<8oC{9+eq zBlvxGyGua?X&-Jti{vPidhAIdHuQm~7$8~JyAKdw0EiAO7S$vRQ9`&nQiifvq*^2k z!|&&@W*C-oi1Rwm1JJr@6CWjJDtx7L=EU?8)JVS2bof_JO4XJ4#FRx4Kh!PjgcLKh z)$h7&#QXsn)3umti!6jKBpyEn_*0Cn7u@l&Sr4Y$55Y;pUrwu*i?baTp-GV`*5gO& zQvB6ldlP4U9VXck8-+nVN*CI0nWh$3358D99?Ka--P#!CB-^keNV@@9;b7ivxy+R@ zLiI4cZQZ~vMuPborNV}WJ6*N=UttZ4^)W?Q6}ZtEz0bM~5~1G8N(?;^g?4L8KEC1g zgdMlEawdwJUoC`-6F;t9SuF^{lHYG5jgPO5RPL>9)WZsxJ{-pHw{3jqWoo0NC+Ess zszIIao~`|*Mv#P|G)`nf$**uE7UW3I$~H&PeBc{00d>?`Ax5~O&RZ)Ch(3s5=uE*8 z8x6xdQuHy(O|78seOwAK61=*X)woifJF!E_Vq%1#!Y@^|5urR42psTPdsd<=2!=s(uTTjxFZmf7oZ$ ztN0uXKLp-*|9oi0*eBscxtW%)(j)PteI%WF^Xf@2jR}jDG*h?~TS;8@M}sp~A7LjZ zpyYGwEKGz4r8st;6>uL<>eZS%Bs8 zLd1~EV5VGQqXF6oezi6A1Ti#qL2!$9;e>knTcPj7dKOH=$EA7#qNOY?S{{#B(i(>Y zM8W0ii_!)^RDsC(ZAt?UL7UeL_kb$2w50g+gH}u8$+G9<^2>&bzI1_5xml4Ec2stC zb8VpJQ5iNGkE6I&ROu)G0t#{(ea{4D+YU~kRdEpuP?-@Nu6+l9L1kI*V9B39c9c;txh`9Qlx~f6M9y8vRU6h*V7)dBn;Ils6E#dYhg_ zlu|=L3v%7jTVcV8jdqi30|k0k%5^NE<3YsRi#@a5433o3oJ*0DXVvmi1eNJ8OJ2`V zg>f%n37&Yw(MypMKcJ!r3Ozj%`Z)p;kJv?=qFEvrs~1-5HB^Ns(6yBjPg~Ec$s2_6 z&ijvZcx<5|XxDbJPj)jamz!fOg0n7b8xh_`ZjNCkt%l(@XCCOw?6zR^3K|LRQFcvx z1k(WnLBJ6Bk6auOK~gryXOUk<-ks_9p01hdeDzi>9txTGMgKStMPjmuyPh(}*=Ct^XI;K)@At zD4-VvRZdf%ep=`+ysN(Pv~Xw8jz5420podqrgNA2)zd;+-@{Ee2+37_hxrFMLr1Dr zGyhFZd`9RmX8%FWeMTscWIP0-)`FLqQ7T^O_*-~XiaoOOc5UGi^~Yy~r~0k@l8Z&L zD}W2Vc3 zyN`3&rRtMtuua6p5CXWO2IT{-^b05>zeS;bo)+8k1&}AK--%DT?ZJvsa@!+tTTDK2 z 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 X^gylmT;=ZxeP9W%Zw(ICTnYLg$0_?J delta 65986 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

Rt@;j6NK_OMw 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 DOOR_1 && type <= DOOR_6) || type == DOOR_LIFT; } - int isItem() { + bool isItem() { 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 } + bool isKeyHole() { + return type >= KEY_HOLE_1 && type <= KEY_HOLE_2; + } + bool isBlock() { return type >= TR::Entity::BLOCK_1 && type <= TR::Entity::BLOCK_2; } + static Type convToInv(Type type) { + switch (type) { + case PISTOLS : return INV_PISTOLS; + case SHOTGUN : return INV_SHOTGUN; + case MAGNUMS : return INV_MAGNUMS; + case UZIS : return INV_UZIS; + + case AMMO_PISTOLS : return INV_AMMO_PISTOLS; + case AMMO_SHOTGUN : return INV_AMMO_SHOTGUN; + case AMMO_MAGNUMS : return INV_AMMO_MAGNUMS; + case AMMO_UZIS : return INV_AMMO_UZIS; + + case MEDIKIT_SMALL : return INV_MEDIKIT_SMALL; + case MEDIKIT_BIG : return INV_MEDIKIT_BIG; + + case PUZZLE_1 : return INV_PUZZLE_1; + case PUZZLE_2 : return INV_PUZZLE_2; + case PUZZLE_3 : return INV_PUZZLE_3; + case PUZZLE_4 : return INV_PUZZLE_4; + + case KEY_1 : return INV_KEY_1; + case KEY_2 : return INV_KEY_2; + case KEY_3 : return INV_KEY_3; + case KEY_4 : return INV_KEY_4; + + case LEADBAR : return INV_LEADBAR; + //case TR::Entity::SCION : return TR::Entity::INV_SCION; + default : return type; + } + } + + static Type getKeyForHole(Type hole) { + switch (hole) { + case PUZZLE_HOLE_1 : return PUZZLE_1; break; + case PUZZLE_HOLE_2 : return PUZZLE_2; break; + case PUZZLE_HOLE_3 : return PUZZLE_3; break; + case PUZZLE_HOLE_4 : return PUZZLE_4; break; + case KEY_HOLE_1 : return KEY_1; break; + case KEY_HOLE_2 : return KEY_2; break; + case KEY_HOLE_3 : return KEY_3; break; + case KEY_HOLE_4 : return KEY_4; break; + default : return NONE; + } + } + static void fixOpaque(Type type, bool &opaque) { if (type >= LARA && type <= ENEMY_GIANT_MUTANT && type != ENEMY_REX @@ -985,7 +1034,7 @@ namespace TR { struct { int16 muzzleFlash; - int16 puzzleSet; + int16 puzzleDone[4]; int16 weapons[4]; int16 braid; @@ -1230,7 +1279,10 @@ namespace TR { for (int i = 0; i < modelsCount; i++) switch (models[i].type) { case Entity::MUZZLE_FLASH : extra.muzzleFlash = i; break; - case Entity::PUZZLE_DONE_1 : extra.puzzleSet = i; break; + case Entity::PUZZLE_DONE_1 : extra.puzzleDone[0] = i; break; + case Entity::PUZZLE_DONE_2 : extra.puzzleDone[1] = i; break; + case Entity::PUZZLE_DONE_3 : extra.puzzleDone[2] = i; break; + case Entity::PUZZLE_DONE_4 : extra.puzzleDone[3] = i; break; case Entity::LARA_PISTOLS : extra.weapons[0] = i; break; case Entity::LARA_SHOTGUN : extra.weapons[1] = i; break; case Entity::LARA_MAGNUMS : extra.weapons[2] = i; break; diff --git a/src/inventory.h b/src/inventory.h index 2da92fd..1c0897f 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -37,7 +37,7 @@ struct Inventory { struct Desc { const char *name; - int page; + Page page; int model; } desc; @@ -45,42 +45,42 @@ struct Inventory { Item(TR::Level *level, TR::Entity::Type type, int count = 1) : type(type), count(count), angle(0.0f) { switch (type) { - case TR::Entity::INV_PASSPORT : desc = { "Game", 0, level->extra.inv.passport }; break; - case TR::Entity::INV_PASSPORT_CLOSED : desc = { "Game", 0, level->extra.inv.passport_closed }; break; - case TR::Entity::INV_MAP : desc = { "Map", 1, level->extra.inv.map }; break; - case TR::Entity::INV_COMPASS : desc = { "Compass", 1, level->extra.inv.compass }; break; - case TR::Entity::INV_HOME : desc = { "Lara's Home", 0, level->extra.inv.home }; break; - case TR::Entity::INV_DETAIL : desc = { "Detail Levels", 0, level->extra.inv.detail }; break; - case TR::Entity::INV_SOUND : desc = { "Sound", 0, level->extra.inv.sound }; break; - case TR::Entity::INV_CONTROLS : desc = { "Controls", 0, level->extra.inv.controls }; break; - case TR::Entity::INV_GAMMA : desc = { "Gamma", 0, level->extra.inv.gamma }; break; + case TR::Entity::INV_PASSPORT : desc = { "Game", PAGE_OPTION, level->extra.inv.passport }; break; + case TR::Entity::INV_PASSPORT_CLOSED : desc = { "Game", PAGE_OPTION, level->extra.inv.passport_closed }; break; + case TR::Entity::INV_MAP : desc = { "Map", PAGE_INVENTORY, level->extra.inv.map }; break; + case TR::Entity::INV_COMPASS : desc = { "Compass", PAGE_INVENTORY, level->extra.inv.compass }; break; + case TR::Entity::INV_HOME : desc = { "Lara's Home", PAGE_OPTION, level->extra.inv.home }; break; + case TR::Entity::INV_DETAIL : desc = { "Detail Levels", PAGE_OPTION, level->extra.inv.detail }; break; + case TR::Entity::INV_SOUND : desc = { "Sound", PAGE_OPTION, level->extra.inv.sound }; break; + case TR::Entity::INV_CONTROLS : desc = { "Controls", PAGE_OPTION, level->extra.inv.controls }; break; + case TR::Entity::INV_GAMMA : desc = { "Gamma", PAGE_OPTION, level->extra.inv.gamma }; break; - case TR::Entity::INV_PISTOLS : desc = { "Pistols", 1, level->extra.inv.weapon[0] }; break; - case TR::Entity::INV_SHOTGUN : desc = { "Shotgun", 1, level->extra.inv.weapon[1] }; break; - case TR::Entity::INV_MAGNUMS : desc = { "Magnums", 1, level->extra.inv.weapon[2] }; break; - case TR::Entity::INV_UZIS : desc = { "Uzis", 1, level->extra.inv.weapon[3] }; break; + case TR::Entity::INV_PISTOLS : desc = { "Pistols", PAGE_INVENTORY, level->extra.inv.weapon[0] }; break; + case TR::Entity::INV_SHOTGUN : desc = { "Shotgun", PAGE_INVENTORY, level->extra.inv.weapon[1] }; break; + case TR::Entity::INV_MAGNUMS : desc = { "Magnums", PAGE_INVENTORY, level->extra.inv.weapon[2] }; break; + case TR::Entity::INV_UZIS : desc = { "Uzis", PAGE_INVENTORY, level->extra.inv.weapon[3] }; break; - case TR::Entity::INV_AMMO_PISTOLS : desc = { "Pistol Clips", 1, level->extra.inv.ammo[0] }; break; - case TR::Entity::INV_AMMO_SHOTGUN : desc = { "Shotgun Shells", 1, level->extra.inv.ammo[1] }; break; - case TR::Entity::INV_AMMO_MAGNUMS : desc = { "Magnum Clips", 1, level->extra.inv.ammo[2] }; break; - case TR::Entity::INV_AMMO_UZIS : desc = { "Uzi Clips", 1, level->extra.inv.ammo[3] }; break; + case TR::Entity::INV_AMMO_PISTOLS : desc = { "Pistol Clips", PAGE_INVENTORY, level->extra.inv.ammo[0] }; break; + case TR::Entity::INV_AMMO_SHOTGUN : desc = { "Shotgun Shells", PAGE_INVENTORY, level->extra.inv.ammo[1] }; break; + case TR::Entity::INV_AMMO_MAGNUMS : desc = { "Magnum Clips", PAGE_INVENTORY, level->extra.inv.ammo[2] }; break; + case TR::Entity::INV_AMMO_UZIS : desc = { "Uzi Clips", PAGE_INVENTORY, level->extra.inv.ammo[3] }; break; - case TR::Entity::INV_MEDIKIT_SMALL : desc = { "Small Medi Pack", 1, level->extra.inv.medikit[0] }; break; - case TR::Entity::INV_MEDIKIT_BIG : desc = { "Large Medi Pack", 1, level->extra.inv.medikit[1] }; break; + case TR::Entity::INV_MEDIKIT_SMALL : desc = { "Small Medi Pack", PAGE_INVENTORY, level->extra.inv.medikit[0] }; break; + case TR::Entity::INV_MEDIKIT_BIG : desc = { "Large Medi Pack", PAGE_INVENTORY, level->extra.inv.medikit[1] }; break; - case TR::Entity::INV_PUZZLE_1 : desc = { "Puzzle", 2, level->extra.inv.puzzle[0] }; break; - case TR::Entity::INV_PUZZLE_2 : desc = { "Puzzle", 2, level->extra.inv.puzzle[1] }; break; - case TR::Entity::INV_PUZZLE_3 : desc = { "Puzzle", 2, level->extra.inv.puzzle[2] }; break; - case TR::Entity::INV_PUZZLE_4 : desc = { "Puzzle", 2, level->extra.inv.puzzle[3] }; break; - - case TR::Entity::INV_KEY_1 : desc = { "Key", 2, level->extra.inv.key[0] }; break; - case TR::Entity::INV_KEY_2 : desc = { "Key", 2, level->extra.inv.key[1] }; break; - case TR::Entity::INV_KEY_3 : desc = { "Key", 2, level->extra.inv.key[2] }; break; - case TR::Entity::INV_KEY_4 : desc = { "Key", 2, level->extra.inv.key[3] }; break; - - case TR::Entity::INV_LEADBAR : desc = { "Lead Bar", 2, level->extra.inv.leadbar }; break; - case TR::Entity::INV_SCION : desc = { "Scion", 2, level->extra.inv.scion }; break; - default : desc = { "unknown", 2, -1 }; break; + case TR::Entity::INV_PUZZLE_1 : desc = { "Puzzle", PAGE_ITEMS, level->extra.inv.puzzle[0] }; break; + case TR::Entity::INV_PUZZLE_2 : desc = { "Puzzle", PAGE_ITEMS, level->extra.inv.puzzle[1] }; break; + case TR::Entity::INV_PUZZLE_3 : desc = { "Puzzle", PAGE_ITEMS, level->extra.inv.puzzle[2] }; break; + case TR::Entity::INV_PUZZLE_4 : desc = { "Puzzle", PAGE_ITEMS, level->extra.inv.puzzle[3] }; break; + + case TR::Entity::INV_KEY_1 : desc = { "Key", PAGE_ITEMS, level->extra.inv.key[0] }; break; + case TR::Entity::INV_KEY_2 : desc = { "Key", PAGE_ITEMS, level->extra.inv.key[1] }; break; + case TR::Entity::INV_KEY_3 : desc = { "Key", PAGE_ITEMS, level->extra.inv.key[2] }; break; + case TR::Entity::INV_KEY_4 : desc = { "Key", PAGE_ITEMS, level->extra.inv.key[3] }; break; + + case TR::Entity::INV_LEADBAR : desc = { "Lead Bar", PAGE_ITEMS, level->extra.inv.leadbar }; break; + case TR::Entity::INV_SCION : desc = { "Scion", PAGE_ITEMS, level->extra.inv.scion }; break; + default : desc = { "unknown", PAGE_ITEMS, -1 }; break; } if (desc.model > -1) { @@ -132,8 +132,6 @@ struct Inventory { } *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(); - add(TR::Entity::INV_PASSPORT); add(TR::Entity::INV_DETAIL); add(TR::Entity::INV_SOUND); @@ -175,39 +173,8 @@ 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); + type = TR::Entity::convToInv(type); for (int i = 0; i < itemsCount; i++) if (items[i]->type == type) return i; @@ -215,35 +182,38 @@ struct Inventory { } void addAmmo(TR::Entity::Type &type, int &count, int clip, TR::Entity::Type wpnType, TR::Entity::Type ammoType) { - count *= clip; if (type == wpnType) { + count *= clip; int index = contains(ammoType); if (index > -1) { - count += items[index]->count; + count += items[index]->count * clip; remove(index); } } else { - if (contains(wpnType) > -1) + if (contains(wpnType) > -1) { type = wpnType; + count *= clip; + } } } void add(TR::Entity::Type type, int count = 1) { - type = convToInv(type); + type = TR::Entity::convToInv(type); switch (type) { case TR::Entity::INV_SHOTGUN : case TR::Entity::INV_AMMO_SHOTGUN : - addAmmo(type, count, 12, TR::Entity::INV_SHOTGUN, TR::Entity::INV_AMMO_SHOTGUN); + addAmmo(type, count, 2, TR::Entity::INV_SHOTGUN, TR::Entity::INV_AMMO_SHOTGUN); break; case TR::Entity::INV_MAGNUMS : case TR::Entity::INV_AMMO_MAGNUMS : - addAmmo(type, count, 50, TR::Entity::INV_MAGNUMS, TR::Entity::INV_AMMO_MAGNUMS); + addAmmo(type, count, 25, TR::Entity::INV_MAGNUMS, TR::Entity::INV_AMMO_MAGNUMS); break; case TR::Entity::INV_UZIS : case TR::Entity::INV_AMMO_UZIS : - addAmmo(type, count, 100, TR::Entity::INV_UZIS, TR::Entity::INV_AMMO_UZIS); + addAmmo(type, count, 50, TR::Entity::INV_UZIS, TR::Entity::INV_AMMO_UZIS); break; + default : ; } int i = contains(type); @@ -289,33 +259,27 @@ struct Inventory { items[i] = items[i + 1]; itemsCount--; } + + bool chooseKey(TR::Entity::Type hole) { + TR::Entity::Type type = TR::Entity::getKeyForHole(hole); + if (type == TR::Entity::NONE) + return false; + int index = contains(type); + if (index < 0) + return false; + toggle(items[index]->desc.page, type); + return true; + } - bool use(TR::Entity::Type item, TR::Entity::Type slot) { - 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 (getCountPtr(item)) { - remove(item); + bool use(TR::Entity::Type type) { + if (contains(type) > -1) { + remove(type); return true; } - return false; } - bool toggle(Page curPage = PAGE_INVENTORY) { + bool toggle(Page curPage = PAGE_INVENTORY, TR::Entity::Type type = TR::Entity::NONE) { if (phaseRing == 0.0f || phaseRing == 1.0f) { active = !active; vec3 p; @@ -329,6 +293,13 @@ struct Inventory { phasePage = 1.0f; phaseSelect = 1.0f; page = targetPage = curPage; + + if (type != TR::Entity::NONE) { + int i = contains(type); + if (i >= 0) + pageItemIndex[page] = getItemIndex(page, i); + } + index = targetIndex = pageItemIndex[page]; } } @@ -382,7 +353,8 @@ struct Inventory { } void update() { - doPhase(active, 2.0f, phaseRing); + if (phaseChoose == 0.0f) + doPhase(active, 2.0f, phaseRing); doPhase(true, 1.6f, phasePage); doPhase(chosen, 4.0f, phaseChoose); doPhase(true, 2.5f, phaseSelect); @@ -465,13 +437,24 @@ struct Inventory { 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) { - - game->invUse(type, TR::Entity::NONE); - toggle(); + switch (type) { + case TR::Entity::INV_PASSPORT : + case TR::Entity::INV_PASSPORT_CLOSED : + case TR::Entity::INV_MAP : + case TR::Entity::INV_COMPASS : + case TR::Entity::INV_HOME : + case TR::Entity::INV_DETAIL : + case TR::Entity::INV_SOUND : + case TR::Entity::INV_CONTROLS : + case TR::Entity::INV_GAMMA : + case TR::Entity::INV_AMMO_PISTOLS : + case TR::Entity::INV_AMMO_SHOTGUN : + case TR::Entity::INV_AMMO_MAGNUMS : + case TR::Entity::INV_AMMO_UZIS : break; + default : + game->invUse(type); + toggle(); } - } } @@ -518,13 +501,27 @@ struct Inventory { sprintf(buf, "%d %c", item->count, spec); for (int i = 0; buf[i] != ' '; i++) buf[i] -= 47; - UI::textOut(game, pos, buf, UI::aRight, width); + UI::textOut(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); + UI::textOut(vec2(0, 480 - 16), item->desc.name, UI::aCenter, width); renderItemCount(item, vec2(width / 2 - 160, 480 - 96), 320); + + if (phaseChoose == 1.0f) { + if (item->type == TR::Entity::INV_PASSPORT || + item->type == TR::Entity::INV_MAP || + item->type == TR::Entity::INV_COMPASS || + item->type == TR::Entity::INV_HOME || + item->type == TR::Entity::INV_DETAIL || + item->type == TR::Entity::INV_SOUND || + item->type == TR::Entity::INV_CONTROLS || + item->type == TR::Entity::INV_GAMMA) + { + UI::textOut(vec2(0, 240), "Not implemented yet!", UI::aCenter, width); + } + } } void renderPage(int page) { @@ -632,16 +629,16 @@ struct Inventory { static const char* pageTitle[PAGE_MAX] = { "OPTION", "INVENTORY", "ITEMS" }; - UI::textOut(game, vec2( 0, 32), pageTitle[page], UI::aCenter, UI::width); + UI::textOut(vec2( 0, 32), pageTitle[page], UI::aCenter, UI::width); if (page < PAGE_ITEMS && getItemsCount(page + 1)) { - UI::textOut(game, vec2(16, 32), "\x5B", UI::aLeft, UI::width); - UI::textOut(game, vec2( 0, 32), "\x5B", UI::aRight, UI::width - 20); + UI::textOut(vec2(16, 32), "[", UI::aLeft, UI::width); + UI::textOut(vec2( 0, 32), "[", UI::aRight, UI::width - 20); } if (page > PAGE_OPTION && getItemsCount(page - 1)) { - UI::textOut(game, vec2(16, 480 - 16), "\x5D", UI::aLeft, UI::width); - UI::textOut(game, vec2(0, 480 - 16), "\x5D", UI::aRight, UI::width - 20); + UI::textOut(vec2(16, 480 - 16), "]", UI::aLeft, UI::width); + UI::textOut(vec2(0, 480 - 16), "]", UI::aRight, UI::width - 20); } if (index == targetIndex) diff --git a/src/lara.h b/src/lara.h index 0ed9c93..a80352a 100644 --- a/src/lara.h +++ b/src/lara.h @@ -192,6 +192,7 @@ struct Lara : Character { }; bool home; + bool dozy; struct Weapon { enum Type { EMPTY = -1, PISTOLS, SHOTGUN, MAGNUMS, UZIS, MAX }; @@ -216,7 +217,10 @@ struct Lara : Character { ActionCommand actionList[MAX_TRIGGER_ACTIONS]; - int lastPickUp; + TR::Entity::Type usedKey; + TR::Entity *puzzleEntity; + TR::Entity *pickupEntity; + int viewTarget; int roomPrev; // water out from room vec2 rotFactor; @@ -388,7 +392,7 @@ struct Lara : Character { } *braid; - Lara(IGame *game, int entity, bool home) : Character(game, entity, LARA_MAX_HEALTH), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1), braid(NULL) { + Lara(IGame *game, int entity, bool home) : Character(game, entity, LARA_MAX_HEALTH), home(home), dozy(false), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1), braid(NULL) { if (getEntity().type == TR::Entity::LARA) { if (getRoom().flags.water) @@ -437,7 +441,7 @@ struct Lara : Character { //reset(5, vec3(38643, -3072, 92370), PI * 0.5f); // level 3a (gears) //reset(43, vec3(64037, 6656, 48229), PI); // level 3b (movingblock) //reset(0, vec3(40913, -1012, 42252), PI); // level 8c - //reset(10, vec3(90443, 11264 - 256, 114614), PI, true); // villa mortal 2 + //reset(10, vec3(90443, 11264 - 256, 114614), PI, STAND_ONWATER); // villa mortal 2 #endif chestOffset = animation.getJoints(getMatrix(), 7).pos; } @@ -461,7 +465,7 @@ struct Lara : Character { return TR::NO_ROOM; } - void reset(int room, const vec3 &pos, float angle, bool onwater = false) { + void reset(int room, const vec3 &pos, float angle, Stand forceStand = STAND_GROUND) { if (room == TR::NO_ROOM) { stand = STAND_AIR; room = getRoomByPos(pos); @@ -480,10 +484,16 @@ struct Lara : Character { getEntity().room = room; this->pos = pos; this->angle = vec3(0.0f, angle, 0.0f); - if (onwater) { - stand = STAND_ONWATER; - animation.setAnim(ANIM_TO_ONWATER); + + if (forceStand != STAND_GROUND) { + stand = forceStand; + switch (stand) { + case STAND_ONWATER : animation.setAnim(ANIM_TO_ONWATER); break; + case STAND_UNDERWATER : animation.setAnim(ANIM_UNDERWATER); break; + default : ; + } } + updateEntity(); updateLights(false); } @@ -605,6 +615,8 @@ struct Lara : Character { } bool canDrawWeapon() { + if (dozy) return true; + return wpnCurrent != Weapon::EMPTY && emptyHands() && animation.index != ANIM_CLIMB_3 @@ -1255,6 +1267,8 @@ struct Lara : Character { } virtual void hit(float damage, Controller *enemy = NULL) { + if (dozy) return; + damageTime = LARA_DAMAGE_TIME; Character::hit(damage, enemy); @@ -1273,7 +1287,7 @@ struct Lara : Character { Core::lightColor[1 + 0] = Core::lightColor[1 + 1] = vec4(0, 0, 0, 1); }; - void useItem(TR::Entity::Type item) { + bool useItem(TR::Entity::Type item) { switch (item) { case TR::Entity::INV_PISTOLS : wpnChange(Lara::Weapon::PISTOLS); break; case TR::Entity::INV_SHOTGUN : wpnChange(Lara::Weapon::SHOTGUN); break; @@ -1285,8 +1299,21 @@ struct Lara : Character { health = min(LARA_MAX_HEALTH, health + (item == TR::Entity::INV_MEDIKIT_SMALL ? LARA_MAX_HEALTH / 2 : LARA_MAX_HEALTH)); playSound(TR::SND_HEALTH, pos, Sound::PAN); break; - default : ; + case TR::Entity::INV_PUZZLE_1 : + case TR::Entity::INV_PUZZLE_2 : + case TR::Entity::INV_PUZZLE_3 : + case TR::Entity::INV_PUZZLE_4 : + case TR::Entity::INV_KEY_1 : + case TR::Entity::INV_KEY_2 : + case TR::Entity::INV_KEY_3 : + case TR::Entity::INV_KEY_4 : + if (usedKey == item) + return false; + usedKey = item; + break; + default : return false; } + return true; } bool waterOut() { @@ -1347,7 +1374,7 @@ struct Lara : Character { if (stand == STAND_UNDERWATER) angle.x = -25 * DEG2RAD; - lastPickUp = i; + pickupEntity = &item; return true; } } @@ -1422,19 +1449,33 @@ struct Lara : Character { } break; case TR::Level::Trigger::KEY : - if (level->entities[info.trigCmd[0].args].flags.active) + if (level->entities[info.trigCmd[0].args].flags.active || state != STATE_STOP) return; - actionState = level->entities[info.trigCmd[0].args].type == TR::Entity::KEY_HOLE_1 ? STATE_USE_KEY : STATE_USE_PUZZLE; + + actionState = level->entities[info.trigCmd[0].args].isKeyHole() ? STATE_USE_KEY : STATE_USE_PUZZLE; if (!animation.canSetState(actionState)) return; - limit = actionState == STATE_USE_KEY ? &TR::Limits::KEY_HOLE : &TR::Limits::PUZZLE_HOLE; - if (!checkInteraction((Controller*)level->entities[info.trigCmd[0].args].controller, *limit, isPressed(ACTION))) - return; - if (!game->invUse(TR::Entity::NONE, level->entities[info.trigCmd[0].args].type)) { - playSound(TR::SND_NO, pos, Sound::PAN); + + limit = actionState == STATE_USE_PUZZLE ? &TR::Limits::PUZZLE_HOLE : &TR::Limits::KEY_HOLE; + if (!checkInteraction((Controller*)level->entities[info.trigCmd[0].args].controller, *limit, isPressed(ACTION) || usedKey != TR::Entity::NONE)) + return; + + if (!animation.canSetState(actionState)) + return; + + if (usedKey == TR::Entity::NONE) { + if (isPressed(ACTION) && !game->invChooseKey(level->entities[info.trigCmd[0].args].type)) + playSound(TR::SND_NO, pos, Sound::PAN); // no compatible items in inventory return; } - lastPickUp = info.trigCmd[0].args; // TODO: it's not pickup, it's key/puzzle hole + + if (TR::Entity::convToInv(TR::Entity::getKeyForHole(level->entities[info.trigCmd[0].args].type)) != usedKey) { // check compatibility if user select other + playSound(TR::SND_NO, pos, Sound::PAN); // uncompatible item + return; + } + + puzzleEntity = actionState == STATE_USE_PUZZLE ? &level->entities[info.trigCmd[0].args] : NULL; + game->invUse(usedKey); break; case TR::Level::Trigger::PICKUP : if (!isActive) // check if item is not picked up @@ -1498,6 +1539,8 @@ struct Lara : Character { } virtual Stand getStand() { + if (dozy) return STAND_UNDERWATER; + if (state == STATE_HANG || state == STATE_HANG_LEFT || state == STATE_HANG_RIGHT) { if (input & ACTION) return STAND_HANG; @@ -1921,6 +1964,19 @@ struct Lara : Character { virtual int getInput() { // TODO: updateInput if (level->cutEntity > -1) return 0; + input = 0; + + if (!dozy && Input::state[cAction] && Input::state[cJump] && Input::state[cLook] && Input::state[cStepRight]) { + dozy = true; + reset(getRoomIndex(), pos - vec3(0, 512, 0), angle.y, STAND_UNDERWATER); + return input; + } + + if (dozy && Input::state[cWalk]) { + dozy = false; + return input; + } + input = Character::getInput(); if (input & DEATH) return input; @@ -1969,20 +2025,22 @@ struct Lara : Character { virtual void doCustomCommand(int curFrame, int prevFrame) { switch (state) { case STATE_PICK_UP : { - TR::Entity &item = level->entities[lastPickUp]; - if (!item.flags.invisible) { + if (pickupEntity && !pickupEntity->flags.invisible) { int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER; if (animation.isFrameActive(pickupFrame)) { - item.flags.invisible = true; - game->invAdd(item.type, 1); + pickupEntity->flags.invisible = true; + game->invAdd(pickupEntity->type, 1); + pickupEntity = NULL; } } break; } case STATE_USE_PUZZLE : { - TR::Entity &item = level->entities[lastPickUp]; - if (animation.isFrameActive(PUZZLE_FRAME)) - ((Controller*)item.controller)->meshSwap(0, level->extra.puzzleSet); + if (puzzleEntity && animation.isFrameActive(PUZZLE_FRAME)) { + int doneIdx = TR::Entity::convToInv(TR::Entity::getKeyForHole(puzzleEntity->type)) - TR::Entity::INV_PUZZLE_1; + ((Controller*)puzzleEntity->controller)->meshSwap(0, level->extra.puzzleDone[doneIdx]); + puzzleEntity = NULL; + } break; } } @@ -1994,7 +2052,7 @@ struct Lara : Character { if (damageTime > 0.0f) damageTime = max(0.0f, damageTime - Core::deltaTime); - if (stand == STAND_UNDERWATER) { + if (stand == STAND_UNDERWATER && !dozy) { if (oxygen > 0.0f) oxygen -= Core::deltaTime; else @@ -2002,6 +2060,8 @@ struct Lara : Character { } else if (oxygen < LARA_MAX_OXYGEN) oxygen = min(LARA_MAX_OXYGEN, oxygen += Core::deltaTime * 10.0f); + + usedKey = TR::Entity::NONE; } virtual void updateAnimation(bool commands) { @@ -2367,7 +2427,9 @@ struct Lara : Character { } updateEntity(); + if (dozy) stand = STAND_GROUND; checkRoom(); + if (dozy) stand = STAND_UNDERWATER; } virtual void applyFlow(TR::Camera &sink) { diff --git a/src/level.h b/src/level.h index 37cfc5b..4253331 100644 --- a/src/level.h +++ b/src/level.h @@ -127,9 +127,10 @@ struct Level : IGame { camera->shake = time; } - virtual bool invUse(TR::Entity::Type item, TR::Entity::Type slot) { - lara->useItem(item); - return inventory.use(item, slot); + virtual bool invUse(TR::Entity::Type type) { + if (!lara->useItem(type)) + return inventory.use(type); + return true; } virtual void invAdd(TR::Entity::Type type, int count) { @@ -140,6 +141,10 @@ struct Level : IGame { return inventory.getCountPtr(type); } + virtual bool invChooseKey(TR::Entity::Type hole) { + return inventory.chooseKey(hole); + } + 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; @@ -636,8 +641,8 @@ struct Level : IGame { void update() { #ifdef LEVEL_EDITOR - if (Input::down[ikCtrl]) { - Input::down[ikCtrl] = false; + if (Input::down[ik0]) { + Input::down[ik0] = false; lara->reset(TR::NO_ROOM, camera->pos, camera->angle.y, false); } #endif @@ -993,11 +998,13 @@ struct Level : IGame { } } - if (lara->stand == Lara::STAND_ONWATER || lara->stand == Character::STAND_UNDERWATER) + if (!lara->dozy && (lara->stand == Lara::STAND_ONWATER || lara->stand == Character::STAND_UNDERWATER)) UI::renderBar(1, vec2(32, 32), size, oxygen); inventory.renderUI(); + UI::renderHelp(); + UI::end(); } diff --git a/src/ui.h b/src/ui.h index 907d756..d38f24f 100644 --- a/src/ui.h +++ b/src/ui.h @@ -7,6 +7,8 @@ namespace UI { IGame *game; float width; + float helpTipTime; + bool showHelp; const static uint8 char_width[110] = { 14, 11, 11, 11, 11, 11, 11, 13, 8, 11, 12, 11, 13, 13, 12, 11, 12, 12, 11, 12, 13, 13, 13, 12, @@ -84,7 +86,7 @@ namespace UI { Core::setDepthTest(true); } - void textOut(IGame *game, const vec2 &pos, const char *text, Align align = aLeft, float width = 0) { + void textOut(const vec2 &pos, const char *text, Align align = aLeft, float width = 0) { if (!text) return; TR::Level *level = game->getLevel(); @@ -101,6 +103,8 @@ namespace UI { if (align == aRight) x += int(width - getTextSize(text).x); + int left = x; + while (char c = *text++) { if (c == ' ' || c == '_') { x += 6; @@ -108,7 +112,7 @@ namespace UI { } if (c == '@') { - x = int(pos.x); + x = left; y += 16; continue; } @@ -130,10 +134,18 @@ namespace UI { void init(IGame *game) { UI::game = game; + showHelp = false; + helpTipTime = 5.0f; } void update() { - // + if (Input::down[ikH]) { + Input::down[ikH] = false; + showHelp = !showHelp; + helpTipTime = 0.0f; + } + if (helpTipTime > 0.0f) + helpTipTime -= Core::deltaTime; } void renderControl(const vec2 &pos, float size, bool active) { @@ -181,6 +193,38 @@ namespace UI { if (value > 0.0f) mesh->addBar(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, type, pos, vec2(size.x * value, size.y), 0xFFFFFFFF); } + +const char *helpText = \ +"Controls gamepad, touch and keyboard:@"\ +" H - Show or hide this help@"\ +" TAB - Inventory@"\ +" LEFT - Left@"\ +" RIGHT - Right@"\ +" UP - Run@"\ +" DOWN - Back@"\ +" SHIFT - Walk@"\ +" SPACE - Draw Weapon@"\ +" CTRL - Action@"\ +" ALT - Jump@"\ +" Z - Step Left@"\ +" X - Step Right@"\ +" A - Roll@"\ +" C - Look # not implemented #@"\ +" V - First Person View@@" +"Actions:@"\ +" Out of water - Run + Action@"\ +" Handstand - Run + Walk@"\ +" Swan dive - Run + Walk + jump@"\ +" DOZY on - Look + Step Right + Action + Jump@"\ +" DOZY off - Walk@"; + + void renderHelp() { + if (showHelp) + textOut(vec2(0, 64), helpText, aRight, width - 32); + else + if (helpTipTime > 0.0f) + textOut(vec2(0, 480 - 32), "Press H for help", aCenter, width); + } }; #endif \ No newline at end of file From ec35c44a77940fe35c62b5aa06cca3beabf5aa9a Mon Sep 17 00:00:00 2001 From: XProger Date: Sun, 2 Jul 2017 19:07:42 +0300 Subject: [PATCH 7/7] #11 addition of help info; #15 remove controls info from web page --- bin/OpenLara.exe | Bin 212992 -> 212992 bytes src/platform/web/index.html | 7 +------ src/ui.h | 5 ++++- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index eca9d299f1f0f97c59333416ad4a821875fbb2ea..0b255ee9d8409ba94299c3e7fe53a7c654d554f1 100644 GIT binary patch delta 13354 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 (.PHD, .PSX)

- OpenLara on github & facebook

- controls:
- keyboad: move - arrow keys, jump - Alt, action - Ctrl, draw weapon - Space, inventory - Tab, walk - Shift, side steps - ZX, change view - V)
- gamepad: PSX controls for Xbox controller
- Time Control: R - slow motion, T - fast motion
- FullScreen: Alt + Enter + OpenLara on github & facebook

diff --git a/src/ui.h b/src/ui.h index d38f24f..323152e 100644 --- a/src/ui.h +++ b/src/ui.h @@ -210,7 +210,10 @@ const char *helpText = \ " X - Step Right@"\ " A - Roll@"\ " C - Look # not implemented #@"\ -" V - First Person View@@" +" V - First Person View@" +" R - slow motion@"\ +" T - fast motion@"\ +" ALT + ENTER - Fullscreen@@"\ "Actions:@"\ " Out of water - Run + Action@"\ " Handstand - Run + Walk@"\