From 64438c3ec11caf7ba2b3c31f11c5537817c23640 Mon Sep 17 00:00:00 2001 From: XProger Date: Tue, 13 Mar 2018 10:14:52 +0300 Subject: [PATCH] #23 fix water; #14 crocodile basic logic --- src/cache.h | 6 +- src/character.h | 10 ++ src/controller.h | 12 ++- src/enemy.h | 232 +++++++++++++++++++++++++++++++++++++++-- src/format.h | 1 - src/lara.h | 3 + src/level.h | 26 +++-- src/shaders/water.glsl | 19 ++-- 8 files changed, 273 insertions(+), 36 deletions(-) diff --git a/src/cache.h b/src/cache.h index 5bd245b..9354b58 100644 --- a/src/cache.h +++ b/src/cache.h @@ -328,7 +328,7 @@ struct AmbientCache { bool needFlip = task.flip != level->state.flags.flipped; - if (needFlip) game->flipMap(); + if (needFlip) game->flipMap(false); int sector = task.sector; if (task.flip) { @@ -337,7 +337,7 @@ struct AmbientCache { } renderAmbient(task.room, sector, &task.cube->colors[0]); - if (needFlip) game->flipMap(); + if (needFlip) game->flipMap(false); task.cube->status = Cube::READY; } @@ -628,7 +628,7 @@ struct WaterCache { vec3 p; p.x = (drop.pos.x - (item.pos.x - item.size.x)) * DETAIL; p.z = (drop.pos.z - (item.pos.z - item.size.z)) * DETAIL; - Core::active.shader->setParam(uParam, vec4(p.x, p.z, drop.radius * DETAIL, drop.strength)); + Core::active.shader->setParam(uParam, vec4(p.x, p.z, drop.radius * DETAIL, -drop.strength)); item.data[0]->bind(sDiffuse); Core::setTarget(item.data[1], CLEAR_ALL); diff --git a/src/character.h b/src/character.h index f74aa5a..8c2cd7c 100644 --- a/src/character.h +++ b/src/character.h @@ -83,6 +83,16 @@ struct Character : Controller { return index; } + virtual TR::Room& getLightRoom() { + if (stand == STAND_ONWATER) { + int16 rIndex = getRoomIndex(); + TR::Room::Sector *sector = level->getSector(rIndex, pos); + if (sector && sector->roomAbove != TR::NO_ROOM) + return level->rooms[sector->roomAbove]; + } + return Controller::getLightRoom(); + } + bool updateZone() { if (level->isCutsceneLevel()) return false; diff --git a/src/controller.h b/src/controller.h index 1011185..bc3d15c 100644 --- a/src/controller.h +++ b/src/controller.h @@ -64,7 +64,7 @@ struct IGame { virtual bool isCutscene() { return false; } virtual uint16 getRandomBox(uint16 zone, uint16 *zones) { return 0; } virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) { return 0; } - virtual void flipMap() {} + virtual void flipMap(bool water = true) {} virtual void setClipParams(float clipSign, float clipHeight) {} virtual void setWaterParams(float height) {} virtual void waterDrop(const vec3 &pos, float radius, float strength) {} @@ -1085,13 +1085,17 @@ struct Controller { updateExplosion(); updateLights(true); } + + virtual TR::Room& getLightRoom() { + return getRoom(); + } void updateLights(bool lerp = true) { TR::Room::Light sunLight; - if (getModel()) { - const TR::Room &room = getRoom(); + const TR::Room &room = getLightRoom(); + if (getModel()) { vec3 center = getBoundingBox().center(); float maxAtt = 0.0f; /* @@ -1131,7 +1135,7 @@ struct Controller { vec4 tcolor = vec4(vec3(targetLight->color.r, targetLight->color.g, targetLight->color.b) * (1.0f / 255.0f), float(targetLight->radius)); if (mainLightFlip != level->state.flags.flipped) { - if (getRoom().alternateRoom > -1) + if (room.alternateRoom > -1) lerp = false; mainLightFlip = level->state.flags.flipped; } diff --git a/src/enemy.h b/src/enemy.h index 83c505a..38a57c5 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -648,7 +648,7 @@ struct Lion : Enemy { STATE_BITE , }; - Lion(IGame *game, int entity) : Enemy(game, entity, 6, 341, 375.0f, 0.25f) { + Lion(IGame *game, int entity) : Enemy(game, entity, 6, 341, 400.0f, 0.25f) { dropHeight = -1024; jointChest = 19; jointHead = 20; @@ -756,15 +756,16 @@ struct Lion : Enemy { struct Rat : Enemy { enum { - HIT_MASK = 0x300018F, + HIT_MASK = 0x0300018F, }; enum { - ANIM_GROUND_DEATH = 8, - ANIM_WATER_DEATH = 2, + ANIM_DEATH_LAND = 8, + ANIM_DEATH_WATER = 2, }; enum { + // land STATE_NONE , STATE_STOP , STATE_ATTACK , @@ -772,8 +773,9 @@ struct Rat : Enemy { STATE_BITE , STATE_DEATH , STATE_WAIT , + // water STATE_WATER_SWIM = 1, - STATE_WATER_ATTACK , + STATE_WATER_BITE , STATE_WATER_DEATH , }; @@ -889,9 +891,9 @@ struct Rat : Enemy { switch (state) { case STATE_WATER_SWIM : if (targetInView && (collide(target) & HIT_MASK)) - return STATE_WATER_ATTACK; + return STATE_WATER_BITE; break; - case STATE_WATER_ATTACK : + case STATE_WATER_BITE : if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) { game->waterDrop(getJoint(jointHead).pos, 256.0f, 0.2f); bite(getJoint(jointHead).pos, RAT_DAMAGE); @@ -926,10 +928,224 @@ struct Rat : Enemy { bool water = getRoom().flags.water; if ((water && state == STATE_WATER_DEATH) || (!water && state == STATE_DEATH)) return state; - return animation.setAnim(water ? ANIM_WATER_DEATH : ANIM_GROUND_DEATH); + return animation.setAnim(water ? ANIM_DEATH_WATER : ANIM_DEATH_LAND); } }; + +#define CROCODILE_TURN_SLOW (DEG2RAD * 90) +#define CROCODILE_TURN_FAST (DEG2RAD * 180) +#define CROCODILE_DIST_BITE 435.0f +#define CROCODILE_DIST_TURN (1024 * 3) +#define CROCODILE_LIFT_SPEED 960.0f +#define CROCODILE_DAMAGE 25 + +struct Crocodile : Enemy { + + enum { + HIT_MASK = 0x000003FC, + }; + + enum { + ANIM_DEATH_LAND = 11, + ANIM_DEATH_WATER = 4, + }; + + enum { + // land + STATE_NONE , + STATE_STOP , + STATE_RUN , + STATE_WALK , + STATE_TURN , + STATE_BITE , + STATE_UNUSED , + STATE_DEATH , + // water + STATE_WATER_SWIM = 1, + STATE_WATER_BITE , + STATE_WATER_DEATH , + }; + + int modelLand, modelWater; + + Crocodile(IGame *game, int entity) : Enemy(game, entity, 20, 341, 600.0f, 0.25f) { + jointChest = 1; + jointHead = 8; + + modelLand = level->getModelIndex(TR::Entity::ENEMY_CROCODILE_LAND) - 1; + modelWater = level->getModelIndex(TR::Entity::ENEMY_CROCODILE_WATER) - 1; + + bool water = getRoom().flags.water; + flying = water; + stand = water ? STAND_UNDERWATER : STAND_GROUND; + } + + const virtual TR::Model* getModel() { + bool water = getRoom().flags.water; + int modelIndex = water ? modelWater : modelLand; + + ASSERT(modelIndex > -1); + const TR::Model *model = &level->models[modelIndex]; + if (animation.model != model) { + targetBox = -1; + animation.setModel(model); + stand = water ? STAND_UNDERWATER : STAND_GROUND; + flying = water; + + int16 rIndex = getRoomIndex(); + if (water) { + TR::Room::Sector *sector = level->getWaterLevelSector(rIndex, pos); + if (sector) { + pos.y = float(sector->ceiling * 256); + roomIndex = rIndex; + } + } else { + int16 rIndex = getRoomIndex(); + TR::Room::Sector *sector = level->getSector(rIndex, pos); + if (sector) { + pos.y = float(sector->floor * 256); + roomIndex = rIndex; + } + } + + nextState = STATE_NONE; + state = STATE_NONE; + + if (health <= 0.0f) { + getStateDeath(); + animation.goEnd(false); + } + + updateZone(); + } + return animation.model; + } + + virtual int getStateGround() { + if (!think(true)) + return state; + + float angle; + getTargetInfo(0, NULL, NULL, &angle, NULL); + + if (nextState == state) + nextState = STATE_NONE; + + bool isBite = targetInView && fabsf(target->pos.y - pos.y) < 256.0f; + + switch (state) { + case STATE_STOP : + if (isBite && targetDist < CROCODILE_DIST_BITE) + return STATE_BITE; + switch (mood) { + case MOOD_ESCAPE : return STATE_RUN; + case MOOD_ATTACK : return STATE_RUN; // TODO: turn + case MOOD_STALK : return STATE_WALK; + default : return state; + } + case STATE_RUN : + if (targetInView && (collide(target) & HIT_MASK)) + return STATE_STOP; + switch (mood) { + case MOOD_SLEEP : return STATE_STOP; + case MOOD_STALK : return STATE_WALK; + case MOOD_ATTACK : if (targetDist < CROCODILE_DIST_TURN) return STATE_STOP; // TODO: check turn angles + default : return state; + } + case STATE_WALK : + if (targetInView && (collide(target) & HIT_MASK)) + return STATE_STOP; + switch (mood) { + case MOOD_SLEEP : return STATE_STOP; + case MOOD_ATTACK : + case MOOD_ESCAPE : return STATE_RUN; + default : return state; + } + case STATE_TURN : // TODO turn + return STATE_WALK; + case STATE_BITE : + if (nextState == STATE_NONE) { + bite(getJoint(jointHead).pos, CROCODILE_DAMAGE); + nextState = STATE_STOP; + } + break; + default : ; + } + + return state; + } + + virtual int getStateUnderwater() { + if (!think(false)) + return state; + + float angle; + getTargetInfo(0, NULL, NULL, &angle, NULL); + + if (nextState == state) + nextState = STATE_NONE; + + if (animation.frameIndex % 4 == 0) + game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f); + + switch (state) { + case STATE_WATER_SWIM : + if (targetInView && collide(target)) + return STATE_WATER_BITE; + break; + case STATE_WATER_BITE : + if (collide(target)) { + if (nextState != STATE_NONE) + return state; + bite(getJoint(jointHead).pos, CROCODILE_DAMAGE); + nextState = STATE_WATER_SWIM; + } + return STATE_WATER_SWIM; + default : ; + } + + return state; + } + + virtual void updatePosition() { + float angleY = 0.0f; + + if ((stand == STAND_GROUND && (state == STATE_RUN || state == STATE_WALK)) || (stand == STAND_UNDERWATER && state == STATE_WATER_SWIM)) + getTargetInfo(0, NULL, NULL, &angleY, NULL); + + turn(angleY, RAT_TURN_FAST); + + if (state == STATE_DEATH) { + animation.overrideMask = 0; + return; + } + + if (flying) { + lift(waypoint.y - pos.y, CROCODILE_LIFT_SPEED); + int16 rIndex = getRoomIndex(); + TR::Room::Sector *sector = level->getWaterLevelSector(rIndex, pos); + if (sector) { + float waterLevel = float(sector->ceiling * 256) + 256; + if (pos.y < waterLevel) + pos.y = waterLevel; + } + } + + Enemy::updatePosition(); + setOverrides(state != STATE_DEATH, jointChest, jointHead); + lookAt(target); + } + + virtual int getStateDeath() { + bool water = getRoom().flags.water; + if ((water && state == STATE_WATER_DEATH) || (!water && state == STATE_DEATH)) + return state; + return animation.setAnim(water ? ANIM_DEATH_WATER : ANIM_DEATH_LAND); + } +}; + + #define BEAR_DIST_EAT 768 #define BEAR_DIST_HOWL 2048 #define BEAR_DIST_BITE 1024 diff --git a/src/format.h b/src/format.h index 76426ee..7cffaff 100644 --- a/src/format.h +++ b/src/format.h @@ -3762,7 +3762,6 @@ namespace TR { if (type == spriteSequences[i].type) return -(i + 1); - ASSERT(false); return 0; } diff --git a/src/lara.h b/src/lara.h index 1cf2bff..74cc1dd 100644 --- a/src/lara.h +++ b/src/lara.h @@ -2590,6 +2590,9 @@ struct Lara : Character { // scion debug (TODO: remove) if (Input::down[ikP]) { switch (level->id) { + case TR::LVL_TR1_GYM : + reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) + break; case TR::LVL_TR1_2 : reset(61, vec3(21987, -1024, 29144), PI * 3.0f * 0.5f); // level 2 (trap door) break; diff --git a/src/level.h b/src/level.h index 09d4b60..f234c3e 100644 --- a/src/level.h +++ b/src/level.h @@ -330,9 +330,9 @@ struct Level : IGame { } } - virtual void flipMap() { + virtual void flipMap(bool water = true) { updateBlocks(false); - if (waterCache) waterCache->flipMap(); + if (water && waterCache) waterCache->flipMap(); mesh->flipMap(); level.flipMap(); updateBlocks(true); @@ -372,18 +372,24 @@ struct Level : IGame { Core::lightColor[3] = vec4(0, 0, 0, 1); } - if (room.flags.water) - setWaterParams(float(room.info.yTop)); - else - setWaterParams(NO_CLIP_PLANE); - setShader(Core::pass, type, room.flags.water, alphaTest); if (room.flags.water) { if (waterCache) waterCache->bindCaustics(roomIndex); - setWaterParams(float(room.info.yTop)); - } + + if (room.waterLevel == -1) { // TODO: precalculate + int16 rIndex = roomIndex; + TR::Room::Sector *sector = level.getWaterLevelSector(rIndex, room.getOffset() + vec3(512, 0, 512)); + if (sector) + setWaterParams(float(sector->ceiling * 256)); + else + setWaterParams(float(room.info.yTop)); + } else + setWaterParams(float(room.waterLevel)); + + } else + setWaterParams(NO_CLIP_PLANE); Core::active.shader->setParam(uParam, Core::params); Core::setMaterial(diffuse, ambient, specular, alpha); @@ -760,7 +766,7 @@ struct Level : IGame { case TR::Entity::ENEMY_CENTAUR : return new Centaur(this, index); case TR::Entity::ENEMY_MUMMY : return new Mummy(this, index); case TR::Entity::ENEMY_CROCODILE_LAND : - case TR::Entity::ENEMY_CROCODILE_WATER : + case TR::Entity::ENEMY_CROCODILE_WATER : return new Crocodile(this, index); case TR::Entity::ENEMY_GORILLA : return new Enemy(this, index, 100, 10, 0.0f, 0.0f); case TR::Entity::ENEMY_LARSON : return new Larson(this, index); case TR::Entity::ENEMY_PIERRE : return new Pierre(this, index); diff --git a/src/shaders/water.glsl b/src/shaders/water.glsl index 28b1b2f..d50793f 100644 --- a/src/shaders/water.glsl +++ b/src/shaders/water.glsl @@ -83,9 +83,9 @@ uniform sampler2D sNormal; #define PI 3.141592653589793 - float calcFresnel(float NdotL, float fbias, float fpow) { - float f = 1.0 - abs(NdotL); - return clamp(fbias + (1.0 - fbias) * pow(f, fpow), 0.0, 1.0); + float calcFresnel(float NdotL, float fbias) { + float f = 1.0 - NdotL; + return clamp(fbias + (1.0 - fbias) * (f * f), 0.0, 1.0); } vec3 applyFog(vec3 color, vec3 fogColor, float factor) { @@ -132,7 +132,7 @@ uniform sampler2D sNormal; } float h(vec2 tc) { - return simplex_noise(vec3(tc * 16.0, uParam.w)) * 0.001; + return simplex_noise(vec3(tc * 16.0, uParam.w)) * 0.0005; } vec4 calc() { @@ -149,7 +149,7 @@ uniform sampler2D sNormal; float average = dot(f, vec4(0.25)); // normal - v.zw = normalize( vec3(f.x - f.z, 64.0 / (1024.0 * 2.0), f.y - f.w) ).xz; + v.zw = normalize( vec3(f.x - f.z, 64.0 / (1024.0 * 4.0), f.y - f.w) ).xz; // integrate const float vel = 1.4; @@ -176,13 +176,12 @@ uniform sampler2D sNormal; } vec4 compose() { + vec3 viewVec = normalize(vViewVec); + vec4 value = texture2D(sNormal, vTexCoord); - - vec3 normal = vec3(value.z, -sqrt(1.0 - dot(value.zw, value.zw)), value.w); - + vec3 normal = vec3(value.z, sqrt(1.0 - dot(value.zw, value.zw)) * sign(viewVec.y), value.w); vec2 dudv = (uViewProj * vec4(normal.x, 0.0, normal.z, 0.0)).xy; - vec3 viewVec = normalize(vViewVec); vec3 rv = reflect(-viewVec, normal); vec3 lv = normalize(vLightVec); @@ -195,7 +194,7 @@ uniform sampler2D sNormal; vec4 refr = vec4(mix(refrA.xyz, refrB.xyz, refrA.w), 1.0); vec4 refl = texture2D(sReflect, vec2(tc.x, 1.0 - tc.y) + dudv * uParam.w); - float fresnel = calcFresnel(dot(normal, viewVec), 0.1, 2.0); + float fresnel = calcFresnel(dot(normal, viewVec), 0.04); vec4 color = mix(refr, refl, fresnel) + spec * 1.5;