#include "framework.h" #include "lara_fire.h" #include "items.h" #include "lara_flare.h" #include "lara_one_gun.h" #include "lara_two_guns.h" #include "camera.h" #include "objects.h" #include "effects/effects.h" #include "sphere.h" #include "animation.h" #include "level.h" #include "control/lot.h" #include "setup.h" #include "input.h" #include "Sound/sound.h" #include "control/los.h" #include "savegame.h" #include "GameFlowScript.h" #include "lara_struct.h" #include "itemdata/creature_info.h" #include "Objects/Generic/Object/burning_torch.h" using namespace TEN::Entities::Generic; WEAPON_INFO Weapons[NUM_WEAPONS] = { /* No weapons */ { { ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f) }, { ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f) }, { ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f) }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Pistols */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(60.0f), ANGLE(60.0f) }, { -ANGLE(170.0f), ANGLE(60.0f), -ANGLE(80.0f), ANGLE(80.0f) }, { -ANGLE(60.0f), ANGLE(170.0f), -ANGLE(80.0f), ANGLE(80.0f) }, ANGLE(10.0f), ANGLE(8.0f), 650, SECTOR(8), 1, 9, 3, 0, SFX_TR4_LARA_FIRE, 0 }, /* Revolver */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(60.0f), ANGLE(60.0f) }, { -ANGLE(10.0f), ANGLE(10.0f), -ANGLE(80.0f), ANGLE(80.0f) }, { ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f) }, ANGLE(10.0f), ANGLE(4.0f), 650, SECTOR(8), 21, 16, 3, 0, SFX_TR4_DESSERT_EAGLE_FIRE, 0 }, /* Uzis */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(60.0f), ANGLE(60.0f) }, { -ANGLE(170.0f), ANGLE(60.0f), -ANGLE(80.0f), ANGLE(80.0f) }, { -ANGLE(60.0f), ANGLE(170.0f), -ANGLE(80.0f), ANGLE(80.0f) }, ANGLE(10.0f), ANGLE(8.0f), 650, SECTOR(8), 1, 3, 3, 0, SFX_TR4_LARA_UZI_FIRE, 0 }, /* Shotgun */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, ANGLE(10.0f), 0, 500, SECTOR(8), 3, 9, 3, 10, SFX_TR4_LARA_SHOTGUN, 0 }, /* HK */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, ANGLE(10.0f), ANGLE(4.0f), 500, SECTOR(12), 4, 0, 3, 10, 0, // FIRE/SILENCER_FIRE 0 }, /* Crossbow */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, ANGLE(10.0f), ANGLE(8.0f), 500, SECTOR(8), 5, 0, 2, 10, SFX_TR4_LARA_CROSSBOW, 20 }, /* Flare */ { { ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f) }, { ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f) }, { ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f), ANGLE(0.0f) }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Flare 2 */ { { -ANGLE(30.0f), ANGLE(30.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(30.0f), ANGLE(30.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(30.0f), ANGLE(30.0f), -ANGLE(55.0f), ANGLE(55.0f) }, ANGLE(10.0f), ANGLE(8.0f), 400, SECTOR(8), 3, 0, 2, 0, SFX_TR4_LARA_UZI_FIRE, 0 }, /* Grenade launcher */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, ANGLE(10.0f), ANGLE(8.0f), 500, SECTOR(8), 20, 0, 2, 10, 0, 30 }, /* Harpoon gun */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(65.0f), ANGLE(65.0f) }, { -ANGLE(20.0f), ANGLE(20.0f), -ANGLE(75.0f), ANGLE(75.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(75.0f), ANGLE(75.0f) }, ANGLE(10.0f), ANGLE(8.0f), 500, SECTOR(8), 6, 0, 2, 10, 0, 0 }, /* Rocket launcher */ { { -ANGLE(60.0f), ANGLE(60.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, { -ANGLE(80.0f), ANGLE(80.0f), -ANGLE(65.0f), ANGLE(65.0f) }, ANGLE(10.0f), ANGLE(8.0f), 500, SECTOR(8), 30, 0, 2, 12, 77, 30 }, /* Snowmobile */ { { -ANGLE(30.0f), ANGLE(30.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(30.0f), ANGLE(30.0f), -ANGLE(55.0f), ANGLE(55.0f) }, { -ANGLE(30.0f), ANGLE(30.0f), -ANGLE(55.0f), ANGLE(55.0f) }, ANGLE(10.0f), ANGLE(8.0f), 400, SECTOR(8), 3, 0, 0, 0, SFX_TR4_LARA_UZI_FIRE, 0 } }; short HoldStates[] = { LS_WALK_FORWARD, LS_RUN_FORWARD, LS_STOP, LS_POSE, LS_TURN_RIGHT_SLOW, LS_TURN_LEFT_SLOW, LS_WALK_BACK, LS_TURN_FAST, LS_STEP_RIGHT, LS_STEP_LEFT, LS_PICKUP, LS_SWITCH_DOWN, LS_SWITCH_UP, LS_WADE_FORWARD, LS_CROUCH_IDLE, LS_CROUCH_TURN_LEFT, LS_CROUCH_TURN_RIGHT, -1 }; bool MonksAttackLara; ITEM_INFO* LastTargets[MAX_TARGETS]; ITEM_INFO* TargetList[MAX_TARGETS]; GAME_OBJECT_ID WeaponObject(int weaponType) { switch (weaponType) { case WEAPON_UZI: return ID_UZI_ANIM; case WEAPON_SHOTGUN: return ID_SHOTGUN_ANIM; case WEAPON_REVOLVER: return ID_REVOLVER_ANIM; case WEAPON_CROSSBOW: return ID_CROSSBOW_ANIM; case WEAPON_HK: return ID_HK_ANIM; case WEAPON_FLARE: return ID_LARA_FLARE_ANIM; case WEAPON_GRENADE_LAUNCHER: return ID_GRENADE_ANIM; case WEAPON_ROCKET_LAUNCHER: return ID_ROCKET_ANIM; case WEAPON_HARPOON_GUN: return ID_HARPOON_ANIM; default: return ID_PISTOLS_ANIM; } } void AimWeapon(ITEM_INFO* lara, WEAPON_INFO* weaponInfo, LARA_ARM* arm) { LaraInfo*& laraInfo = lara->data; short rotY, rotX, speed, x, y; speed = weaponInfo->aimSpeed; // Have target lock, so get XY angles for arms. if (arm->lock) { y = laraInfo->targetAngles[0]; x = laraInfo->targetAngles[1]; } // No target lock, so aim straight. else { y = 0; x = 0; } // Rotate arms on y axis toward target. rotY = arm->yRot; if (rotY >= (y - speed) && rotY <= (y + speed)) rotY = y; else if (rotY < y) rotY += speed; else rotY -= speed; arm->yRot = rotY; // Rotate arms on x axis toward target. rotX = arm->xRot; if (rotX >= (x - speed) && rotX <= (x + speed)) rotX = x; else if (rotX < x) rotX += speed; else rotX -= speed; arm->xRot = rotX; // TODO: Set arms to inherit rotations of parent bones. arm->zRot = 0; } void SmashItem(short itemNum) { ITEM_INFO* item = &g_Level.Items[itemNum]; if (item->objectNumber >= ID_SMASH_OBJECT1 && item->objectNumber <= ID_SMASH_OBJECT8) SmashObject(itemNum); } void LaraGun(ITEM_INFO* lara) { LaraInfo*& laraInfo = lara->data; if (laraInfo->leftArm.flash_gun > 0) --laraInfo->leftArm.flash_gun; if (laraInfo->rightArm.flash_gun > 0) --laraInfo->rightArm.flash_gun; if (laraInfo->gunType == WEAPON_TORCH) { DoFlameTorch(); return; } if (lara->hitPoints <= 0) laraInfo->gunStatus = LG_NO_ARMS; else if (laraInfo->gunStatus == LG_NO_ARMS) { // Draw weapon. if (TrInput & IN_DRAW) laraInfo->requestGunType = laraInfo->lastGunType; // Draw flare. else if (TrInput & IN_FLARE && (g_GameFlow->GetLevel(CurrentLevel)->LaraType != LaraType::Young)) { if (lara->currentAnimState == LS_CROUCH_IDLE && lara->animNumber != LA_CROUCH_IDLE) { return; } if (laraInfo->gunType == WEAPON_FLARE) { // if (!laraInfo->leftArm.frameNumber) //NO { laraInfo->gunStatus = LG_UNDRAW_GUNS; } } else if (laraInfo->NumFlares) { if (laraInfo->NumFlares != -1) laraInfo->NumFlares--; laraInfo->requestGunType = WEAPON_FLARE; } } if (TrInput & IN_DRAW || laraInfo->requestGunType != laraInfo->gunType) { if ((lara->currentAnimState == LS_CROUCH_IDLE || lara->currentAnimState == LS_CROUCH_TURN_LEFT || lara->currentAnimState == LS_CROUCH_TURN_RIGHT) && (laraInfo->requestGunType == WEAPON_HK || laraInfo->requestGunType == WEAPON_CROSSBOW || laraInfo->requestGunType == WEAPON_SHOTGUN || laraInfo->requestGunType == WEAPON_HARPOON_GUN)) { if (laraInfo->gunType == WEAPON_FLARE) laraInfo->requestGunType = WEAPON_FLARE; } else if (laraInfo->requestGunType == WEAPON_FLARE || (laraInfo->Vehicle == NO_ITEM && (laraInfo->requestGunType == WEAPON_HARPOON_GUN || laraInfo->waterStatus == LW_ABOVE_WATER || (laraInfo->waterStatus == LW_WADE && laraInfo->waterSurfaceDist > -Weapons[laraInfo->gunType].gunHeight)))) { if (laraInfo->gunType == WEAPON_FLARE) { CreateFlare(lara, ID_FLARE_ITEM, 0); undraw_flare_meshes(lara); laraInfo->flareControlLeft = false; laraInfo->flareAge = 0; } laraInfo->gunType = laraInfo->requestGunType; InitialiseNewWeapon(lara); laraInfo->rightArm.frameNumber = 0; laraInfo->leftArm.frameNumber = 0; laraInfo->gunStatus = LG_DRAW_GUNS; } else { laraInfo->lastGunType = laraInfo->requestGunType; if (laraInfo->gunType != WEAPON_FLARE) laraInfo->gunType = laraInfo->requestGunType; else laraInfo->requestGunType = WEAPON_FLARE; } } } else if (laraInfo->gunStatus == LG_READY) { if (TrInput & IN_DRAW || laraInfo->requestGunType != laraInfo->gunType) { laraInfo->gunStatus = LG_UNDRAW_GUNS; } else if (laraInfo->gunType != WEAPON_HARPOON_GUN && laraInfo->waterStatus != LW_ABOVE_WATER && (laraInfo->waterStatus != LW_WADE || laraInfo->waterSurfaceDist < -Weapons[laraInfo->gunType].gunHeight)) { laraInfo->gunStatus = LG_UNDRAW_GUNS; } } else if (TrInput & IN_FLARE && laraInfo->gunStatus == LG_HANDS_BUSY && lara->currentAnimState == LS_CRAWL_IDLE && lara->animNumber == LA_CRAWL_IDLE) { laraInfo->requestGunType = WEAPON_FLARE; } switch (laraInfo->gunStatus) { case LG_DRAW_GUNS: if (laraInfo->gunType != WEAPON_FLARE && laraInfo->gunType != WEAPON_NONE) { laraInfo->lastGunType = laraInfo->gunType; } switch (laraInfo->gunType) { case WEAPON_PISTOLS: case WEAPON_REVOLVER: case WEAPON_UZI: if (Camera.type != CAMERA_TYPE::LOOK_CAMERA && Camera.type != CAMERA_TYPE::HEAVY_CAMERA) Camera.type = CAMERA_TYPE::COMBAT_CAMERA; draw_pistols(laraInfo->gunType); break; case WEAPON_SHOTGUN: case WEAPON_CROSSBOW: case WEAPON_HK: case WEAPON_GRENADE_LAUNCHER: case WEAPON_ROCKET_LAUNCHER: case WEAPON_HARPOON_GUN: if (Camera.type != CAMERA_TYPE::LOOK_CAMERA && Camera.type != CAMERA_TYPE::HEAVY_CAMERA) Camera.type = CAMERA_TYPE::COMBAT_CAMERA; draw_shotgun(laraInfo->gunType); break; case WEAPON_FLARE: draw_flare(lara); break; default: laraInfo->gunStatus = LG_NO_ARMS; break; } break; case LG_SPECIAL: draw_flare(lara); break; case LG_UNDRAW_GUNS: laraInfo->meshPtrs[LM_HEAD] = Objects[ID_LARA_SKIN].meshIndex + LM_HEAD; switch (laraInfo->gunType) { case WEAPON_PISTOLS: case WEAPON_REVOLVER: case WEAPON_UZI: undraw_pistols(laraInfo->gunType); break; case WEAPON_SHOTGUN: case WEAPON_CROSSBOW: case WEAPON_HK: case WEAPON_GRENADE_LAUNCHER: case WEAPON_ROCKET_LAUNCHER: case WEAPON_HARPOON_GUN: undraw_shotgun(laraInfo->gunType); break; case WEAPON_FLARE: undraw_flare(lara); break; default: return; } break; case LG_READY: if (!(TrInput & IN_ACTION)) laraInfo->meshPtrs[LM_HEAD] = Objects[ID_LARA_SKIN].meshIndex + LM_HEAD; else laraInfo->meshPtrs[LM_HEAD] = Objects[ID_LARA_SCREAM].meshIndex + LM_HEAD; if (Camera.type != CAMERA_TYPE::LOOK_CAMERA && Camera.type != CAMERA_TYPE::HEAVY_CAMERA) { Camera.type = CAMERA_TYPE::COMBAT_CAMERA; } if (TrInput & IN_ACTION) { if (!GetAmmo(lara, laraInfo->gunType)) { laraInfo->requestGunType = Objects[ID_PISTOLS_ITEM].loaded ? WEAPON_PISTOLS : WEAPON_NONE; return; } } switch (laraInfo->gunType) { case WEAPON_PISTOLS: case WEAPON_UZI: PistolHandler(laraInfo->gunType); break; case WEAPON_SHOTGUN: case WEAPON_CROSSBOW: case WEAPON_HK: case WEAPON_GRENADE_LAUNCHER: case WEAPON_ROCKET_LAUNCHER: case WEAPON_HARPOON_GUN: case WEAPON_REVOLVER: RifleHandler(laraInfo->gunType); break; default: return; } break; case LG_NO_ARMS: if (laraInfo->gunType == WEAPON_FLARE) { if (laraInfo->Vehicle != NO_ITEM || CheckForHoldingState(lara->currentAnimState)) { if (laraInfo->flareControlLeft) { if (laraInfo->leftArm.frameNumber) { if (++laraInfo->leftArm.frameNumber == 110) laraInfo->leftArm.frameNumber = 0; } } else { laraInfo->leftArm.frameNumber = 95; laraInfo->flareControlLeft = true; } } else laraInfo->flareControlLeft = false; DoFlareInHand(lara, laraInfo->flareAge); set_flare_arm(lara, laraInfo->leftArm.frameNumber); } break; case LG_HANDS_BUSY: if (laraInfo->gunType == WEAPON_FLARE) { if (laraInfo->meshPtrs[LM_LHAND] == Objects[ID_LARA_FLARE_ANIM].meshIndex + LM_LHAND) { laraInfo->flareControlLeft = (laraInfo->Vehicle != NO_ITEM || CheckForHoldingState(lara->currentAnimState)); DoFlareInHand(lara, laraInfo->flareAge); set_flare_arm(lara, laraInfo->leftArm.frameNumber); } } break; } } Ammo& GetAmmo(ITEM_INFO* lara, int weaponType) { LaraInfo*& laraInfo = lara->data; return laraInfo->Weapons[weaponType].Ammo[laraInfo->Weapons[weaponType].SelectedAmmo]; } void InitialiseNewWeapon(ITEM_INFO* lara) { LaraInfo*& laraInfo = lara->data; laraInfo->rightArm.frameNumber = 0; laraInfo->leftArm.frameNumber = 0; laraInfo->leftArm.zRot = 0; laraInfo->leftArm.yRot = 0; laraInfo->leftArm.xRot = 0; laraInfo->rightArm.zRot = 0; laraInfo->rightArm.yRot = 0; laraInfo->rightArm.xRot = 0; laraInfo->target = nullptr; laraInfo->rightArm.lock = false; laraInfo->leftArm.lock = false; laraInfo->rightArm.flash_gun = 0; laraInfo->leftArm.flash_gun = 0; switch (laraInfo->gunType) { case WEAPON_PISTOLS: case WEAPON_UZI: laraInfo->rightArm.frameBase = Objects[ID_PISTOLS_ANIM].frameBase; laraInfo->leftArm.frameBase = Objects[ID_PISTOLS_ANIM].frameBase; if (laraInfo->gunStatus != LG_NO_ARMS) draw_pistol_meshes(laraInfo->gunType); break; case WEAPON_SHOTGUN: case WEAPON_REVOLVER: case WEAPON_HK: case WEAPON_GRENADE_LAUNCHER: case WEAPON_HARPOON_GUN: case WEAPON_ROCKET_LAUNCHER: laraInfo->rightArm.frameBase = Objects[WeaponObject(laraInfo->gunType)].frameBase; laraInfo->leftArm.frameBase = Objects[WeaponObject(laraInfo->gunType)].frameBase; if (laraInfo->gunStatus != LG_NO_ARMS) draw_shotgun_meshes(laraInfo->gunType); break; case WEAPON_FLARE: laraInfo->rightArm.frameBase = Objects[ID_LARA_FLARE_ANIM].frameBase; laraInfo->leftArm.frameBase = Objects[ID_LARA_FLARE_ANIM].frameBase; if (laraInfo->gunStatus != LG_NO_ARMS) draw_flare_meshes(lara); break; default: laraInfo->rightArm.frameBase = g_Level.Anims[lara->animNumber].framePtr; laraInfo->leftArm.frameBase = g_Level.Anims[lara->animNumber].framePtr; break; } } GAME_OBJECT_ID WeaponObjectMesh(ITEM_INFO* lara, int weaponType) { LaraInfo*& laraInfo = lara->data; switch (weaponType) { case WEAPON_REVOLVER: return (laraInfo->Weapons[WEAPON_REVOLVER].HasLasersight == true ? ID_LARA_REVOLVER_LASER : ID_REVOLVER_ANIM); case WEAPON_UZI: return ID_UZI_ANIM; case WEAPON_SHOTGUN: return ID_SHOTGUN_ANIM; case WEAPON_HK: return ID_HK_ANIM; case WEAPON_CROSSBOW: return (laraInfo->Weapons[WEAPON_CROSSBOW].HasLasersight == true ? ID_LARA_CROSSBOW_LASER : ID_CROSSBOW_ANIM); case WEAPON_GRENADE_LAUNCHER: return ID_GRENADE_ANIM; case WEAPON_HARPOON_GUN: return ID_HARPOON_ANIM; case WEAPON_ROCKET_LAUNCHER: return ID_ROCKET_ANIM; default: return ID_PISTOLS_ANIM; } } void HitTarget(ITEM_INFO* lara, ITEM_INFO* target, GAME_VECTOR* hitPos, int damage, int grenade) { LaraInfo*& laraInfo = lara->data; target->hitStatus = true; if (target->data.is()) ((CREATURE_INFO*)target->data)->hurtByLara = true; OBJECT_INFO* obj = &Objects[target->objectNumber]; if (hitPos != nullptr) { if (obj->hitEffect != HIT_NONE) { switch (obj->hitEffect) { case HIT_BLOOD: if (target->objectNumber == ID_BADDY2 && (target->currentAnimState == 8 || GetRandomControl() & 1) && (laraInfo->gunType == WEAPON_PISTOLS || laraInfo->gunType == WEAPON_SHOTGUN || laraInfo->gunType == WEAPON_UZI)) { // Baddy2 gun hitting sword SoundEffect(SFX_TR4_BAD_SWORD_RICO, &target->pos, 0); TriggerRicochetSpark(hitPos, lara->pos.yRot, 3, 0); return; } else DoBloodSplat(hitPos->x, hitPos->y, hitPos->z, (GetRandomControl() & 3) + 3, target->pos.yRot, target->roomNumber); break; case HIT_RICOCHET: TriggerRicochetSpark(hitPos, lara->pos.yRot, 3, 0); break; case HIT_SMOKE: TriggerRicochetSpark(hitPos, lara->pos.yRot, 3, -5); if (target->objectNumber == ID_ROMAN_GOD1 || target->objectNumber == ID_ROMAN_GOD2) { SoundEffect(SFX_TR5_SWORD_GOD_HITMET, &target->pos, 0); } break; } } } if (!obj->undead || grenade || target->hitPoints == NOT_TARGETABLE) { if (target->hitPoints > 0) { Statistics.Level.AmmoHits++; if (target->hitPoints >= damage) target->hitPoints -= damage; else target->hitPoints = 0; } } } FireWeaponType FireWeapon(LARA_WEAPON_TYPE weaponType, ITEM_INFO* target, ITEM_INFO* src, short* angles) { LaraInfo*& laraInfo = src->data; Ammo& ammo = GetAmmo(src, weaponType); if (ammo.getCount() == 0 && !ammo.hasInfinite()) return FW_NOAMMO; if (!ammo.hasInfinite()) ammo--; WEAPON_INFO* weapon = &Weapons[weaponType]; int r; PHD_VECTOR pos, muzzleOffset; GetLaraJointPosition(&muzzleOffset, LM_RHAND); pos.x = src->pos.xPos; pos.y = muzzleOffset.y; pos.z = src->pos.zPos; PHD_3DPOS rotation; rotation.xRot = angles[1] + (GetRandomControl() - 16384) * weapon->shotAccuracy / 65536; rotation.yRot = angles[0] + (GetRandomControl() - 16384) * weapon->shotAccuracy / 65536; rotation.zRot = 0; // Calculate ray from rotation angles float x = sin(TO_RAD(rotation.yRot)) * cos(TO_RAD(rotation.xRot)); float y = -sin(TO_RAD(rotation.xRot)); float z = cos(TO_RAD(rotation.yRot)) * cos(TO_RAD(rotation.xRot)); Vector3 direction = Vector3(x, y, z); direction.Normalize(); Vector3 source = Vector3(pos.x, pos.y, pos.z); Vector3 destination = source + direction * weapon->targetDist; Ray ray = Ray(source, direction); int num = GetSpheres(target, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity); int best = NO_ITEM; float bestDistance = FLT_MAX; for (int i = 0; i < num; i++) { BoundingSphere sphere = BoundingSphere(Vector3(CreatureSpheres[i].x, CreatureSpheres[i].y, CreatureSpheres[i].z), CreatureSpheres[i].r); float distance; if (ray.Intersects(sphere, distance)) { if (distance < bestDistance) { bestDistance = distance; best = i; } } } laraInfo->hasFired = true; laraInfo->fired = true; GAME_VECTOR vSrc; vSrc.x = pos.x; vSrc.y = pos.y; vSrc.z = pos.z; short roomNumber = src->roomNumber; GetFloor(pos.x, pos.y, pos.z, &roomNumber); vSrc.roomNumber = roomNumber; if (best < 0) { GAME_VECTOR vDest; vDest.x = destination.x; vDest.y = destination.y; vDest.z = destination.z; GetTargetOnLOS(&vSrc, &vDest, false, true); return FW_MISS; } else { Statistics.Game.AmmoHits++; destination = source + direction * bestDistance; GAME_VECTOR vDest; vDest.x = destination.x; vDest.y = destination.y; vDest.z = destination.z; // TODO: enable it when the slot is created ! /* if (target->objectNumber == ID_TRIBEBOSS) { long dx, dy, dz; dx = (vDest.x - vSrc.x) >> 5; dy = (vDest.y - vSrc.y) >> 5; dz = (vDest.z - vSrc.z) >> 5; FindClosestShieldPoint(vDest.x - dx, vDest.y - dy, vDest.z - dz, target); } else if (target->objectNumber == ID_ARMY_WINSTON || target->objectNumber == ID_LONDONBOSS) //Don't want blood on Winston - never get the stains out { short ricochet_angle; target->hitStatus = true; //need to do this to maintain defence state target->hitPoints--; ricochet_angle = (mGetAngle(lara->pos.zPos, lara->pos.xPos, target->pos.zPos, target->pos.xPos) >> 4) & 4095; TriggerRicochetSparks(&vDest, ricochet_angle, 16, 0); SoundEffect(SFX_TR4_LARA_RICOCHET, &target->pos, 0); // play RICOCHET Sample } else if (target->objectNumber == ID_SHIVA) //So must be Shiva { z = target->pos.zPos - lara_item->pos.zPos; x = target->pos.xPos - lara_item->pos.xPos; angle = 0x8000 + phd_atan(z, x) - target->pos.yRot; if ((target->currentAnimState > 1 && target->currentAnimState < 5) && angle < 0x4000 && angle > -0x4000) { target->hitStatus = true; //need to do this to maintain defence state ricochet_angle = (mGetAngle(lara->pos.zPos, lara->pos.xPos, target->pos.zPos, target->pos.xPos) >> 4) & 4095; TriggerRicochetSparks(&vDest, ricochet_angle, 16, 0); SoundEffect(SFX_TR4_LARA_RICOCHET, &target->pos, 0); // play RICOCHET Sample } else //Shiva's not in defence mode or has its back to Lara HitTarget(target, &vDest, weapon->damage, 0); } else {*/ // NOTE: it seems that items for being hit by Lara in the normal way must have GetTargetOnLOS returning false // it's really weird but we decided to replicate original behaviour until we'll fully understand what is happening // with weapons if (!GetTargetOnLOS(&vSrc, &vDest, false, true)) HitTarget(src, target, &vDest, weapon->damage, false); //} return FW_MAYBEHIT; } } void find_target_point(ITEM_INFO* item, GAME_VECTOR* target) { BOUNDING_BOX* bounds; int x, y, z; float c, s; bounds = (BOUNDING_BOX*)GetBestFrame(item); x = (int)(bounds->X1 + bounds->X2) / 2; y = (int) bounds->Y1 + (bounds->Y2 - bounds->Y1) / 3; z = (int)(bounds->Z1 + bounds->Z2) / 2; c = phd_cos(item->pos.yRot); s = phd_sin(item->pos.yRot); target->x = item->pos.xPos + c * x + s * z; target->y = item->pos.yPos + y; target->z = item->pos.zPos + c * z - s * x; target->roomNumber = item->roomNumber; } void LaraTargetInfo(ITEM_INFO* lara, WEAPON_INFO* weapon) { LaraInfo*& laraInfo = lara->data; if (laraInfo->target == nullptr) { laraInfo->rightArm.lock = false; laraInfo->leftArm.lock = false; laraInfo->targetAngles[1] = 0; laraInfo->targetAngles[0] = 0; return; } GAME_VECTOR src, targetPoint; PHD_VECTOR muzzleOffset; short angles[2]; GetLaraJointPosition(&muzzleOffset, LM_RHAND); src.x = lara->pos.xPos; src.y = muzzleOffset.y; src.z = lara->pos.zPos; src.roomNumber = lara->roomNumber; find_target_point(laraInfo->target, &targetPoint); phd_GetVectorAngles(targetPoint.x - src.x, targetPoint.y - src.y, targetPoint.z - src.z, angles); angles[0] -= lara->pos.yRot; angles[1] -= lara->pos.xRot; if (LOS(&src, &targetPoint)) { if (angles[0] >= weapon->lockAngles[0] && angles[0] <= weapon->lockAngles[1] && angles[1] >= weapon->lockAngles[2] && angles[1] <= weapon->lockAngles[3]) { laraInfo->rightArm.lock = true; laraInfo->leftArm.lock = true; } else { if (laraInfo->leftArm.lock) { if (angles[0] < weapon->leftAngles[0] || angles[0] > weapon->leftAngles[1] || angles[1] < weapon->leftAngles[2] || angles[1] > weapon->leftAngles[3]) laraInfo->leftArm.lock = false; } if (laraInfo->rightArm.lock) { if (angles[0] < weapon->rightAngles[0] || angles[0] > weapon->rightAngles[1] || angles[1] < weapon->rightAngles[2] || angles[1] > weapon->rightAngles[3]) laraInfo->rightArm.lock = false; } } } else { laraInfo->rightArm.lock = false; laraInfo->leftArm.lock = false; } laraInfo->targetAngles[0] = angles[0]; laraInfo->targetAngles[1] = angles[1]; } bool CheckForHoldingState(int state) { #if 0 if (laraInfo->ExtraAnim != NO_ITEM) return false; #endif short* holdState = HoldStates; while (*holdState >= 0) { if (state == *holdState) return true; holdState++; } return false; } void LaraGetNewTarget(ITEM_INFO* lara, WEAPON_INFO* weaponInfo) { LaraInfo*& laraInfo = lara->data; GAME_VECTOR src, target; PHD_VECTOR muzzleOffset; int bestDistance, maxDistance, targets, slot, x, y, z, distance; ITEM_INFO* bestItem, *item; short bestYrot, angle[2], match; bool flag, loop; if (BinocularRange) { laraInfo->target = nullptr; return; } GetLaraJointPosition(&muzzleOffset, LM_RHAND); src.x = lara->pos.xPos; src.y = muzzleOffset.y; src.z = lara->pos.zPos; src.roomNumber = lara->roomNumber; bestItem = NULL; bestYrot = MAXSHORT; bestDistance = MAXINT; maxDistance = weaponInfo->targetDist; targets = 0; for (slot = 0; slot < ActiveCreatures.size(); ++slot) { if (ActiveCreatures[slot]->itemNum != NO_ITEM) { item = &g_Level.Items[ActiveCreatures[slot]->itemNum]; if (item->hitPoints > 0) { x = item->pos.xPos - src.x; y = item->pos.yPos - src.y; z = item->pos.zPos - src.z; if (abs(x) <= maxDistance && abs(y) <= maxDistance && abs(z) <= maxDistance) { distance = SQUARE(x) + SQUARE(y) + SQUARE(z); if (distance < SQUARE(maxDistance)) { find_target_point(item, &target); if (LOS(&src, &target)) { phd_GetVectorAngles(target.x - src.x, target.y - src.y, target.z - src.z, angle); angle[0] -= lara->pos.yRot + laraInfo->torsoYrot; angle[1] -= lara->pos.xRot + laraInfo->torsoXrot; if (angle[0] >= weaponInfo->lockAngles[0] && angle[0] <= weaponInfo->lockAngles[1] && angle[1] >= weaponInfo->lockAngles[2] && angle[1] <= weaponInfo->lockAngles[3]) { TargetList[targets] = item; ++targets; if (abs(angle[0]) < bestYrot + ANGLE(15.0f) && distance < bestDistance) { bestDistance = distance; bestYrot = abs(angle[0]); bestItem = item; } } } } } } } } TargetList[targets] = NULL; if (!TargetList[0]) { laraInfo->target = NULL; } else { for (slot = 0; slot < MAX_TARGETS; ++slot) { if (!TargetList[slot]) laraInfo->target = NULL; if (TargetList[slot] == laraInfo->target) break; } if (laraInfo->gunStatus != LG_NO_ARMS || TrInput & IN_LOOKSWITCH) { if (!laraInfo->target) { laraInfo->target = bestItem; LastTargets[0] = NULL; } else if (TrInput & IN_LOOKSWITCH) { laraInfo->target = NULL; flag = true; for (match = 0; match < MAX_TARGETS && TargetList[match]; ++match) { loop = false; for (slot = 0; slot < MAX_TARGETS && LastTargets[slot]; ++slot) { if (LastTargets[slot] == TargetList[match]) { loop = true; break; } } if (!loop) { laraInfo->target = TargetList[match]; if (laraInfo->target) flag = false; break; } } if (flag) { laraInfo->target = bestItem; LastTargets[0] = NULL; } } } } if (laraInfo->target != LastTargets[0]) { for (slot = 7; slot > 0; --slot) LastTargets[slot] = LastTargets[slot - 1]; LastTargets[0] = laraInfo->target; } LaraTargetInfo(lara, weaponInfo); } HOLSTER_SLOT HolsterSlotForWeapon(LARA_WEAPON_TYPE weapon) { switch(weapon){ case WEAPON_PISTOLS: return HOLSTER_SLOT::Pistols; case WEAPON_UZI: return HOLSTER_SLOT::Uzis; case WEAPON_REVOLVER: return HOLSTER_SLOT::Revolver; case WEAPON_SHOTGUN: return HOLSTER_SLOT::Shotgun; case WEAPON_HK: return HOLSTER_SLOT::HK; case WEAPON_HARPOON_GUN: return HOLSTER_SLOT::Harpoon; case WEAPON_CROSSBOW: return HOLSTER_SLOT::Crowssbow; case WEAPON_GRENADE_LAUNCHER: return HOLSTER_SLOT::GrenadeLauncher; case WEAPON_ROCKET_LAUNCHER: return HOLSTER_SLOT::RocketLauncher; default: return HOLSTER_SLOT::Empty; } }