#include "control.h" #include "..\Global\global.h" #include "pickup.h" #include "spotcam.h" #include "Camera.h" #include "Lara.h" #include "hair.h" #include "items.h" #include "effect2.h" #include "draw.h" #include "inventory.h" #include "gameflow.h" #include "lot.h" #include "pickup.h" #include "draw.h" #include "healt.h" #include "savegame.h" #include "sound.h" #include "spotcam.h" #include "box.h" #include "objects.h" #include "switch.h" #include "laramisc.h" #include "rope.h" #include "tomb4fx.h" #include "traps.h" #include "effects.h" #include "..\Specific\roomload.h" #include "..\Specific\input.h" #include "..\Specific\init.h" #include "..\Specific\winmain.h" #include #include int KeyTriggerActive; PENDULUM CurrentPendulum; int number_los_rooms; short los_rooms[20]; extern GameFlow* g_GameFlow; extern GameScript* g_GameScript; extern Inventory* g_Inventory; extern int SplashCount; GAME_STATUS ControlPhase(int numFrames, int demoMode) { GameScriptLevel* level = g_GameFlow->GetLevel(CurrentLevel); RegeneratePickups(); if (numFrames > 10) numFrames = 10; if (TrackCameraInit) UseSpotCam = false; SetDebounce = true; for (FramesCount += numFrames; FramesCount > 0; FramesCount -= 2) { GlobalCounter++; UpdateSky(); // Poll the keyboard and update input variables if (CurrentLevel != 0) { if (S_UpdateInput() == -1) return GAME_STATUS_NONE; } // Has Lara control been disabled? if (DisableLaraControl || CurrentLevel == 0) { if (CurrentLevel != 0) DbInput = 0; TrInput &= 0x200; } // If cutscene has been triggered then clear input if (CutSeqTriggered) TrInput = 0; // Does the player want to enter inventory? SetDebounce = false; if (CurrentLevel != 0 && !g_Renderer->IsFading()) { if ((DbInput & IN_DESELECT || g_Inventory->GetEnterObject() != NO_ITEM) && !CutSeqTriggered && LaraItem->hitPoints > 0) { // Stop all sounds int inventoryResult = g_Inventory->DoInventory(); switch (inventoryResult) { case INV_RESULT_LOAD_GAME: return GAME_STATUS_LOAD_GAME; case INV_RESULT_EXIT_TO_TILE: return GAME_STATUS_EXIT_TO_TITLE; } } } // Has level been completed? if (CurrentLevel != 0 && LevelComplete) return GAME_STATUS_LEVEL_COMPLETED; int oldInput = TrInput; // Is Lara dead? if (CurrentLevel != 0 && (Lara.deathCount > 300 || Lara.deathCount > 60 && TrInput)) { int inventoryResult = g_Inventory->DoInventory(); switch (inventoryResult) { case INV_RESULT_NEW_GAME: return GAME_STATUS_NEW_GAME; case INV_RESULT_LOAD_GAME: return GAME_STATUS_LOAD_GAME; case INV_RESULT_EXIT_TO_TILE: return GAME_STATUS_EXIT_TO_TITLE; } } if (demoMode && TrInput == -1) { oldInput = 0; TrInput = 0; } if (CurrentLevel == 0) TrInput = 0; // Handle lasersight and binocular if (CurrentLevel != 0) { if (!(TrInput & IN_LOOK) || SniperCameraActive || UseSpotCam || TrackCameraInit || ((LaraItem->currentAnimState != STATE_LARA_STOP || LaraItem->animNumber != ANIMATION_LARA_STAY_IDLE) && (!Lara.isDucked || TrInput & IN_DUCK || LaraItem->animNumber != ANIMATION_LARA_CROUCH_IDLE || LaraItem->goalAnimState != STATE_LARA_CROUCH_IDLE))) { if (BinocularRange == 0) { if (SniperCameraActive || UseSpotCam || TrackCameraInit) TrInput &= ~IN_LOOK; } else { if (LaserSight) { BinocularRange = 0; LaserSight = false; AlterFOV(ANGLE(80)); LaraItem->meshBits = 0xFFFFFFFF; Lara.isDucked = false; Camera.type = BinocularOldCamera; Lara.headYrot = 0; Lara.headXrot = 0; Lara.torsoYrot = 0; Lara.torsoXrot = 0; Camera.bounce = 0; BinocularOn = -8; TrInput &= ~IN_LOOK; } else { TrInput |= IN_LOOK; } } Infrared = false; } else if (BinocularRange == 0) { if (Lara.gunStatus == LG_READY && ((Lara.gunType == WEAPON_REVOLVER && g_LaraExtra.Weapons[WEAPON_REVOLVER].HasLasersight) || (Lara.gunType == WEAPON_HK) || (Lara.gunType == WEAPON_CROSSBOW && g_LaraExtra.Weapons[WEAPON_CROSSBOW].HasLasersight))) { BinocularRange = 128; BinocularOldCamera = Camera.oldType; Lara.busy = true; LaserSight = true; /*if (!(gfLevelFlags & GF_LVOP_TRAIN)) InfraRed = TRUE; else* InfraRed = false;*/ Infrared = true; } else Infrared = false; } else { if (LaserSight) { /*if (!(gfLevelFlags & GF_LVOP_TRAIN)) InfraRed = TRUE; else InfraRed = false;*/ Infrared = true; } else { /*if ((gfLevelFlags & GF_LVOP_TRAIN) && (inputBusy & IN_ACTION)) InfraRed = TRUE; else InfraRed = false;*/ Infrared = false; } } } // Clear dynamic lights ClearDynamicLights(); ClearFires(); g_Renderer->ClearDynamicLights(); GotLaraSpheres = false; // Update all items InItemControlLoop = true; short itemNum = NextItemActive; while (itemNum != NO_ITEM) { ITEM_INFO* item = &Items[itemNum]; short nextItem = item->nextActive; if (item->afterDeath <= 128) { if (Objects[item->objectNumber].control) Objects[item->objectNumber].control(itemNum); } else { KillItem(itemNum); } itemNum = nextItem; } InItemControlLoop = false; KillMoveItems(); // Update all effects InItemControlLoop = true; short fxNum = NextFxActive; while (fxNum != NO_ITEM) { short nextFx = Effects[fxNum].nextActive; if (Objects[Effects[fxNum].objectNumber].control) Objects[Effects[fxNum].objectNumber].control(fxNum); fxNum = nextFx; } InItemControlLoop = false; KillMoveEffects(); // Update some effect timers if (SmokeCountL) SmokeCountL--; if (SmokeCountR) SmokeCountR--; if (SplashCount) SplashCount--; if (WeaponDelay) WeaponDelay--; if (WeaponEnemyTimer) WeaponEnemyTimer--; if (CurrentLevel != 0) { if (Lara.hasFired) { AlertNearbyGuards(LaraItem); Lara.hasFired = false; } // Is Lara poisoned? if (Lara.poisoned) { if (Lara.poisoned <= 4096) { if (Lara.dpoisoned) ++Lara.dpoisoned; } else { Lara.poisoned = 4096; } if ((gfLevelFlags & 0x80u) != 0 && !Lara.gassed) { if (Lara.dpoisoned) { Lara.dpoisoned -= 8; if (Lara.dpoisoned < 0) Lara.dpoisoned = 0; } } if (Lara.dpoisoned >= 256 && !(Wibble & 0xFF)) { LaraItem->hitPoints -= Lara.poisoned >> (8 - Lara.gassed); PoisonFlags = 16; } } // Control Lara InItemControlLoop = true; Lara.skelebob = NULL; LaraControl(Lara.itemNumber); InItemControlLoop = false; KillMoveItems(); // Update Lara's ponytails HairControl(0, 0, 0); if (level->LaraType == LARA_YOUNG) HairControl(0, 1, 0); } if (UseSpotCam) { // Draw flyby cameras //if (CurrentLevel != 0) // g_Renderer->EnableCinematicBars(true); CalculateSpotCameras(); } else { // Do the standard camera //g_Renderer->EnableCinematicBars(false); TrackCameraInit = false; CalculateCamera(); } //WTF: what is this? It's used everywhere so it has to stay Wibble = (Wibble + 4) & 0xFC; // Update special effects TriggerLaraDrips(); if (SmashedMeshCount) { do { SmashedMeshCount--; FLOOR_INFO* floor = GetFloor( SmashedMesh[SmashedMeshCount]->x, SmashedMesh[SmashedMeshCount]->y, SmashedMesh[SmashedMeshCount]->z, &SmashedMeshRoom[SmashedMeshCount]); int height = GetFloorHeight( floor, SmashedMesh[SmashedMeshCount]->x, SmashedMesh[SmashedMeshCount]->y, SmashedMesh[SmashedMeshCount]->z); TestTriggers(TriggerIndex, 1, 0); floor->stopper = false; SmashedMesh[SmashedMeshCount] = 0; } while (SmashedMeshCount != 0); } UpdateSparks(); UpdateFireSparks(); UpdateSmoke(); UpdateBlood(); UpdateBubbles(); UpdateDebris(); UpdateGunShells(); UpdateSplashes(); UpdateDrips(); UpdateRats(); UpdateBats(); UpdateSpiders(); UpdateShockwaves(); UpdateLightning(); UpdatePulseColor(); AnimateWaterfalls(); if (level->Rumble) RumbleScreen(); SoundEffects(); HealtBarTimer--; GameTimer++; } return GAME_STATUS_NONE; } unsigned __stdcall GameMain(void*) { DB_Log(2, "GameMain - DLL"); printf("GameMain\n"); // We still need legacy matrices because control routines use them MatrixPtr = MatrixStack; DxMatrixPtr = (byte*)malloc(48 * 40); // Initialise legacy memory buffer and game timer InitGameMalloc(); TIME_Init(); // Do a fixed time title image g_Renderer->DoTitleImage(); // Execute the LUA gameflow and play the game g_GameFlow->DoGameflow(); DoTheGame = false; // Finish the thread PostMessageA((HWND)WindowsHandle, 0x10u, 0, 0); _endthreadex(1); return 1; } GAME_STATUS DoTitle(int index) { DB_Log(2, "DoTitle - DLL"); printf("DoTitle\n"); CreditsDone = false; CanLoad = false; // Load the level S_LoadLevelFile(index); int inventoryResult; if (g_GameFlow->TitleType == TITLE_FLYBY) { // Initialise items, effects, lots, camera InitialiseFXArray(true); InitialisePickupDisplay(); InitialiseCamera(); SOUND_Stop(); RequiredStartPos = false; if (InitialiseGame) { GameTimer = 0; RequiredStartPos = false; InitialiseGame = false; } Savegame.Level.Timer = 0; if (CurrentLevel == 1) Savegame.TLCount = 0; LastInventoryItem = -1; DelCutSeqPlayer = 0; TitleControlsLockedOut = false; GameMode = 1; // Initialise flyby cameras InitSpotCamSequences(); InitialiseSpotCam(2); CurrentAtmosphere = 83; UseSpotCam = true; // Play background music //CurrentAtmosphere = ambient; S_CDPlay(CurrentAtmosphere, 1); IsAtmospherePlaying = true; // Initialise ponytails InitialiseHair(); ControlPhase(2, 0); inventoryResult = g_Inventory->DoTitleInventory(); } else { inventoryResult = g_Inventory->DoTitleInventory(); } UseSpotCam = false; S_CDStop(); switch (inventoryResult) { case INV_RESULT_NEW_GAME: return GAME_STATUS_NEW_GAME; case INV_RESULT_LOAD_GAME: return GAME_STATUS_LOAD_GAME; case INV_RESULT_EXIT_GAME: return GAME_STATUS_EXIT_GAME; } return GAME_STATUS_NEW_GAME; } GAME_STATUS DoLevel(int index, int ambient, bool loadFromSavegame) { CreditsDone = false; CanLoad = false; // If not loading a savegame, then clear all the infos if (!loadFromSavegame) { Savegame.Level.Timer = 0; Savegame.Level.Distance = 0; Savegame.Level.AmmoUsed = 0; Savegame.Level.AmmoHits = 0; Savegame.Level.Kills = 0; } // Load the level S_LoadLevelFile(index); // Initialise items, effects, lots, camera InitialiseFXArray(true); InitialisePickupDisplay(); InitialiseCamera(); SOUND_Stop(); // Restore the game? if (loadFromSavegame) { char fileName[255]; ZeroMemory(fileName, 255); sprintf(fileName, "savegame.%d", g_GameFlow->SelectedSaveGame); SaveGame::Load(fileName); Camera.pos.x = LaraItem->pos.xPos + 256; Camera.pos.y = LaraItem->pos.yPos + 256; Camera.pos.z = LaraItem->pos.zPos + 256; Camera.target.x = LaraItem->pos.xPos; Camera.target.y = LaraItem->pos.yPos; Camera.target.z = LaraItem->pos.zPos; RequiredStartPos = false; InitialiseGame = false; g_GameFlow->SelectedSaveGame = 0; } else { RequiredStartPos = false; if (InitialiseGame) { GameTimer = 0; RequiredStartPos = false; InitialiseGame = false; } Savegame.Level.Timer = 0; if (CurrentLevel == 1) Savegame.TLCount = 0; } LastInventoryItem = -1; DelCutSeqPlayer = 0; TitleControlsLockedOut = false; g_Inventory->SetEnterObject(NO_ITEM); g_Inventory->SetSelectedObject(NO_ITEM); // Initialise flyby cameras InitSpotCamSequences(); // Play background music CurrentAtmosphere = ambient; S_CDPlay(CurrentAtmosphere, 1); IsAtmospherePlaying = true; // Initialise ponytails InitialiseHair(); int nframes = 2; GAME_STATUS result = ControlPhase(nframes, 0); g_Renderer->FadeIn(); // The game loop, finally! while (true) { nframes = DrawPhaseGame(); result = ControlPhase(nframes, 0); //printf("LastSpotCam: %d\n", LastSpotCam); if (result == GAME_STATUS_EXIT_TO_TITLE || result == GAME_STATUS_LOAD_GAME || result == GAME_STATUS_LEVEL_COMPLETED) { // Here is the only way for exiting from the loop SOUND_Stop(); S_CDStop(); return result; } Sound_UpdateScene(); } } void TestTriggers(short* data, int heavy, int HeavyFlags) { int flip = -1; int flipAvailable = 0; int newEffect = -1; int switchOff = 0; int switchFlag = 0; short objectNumber = 0; int keyResult = 0; short cameraFlags = 0; short cameraTimer = 0; int spotCamIndex = 0; HeavyTriggered = false; if (!heavy) { Lara.canMonkeySwing = false; Lara.climbStatus = false; } if (!data) return; // Burn Lara if ((*data & 0x1F) == LAVA_TYPE) { if (!heavy && (LaraItem->pos.yPos == LaraItem->floor || Lara.waterStatus)) LavaBurn(LaraItem); if (*data & 0x8000) return; data++; } // Lara can climb if ((*data & 0x1F) == CLIMB_TYPE) { if (!heavy) { short quad = (unsigned short)(LaraItem->pos.yRot + ANGLE(45)) / ANGLE(90); if ((1 << (quad + 8)) & *data) Lara.climbStatus = true; } if (*data & 0x8000) return; data++; } // Lara can monkey if ((*data & 0x1F) == MONKEY_TYPE) { if (!heavy) Lara.canMonkeySwing = true; if (*data & 0x8000) return; data++; } // Trigger triggerer if ((*data & 0x1F) == TRIGTRIGGER_TYPE) { if (!(*data & 0x20) || *data & 0x8000) return; data++; } short triggerType = (*(data++) >> 8) & 0x3F; short flags = *(data++); short timer = flags & 0xFF; if (Camera.type != HEAVY_CAMERA) RefreshCamera(triggerType, data); if (heavy) { switch (triggerType) { case HEAVY: case HEAVYANTITRIGGER: break; case HEAVYSWITCH: if (!HeavyFlags) return; if (HeavyFlags >= 0) { flags &= 0x3E00u; if (flags != HeavyFlags) return; } else { flags |= 0x3E00u; flags += HeavyFlags; } break; default: // Enemies can only activate heavy triggers return; } } short value = 0; switch (triggerType) { case TRIGGER_TYPES::SWITCH: value = *(data++) & 0x3FF; if (flags & 0x100) Items[value].itemFlags[0] = 1; if (!SwitchTrigger(value, timer)) return; objectNumber = Items[value].objectNumber; if (objectNumber >= ID_SWITCH_TYPE1 && objectNumber <= ID_SWITCH_TYPE6 && Items[value].triggerFlags == 5) switchFlag = 1; switchOff = (Items[value].currentAnimState == 1); break; case TRIGGER_TYPES::MONKEY: if (LaraItem->currentAnimState >= 75 && (LaraItem->currentAnimState <= 79 || LaraItem->currentAnimState == 82 || LaraItem->currentAnimState == 83)) break; return; case TRIGGER_TYPES::TIGHTROPE_T: if (LaraItem->currentAnimState >= 119 && LaraItem->currentAnimState <= 127 && LaraItem->currentAnimState != 126) break; return; case TRIGGER_TYPES::CRAWLDUCK_T: if (LaraItem->currentAnimState == 80 || LaraItem->currentAnimState == 81 || LaraItem->currentAnimState == 84 || LaraItem->currentAnimState == 85 || LaraItem->currentAnimState == 86 || LaraItem->currentAnimState == 71 || LaraItem->currentAnimState == 72 || LaraItem->currentAnimState == 105 || LaraItem->currentAnimState == 106) break; return; case TRIGGER_TYPES::CLIMB_T: if (LaraItem->currentAnimState == 10 || LaraItem->currentAnimState == 56 || LaraItem->currentAnimState == 57 || LaraItem->currentAnimState == 58 || LaraItem->currentAnimState == 59 || LaraItem->currentAnimState == 60 || LaraItem->currentAnimState == 61 || LaraItem->currentAnimState == 75) break; return; case TRIGGER_TYPES::PAD: case TRIGGER_TYPES::ANTIPAD: if (LaraItem->pos.yPos == LaraItem->floor) break; return; case TRIGGER_TYPES::KEY: value = *(data++) & 0x3FF; keyResult = KeyTrigger(value); if (keyResult != -1) break; return; case TRIGGER_TYPES::PICKUP: value = *(data++) & 0x3FF; if (!PickupTrigger(value)) return; break; case TRIGGER_TYPES::COMBAT: if (Lara.gunStatus == LG_READY) break; return; case TRIGGER_TYPES::SKELETON_T: Lara.skelebob = 2; break; case TRIGGER_TYPES::HEAVY: case TRIGGER_TYPES::DUMMY: case TRIGGER_TYPES::HEAVYSWITCH: case TRIGGER_TYPES::HEAVYANTITRIGGER: return; default: break; } short targetType = 0; short trigger = 0; ITEM_INFO* item = NULL; ITEM_INFO* cameraItem = NULL; do { trigger = *(data++); value = trigger & 0x3FF; targetType = (trigger >> 10) & 0xF; switch (targetType) { case TO_OBJECT: item = &Items[value]; if (keyResult >= 2 || (triggerType == TRIGGER_TYPES::ANTIPAD || triggerType == TRIGGER_TYPES::ANTITRIGGER || triggerType == TRIGGER_TYPES::HEAVYANTITRIGGER) && item->flags & 0x80) break; if (triggerType == TRIGGER_TYPES::SWITCH) { if (item->flags & 0x40) break; if (item->objectNumber == ID_DART_EMITTER && item->active) break; } item->timer = timer; if (timer != 1) item->timer = 30 * timer; if (triggerType == TRIGGER_TYPES::SWITCH || triggerType == TRIGGER_TYPES::HEAVYSWITCH) { if (HeavyFlags >= 0) { if (switchFlag) item->flags |= (flags & 0x3E00); else item->flags ^= (flags & 0x3E00); if (flags & 0x100) item->flags |= 0x40; } else { if (((flags ^ item->flags) & 0x3E00) == 0x3E00) { item->flags ^= (flags & 0x3E00); if (flags & 0x100) item->flags |= 0x40; } } } else if (triggerType == TRIGGER_TYPES::ANTIPAD || triggerType == TRIGGER_TYPES::ANTITRIGGER || triggerType == TRIGGER_TYPES::HEAVYANTITRIGGER) { if (item->objectNumber == ID_EARTHQUAKE) { item->itemFlags[0] = 0; item->itemFlags[1] = 100; } item->flags &= ~(0x3E00 | 0x4000); if (flags & 0x100) item->flags |= 0x80; if (item->active && Objects[item->objectNumber].intelligent) { item->hitPoints = -16384; DisableBaddieAI(value); KillItem(value); } } else if (flags & 0x3E00) { item->flags |= flags & 0x3E00; } if ((item->flags & 0x3E00) & 0x3E00) { item->flags |= 0x20; if (flags & 0x100) item->flags |= 1; if (!(item->active)) { if (Objects[item->objectNumber].intelligent) { if (item->status != ITEM_DEACTIVATED) { if (item->status == ITEM_INVISIBLE) { item->touchBits = 0; if (EnableBaddieAI(value, 0)) { item->status = ITEM_ACTIVE; AddActiveItem(value); } else { item->status == ITEM_INVISIBLE; AddActiveItem(value); } } } else { item->touchBits = 0; item->status = ITEM_ACTIVE; AddActiveItem(value); EnableBaddieAI(value, 1); } } else { item->touchBits = 0; AddActiveItem(value); item->status = ITEM_ACTIVE; HeavyTriggered = heavy; } } } break; case TO_CAMERA: trigger = *(data++); if (keyResult == 1) break; if (Camera.fixed[value].flags & 0x100) break; Camera.number = value; if (Camera.type == LOOK_CAMERA || Camera.type == COMBAT_CAMERA && !(Camera.fixed[value].flags & 3)) break; if (triggerType == TRIGGER_TYPES::COMBAT) break; if (triggerType == TRIGGER_TYPES::SWITCH && timer && switchOff) break; if (Camera.number != Camera.last || triggerType == TRIGGER_TYPES::SWITCH) { Camera.timer = (trigger & 0xFF) * 30; if (trigger & 0x100) Camera.fixed[Camera.number].flags |= 0x100; Camera.speed = ((trigger & 0x3E00) >> 6) + 1; Camera.type = heavy ? HEAVY_CAMERA : FIXED_CAMERA; } break; case TO_FLYBY: trigger = *(data++); if (keyResult == 1) break; if (triggerType == TRIGGER_TYPES::ANTIPAD || triggerType == TRIGGER_TYPES::ANTITRIGGER || triggerType == TRIGGER_TYPES::HEAVYANTITRIGGER) UseSpotCam = false; else { spotCamIndex = 0; if (SpotCamRemap[value] != 0) { for (int i = 0; i < SpotCamRemap[value]; i++) { spotCamIndex += CameraCnt[i]; } } if (!(SpotCam[spotCamIndex].flags & SCF_CAMERA_ONE_SHOT)) { if (trigger & 0x100) SpotCam[spotCamIndex].flags |= SCF_CAMERA_ONE_SHOT; if (!UseSpotCam || CurrentLevel == 0) { UseSpotCam = true; if (LastSpotCam != value) TrackCameraInit = false; InitialiseSpotCam(value); } } } break; case TO_TARGET: cameraItem = &Items[value]; break; case TO_SINK: Lara.currentActive = value + 1; break; case TO_FLIPMAP: flipAvailable = true; if (FlipMap[value] & 0x100) break; if (triggerType == TRIGGER_TYPES::SWITCH) FlipMap[value] ^= (flags & 0x3E00); else if (flags & 0x3E00) FlipMap[value] |= (flags & 0x3E00); if ((FlipMap[value] & 0x3E00) == 0x3E00) { if (flags & 0x100) FlipMap[value] |= 0x100; if (!FlipStatus) flip = value; } else if (FlipStatus) flip = value; break; case TO_FLIPON: flipAvailable = true; if ((FlipMap[value] & 0x3E00) == 0x3E00 && !FlipStatus) flip = value; break; case TO_FLIPOFF: flipAvailable = true; if ((FlipMap[value] & 0x3E00) == 0x3E00 && FlipStatus) flip = value; break; case TO_FLIPEFFECT: TriggerTimer = timer; newEffect = value; break; case TO_FINISH: RequiredStartPos = false; LevelComplete = CurrentLevel + 1; break; case TO_CD: PlaySoundTrack(value, flags); break; case TO_CUTSCENE: // TODO: not used for now break; case TO_LUA_SCRIPT: trigger = *(data++); g_GameScript->ExecuteTrigger(trigger & 0x7FFF); break; default: break; } } while (!(trigger & 0x8000)); if (cameraItem && (Camera.type == FIXED_CAMERA || Camera.type == HEAVY_CAMERA)) Camera.item = cameraItem; if (flip != -1) DoFlipMap(flip); if (newEffect != -1 && (flip || !flipAvailable)) { FlipEffect = newEffect; FlipTimer = 0; } } void UpdateSky() { GameScriptLevel* level = g_GameFlow->GetLevel(CurrentLevel); if (level->Layer1.Enabled) { SkyPos1 += level->Layer1.CloudSpeed; if (SkyPos1 <= 9728) { if (SkyPos1 < 0) SkyPos1 += 9728; } else { SkyPos1 -= 9728; } } if (level->Layer1.Enabled) { SkyPos2 += level->Layer2.CloudSpeed; if (SkyPos2 <= 9728) { if (SkyPos2 < 0) SkyPos2 += 9728; } else { SkyPos2 -= 9728; } } } void ActivateKey() { KeyTriggerActive = 1; } short GetDoor(FLOOR_INFO* floor) { if (!floor->index) return NO_ROOM; short* data = &FloorData[floor->index]; short type = *(data++); if (((type & DATA_TYPE) == TILT_TYPE) || ((type & DATA_TYPE) == SPLIT1) || ((type & DATA_TYPE) == SPLIT2) || ((type & DATA_TYPE) == NOCOLF1B) || ((type & DATA_TYPE) == NOCOLF1T) || ((type & DATA_TYPE) == NOCOLF2B) || ((type & DATA_TYPE) == NOCOLF2T)) { if (type & END_BIT) return NO_ROOM; data++; type = *(data++); } if (((type & DATA_TYPE) == ROOF_TYPE) || ((type & DATA_TYPE) == SPLIT3) || ((type & DATA_TYPE) == SPLIT4) || ((type & DATA_TYPE) == NOCOLC1B) || ((type & DATA_TYPE) == NOCOLC1T) || ((type & DATA_TYPE) == NOCOLC2B) || ((type & DATA_TYPE) == NOCOLC2T)) { if (type & END_BIT) return NO_ROOM; data++; type = *(data++); } if ((type & DATA_TYPE) == DOOR_TYPE) return (*data); return NO_ROOM; } void TranslateItem(ITEM_INFO* item, int x, int y, int z) { int c = COS(item->pos.yRot); int s = SIN(item->pos.yRot); item->pos.xPos += (c * x + s * z) >> W2V_SHIFT; item->pos.yPos += y; item->pos.zPos += (-s * x + c * z) >> W2V_SHIFT; } int CheckNoColFloorTriangle(FLOOR_INFO* floor, short x, short z) { if (!floor->index) return 0; short func = FloorData[floor->index] & 0x1F; if (func != NOCOLF1T && func != NOCOLF1B && func != NOCOLF2T && func != NOCOLF2B) return 0; int dx = x & 0x3FF; int dz = z & 0x3FF; switch (func) { case NOCOLF1T: if (dx <= 1024 - dz) return -1; break; case NOCOLF1B: if (dx > 1024 - dz) return -1; break; case NOCOLF2T: if (dx <= dz) return -1; break; case NOCOLF2B: if (dx > dz) return -1; break; } return 1; } int GetWaterSurface(int x, int y, int z, short roomNumber) { ROOM_INFO* room = &Rooms[roomNumber]; FLOOR_INFO* floor = &room->floor[((z - room->z) >> WALL_SHIFT) + ((x - room->x) >> WALL_SHIFT)* room->xSize]; if (room->flags & ENV_FLAG_WATER) { while (floor->skyRoom != NO_ROOM) { room = &Rooms[floor->skyRoom]; if (!(room->flags & ENV_FLAG_WATER)) return (floor->ceiling << 8); floor = &room->floor[((z - room->z) >> WALL_SHIFT) + ((x - room->x) >> WALL_SHIFT)* room->xSize]; } return NO_HEIGHT; } else { while (floor->pitRoom != NO_ROOM) { room = &Rooms[floor->pitRoom]; if (room->flags & ENV_FLAG_WATER) return (floor->floor << 8); floor = &room->floor[((z - room->z) >> WALL_SHIFT) + ((x - room->x) >> WALL_SHIFT)* room->xSize]; } } return NO_HEIGHT; } void KillMoveItems() { if (ItemNewRoomNo > 0) { for (int i = 0; i < ItemNewRoomNo; i++) { short itemNumber = ItemNewRooms[2 * i]; if (itemNumber >= 0) ItemNewRoom(itemNumber, ItemNewRooms[2 * i + 1]); else KillItem(itemNumber & 0x7FFF); } } ItemNewRoomNo = 0; } void KillMoveEffects() { if (ItemNewRoomNo > 0) { for (int i = 0; i < ItemNewRoomNo; i++) { short itemNumber = ItemNewRooms[2 * i]; if (itemNumber >= 0) EffectNewRoom(itemNumber, ItemNewRooms[2 * i + 1]); else KillEffect(itemNumber & 0x7FFF); } } ItemNewRoomNo = 0; } int GetChange(ITEM_INFO* item, ANIM_STRUCT* anim) { if (item->currentAnimState == item->goalAnimState) return 0; if (anim->numberChanges <= 0) return 0; for (int i = 0; i < anim->numberChanges; i++) { CHANGE_STRUCT* change = &Changes[anim->changeIndex + i]; if (change->goalAnimState == item->goalAnimState) { for (int j = 0; j < change->numberRanges; j++) { RANGE_STRUCT* range = &Ranges[change->rangeIndex + j]; if (item->frameNumber >= range->startFrame && item->frameNumber <= range->endFrame) { item->animNumber = range->linkAnimNum; item->frameNumber = range->linkFrameNum; return 1; } } } } return 0; } void AlterFloorHeight(ITEM_INFO* item, int height) { FLOOR_INFO* floor; FLOOR_INFO* ceiling; BOX_INFO* box; short roomNumber; int flag = 0; if (abs(height)) { flag = 1; if (height >= 0) height++; else height--; } roomNumber = item->roomNumber; floor = GetFloor(item->pos.xPos, item->pos.yPos, item->pos.zPos, &roomNumber); ceiling = GetFloor(item->pos.xPos, height + item->pos.yPos - WALL_SIZE, item->pos.zPos, &roomNumber); if (floor->floor == NO_HEIGHT / STEP_SIZE) { floor->floor = ceiling->ceiling + height / STEP_SIZE; } else { floor->floor += height / STEP_SIZE; if (floor->floor == ceiling->ceiling && !flag) floor->floor = NO_HEIGHT / STEP_SIZE; } box = &Boxes[floor->box]; if (box->overlapIndex & BLOCKABLE) { if (height >= 0) box->overlapIndex &= ~BLOCKED; else box->overlapIndex |= BLOCKED; } } FLOOR_INFO* GetFloor(int x, int y, int z, short* roomNumber) { ROOM_INFO* r; FLOOR_INFO* floor; short data; int xFloor = 0; int yFloor = 0; short roomDoor = 0; int retval; r = &Rooms[*roomNumber]; do { xFloor = (z - r->z) >> WALL_SHIFT; yFloor = (x - r->x) >> WALL_SHIFT; if (xFloor <= 0) { xFloor = 0; if (yFloor < 1) yFloor = 1; else if (yFloor > r->ySize - 2) yFloor = r->ySize - 2; } else if (xFloor >= r->xSize - 1) { xFloor = r->xSize - 1; if (yFloor < 1) yFloor = 1; else if (yFloor > r->ySize - 2) yFloor = r->ySize - 2; } else if (yFloor >= 0) { if (yFloor >= r->ySize) yFloor = r->ySize - 1; } else { yFloor = 0; } floor = &r->floor[xFloor + (yFloor * r->xSize)]; data = GetDoor(floor); if (data != NO_ROOM) { *roomNumber = data; r = &Rooms[data]; } } while (data != NO_ROOM); if (y < floor->floor * 256) { if (y < floor->ceiling * 256 && floor->skyRoom != NO_ROOM) { do { int noCollision = CheckNoColCeilingTriangle(floor, x, z); if (noCollision == 1 || noCollision == -1 && y >= r->maxceiling) break; *roomNumber = floor->skyRoom; r = &Rooms[floor->skyRoom]; floor = &XZ_GET_SECTOR(r, x - r->x, z - r->z); if (y >= floor->ceiling * 256) break; } while (floor->skyRoom != NO_ROOM); } } else if (floor->pitRoom != NO_ROOM) { do { int noCollision = CheckNoColFloorTriangle(floor, x, z); if (noCollision == 1 || noCollision == -1 && y < r->minfloor) break; *roomNumber = floor->pitRoom; r = &Rooms[floor->pitRoom]; floor = &XZ_GET_SECTOR(r, x - r->x, z - r->z); if (y < floor->floor * 256) break; } while (floor->pitRoom != NO_ROOM); } return floor; } int CheckNoColFloorTriangle(FLOOR_INFO* floor, int x, int z) { if (!floor->index) return 0; short* data = &FloorData[floor->index]; short type = *(data) & DATA_TYPE; if (type == NOCOLF1T || type == NOCOLF1B || type == NOCOLF2T || type == NOCOLF2B) { int dx = x & 1023; int dz = z & 1023; if (type == NOCOLF1T && dx <= (SECTOR(1) - dz)) return -1; else if (type == NOCOLF1B && dx > (SECTOR(1) - dz)) return -1; else if (type == NOCOLF2T && dx <= dz) return -1; else if (type == NOCOLF2B && dx > dz) return -1; else return 1; } return 0; } int CheckNoColCeilingTriangle(FLOOR_INFO * floor, int x, int z) { if (!floor->index) return 0; short* data = &FloorData[floor->index]; short type = *(data) & DATA_TYPE; if (type == TILT_TYPE || type == SPLIT1 || type == SPLIT2 || type == NOCOLF1T || type == NOCOLF1B || type == NOCOLF2T || type == NOCOLF2B) // gibby { if (*(data) & END_BIT) return 0; type = *(data + 2) & DATA_TYPE; } if (type == NOCOLC1T || type == NOCOLC1B || type == NOCOLC2T || type == NOCOLC2B) { int dx = x & 1023; int dz = z & 1023; if (type == NOCOLC1T && dx <= (SECTOR(1) - dz)) return -1; else if (type == NOCOLC1B && dx > (SECTOR(1) - dz)) return -1; else if (type == NOCOLC2T && dx <= dz) return -1; else if (type == NOCOLC2B && dx > dz) return -1; else return 1; } return 0; } int GetFloorHeight(FLOOR_INFO* floor, int x, int y, int z) { TiltYOffset = 0; TiltXOffset = 0; OnObject = 0; HeightType = WALL; ROOM_INFO* r; while (floor->pitRoom != NO_ROOM) { if (CheckNoColFloorTriangle(floor, x, z) == 1) break; r = &Rooms[floor->pitRoom]; floor = &XZ_GET_SECTOR(r, x - r->x, z - r->z); } int height = floor->floor * 256; if (height == NO_HEIGHT) return height; TriggerIndex = NULL; if (floor->index == 0) return height; short* data = &FloorData[floor->index]; short type, hadj; int xOff, yOff, trigger; ITEM_INFO* item; OBJECT_INFO* obj; int tilts, t0, t1, t2, t3, t4, dx, dz, h1, h2; do { type = *(data++); switch (type & DATA_TYPE) { case DOOR_TYPE: case ROOF_TYPE: case SPLIT3: case SPLIT4: case NOCOLC1T: case NOCOLC1B: case NOCOLC2T: case NOCOLC2B: data++; break; case TILT_TYPE: TiltXOffset = xOff = (*data >> 8); TiltYOffset = yOff = *(char *) data; if ((abs(xOff)) > 2 || (abs(yOff)) > 2) HeightType = BIG_SLOPE; else HeightType = SMALL_SLOPE; if (xOff >= 0) height += (xOff * ((-1 - z) & 1023) >> 2); else height -= (xOff * (z & 1023) >> 2); if (yOff >= 0) height += yOff * ((-1 - x) & 1023) >> 2; else height -= yOff * (x & 1023) >> 2; data++; break; case TRIGGER_TYPE: if (!TriggerIndex) TriggerIndex = data - 1; data++; do { trigger = *(data++); if (TRIG_BITS(trigger) != TO_OBJECT) { if (TRIG_BITS(trigger) == TO_CAMERA || TRIG_BITS(trigger) == TO_FLYBY) { trigger = *(data++); } } else { item = &Items[trigger & VALUE_BITS]; obj = &Objects[item->objectNumber]; if (obj->floor && !(item->flags & 0x8000)) { (obj->floor)(item, x, y, z, &height); } } } while (!(trigger& END_BIT)); break; case LAVA_TYPE: TriggerIndex = data - 1; break; case CLIMB_TYPE: case MONKEY_TYPE: case TRIGTRIGGER_TYPE: if (!TriggerIndex) TriggerIndex = data - 1; break; case SPLIT1: case SPLIT2: case NOCOLF1T: case NOCOLF1B: case NOCOLF2T: case NOCOLF2B: tilts = *data; t0 = tilts & 15; t1 = (tilts >> 4) & 15; t2 = (tilts >> 8) & 15; t3 = (tilts >> 12) & 15; dx = x & 1023; dz = z & 1023; xOff = yOff = 0; HeightType = SPLIT_TRI; if ((type & DATA_TYPE) == SPLIT1 || (type & DATA_TYPE) == NOCOLF1T || (type & DATA_TYPE) == NOCOLF1B) { if (dx <= (1024 - dz)) { hadj = (type >> 10) & 0x1F; if (hadj & 0x10) hadj |= 0xfff0; height += 256 * hadj; xOff = t2 - t1; yOff = t0 - t1; } else { hadj = (type >> 5) & 0x1F; if (hadj & 0x10) hadj |= 0xFFF0; height += 256 * hadj; xOff = t3 - t0; yOff = t3 - t2; } } else { if (dx <= dz) { hadj = (type >> 10) & 0x1f; if (hadj & 0x10) hadj |= 0xfff0; height += 256 * hadj; xOff = t2 - t1; yOff = t3 - t2; } else { hadj = (type >> 5) & 0x1f; if (hadj & 0x10) hadj |= 0xfff0; height += 256 * hadj; xOff = t3 - t0; yOff = t0 - t1; } } TiltXOffset = xOff; TiltYOffset = yOff; if ((abs(xOff)) > 2 || (abs(yOff)) > 2) HeightType = DIAGONAL; else if (HeightType != SPLIT_TRI) HeightType = SMALL_SLOPE; if (xOff >= 0) height += xOff * ((-1 - z) & 1023) >> 2; else height -= xOff * (z & 1023) >> 2; if (yOff >= 0) height += yOff * ((-1 - x) & 1023) >> 2; else height -= yOff * (x & 1023) >> 2; data++; break; default: break; } } while (!(type & END_BIT)); return height; } void UpdateBats() { if (!Objects[ID_BATS].loaded) return; short* bounds = GetBoundsAccurate(LaraItem); int x1 = LaraItem->pos.xPos + bounds[0] - (bounds[0] >> 2); int x2 = LaraItem->pos.xPos + bounds[1] - (bounds[1] >> 2); int y1 = LaraItem->pos.yPos + bounds[2] - (bounds[2] >> 2); int y2 = LaraItem->pos.yPos + bounds[3] - (bounds[3] >> 2); int z1 = LaraItem->pos.zPos + bounds[4] - (bounds[4] >> 2); int z2 = LaraItem->pos.zPos + bounds[5] - (bounds[5] >> 2); int minDistance = 0xFFFFFFF; // v40 int minIndex = -1; for (int i=0;i<64;i++) { BAT_STRUCT* bat = &Bats[i]; if (!bat->on) continue; if ((Lara.burn || LaraItem->hitPoints <= 0) && bat->counter > 90 && !(GetRandomControl() & 7)) bat->counter = 90; if (!(--bat->counter)) { bat->on = 0; continue; } if (!(GetRandomControl() & 7)) { bat->laraTarget = GetRandomControl() % 640 + 128; bat->xTarget = (GetRandomControl() & 0x7F) - 64; bat->zTarget = (GetRandomControl() & 0x7F) - 64; } short angles[2]; phd_GetVectorAngles( LaraItem->pos.xPos + 8 * bat->xTarget - bat->pos.xPos, LaraItem->pos.yPos - bat->laraTarget - bat->pos.yPos, LaraItem->pos.zPos + 8 * bat->zTarget - bat->pos.zPos, angles); int distance = SQUARE(LaraItem->pos.zPos - bat->pos.zPos) + SQUARE(LaraItem->pos.xPos - bat->pos.xPos); if (distance < minDistance) { minDistance = distance; minIndex = i; } distance = SQRT_ASM(distance) / 8; if (distance <= 128) { if (distance < 48) distance = 48; } else { distance = 128; } if (bat->speed < distance) bat->speed++; else if (bat->speed > distance) bat->speed--; if (bat->counter > 90) { int speed = bat->speed * 128; short xAngle = abs(angles[1] - bat->pos.yRot) >> 3; short yAngle = abs(angles[0] - bat->pos.yRot) >> 3; if (xAngle <= -speed) xAngle = -speed; else if (xAngle >= speed) xAngle = speed; if (yAngle <= -speed) yAngle = -speed; else if (yAngle >= speed) yAngle = speed; bat->pos.yRot += yAngle; bat->pos.xRot += xAngle; } int sp = bat->speed * SIN(bat->pos.xRot) >> W2V_SHIFT; bat->pos.xPos += sp * SIN(bat->pos.yRot) >> W2V_SHIFT; bat->pos.yPos += bat->speed * SIN(-bat->pos.xRot) >> W2V_SHIFT; bat->pos.zPos += sp * COS(bat->pos.yRot) >> W2V_SHIFT; if ((i % 2 == 0) && bat->pos.xPos > x1 && bat->pos.xPos < x2 && bat->pos.yPos > y1 && bat->pos.yPos < y2 && bat->pos.zPos > z1 && bat->pos.zPos < z2) { TriggerBlood(bat->pos.xPos, bat->pos.yPos, bat->pos.zPos, 2 * GetRandomControl(), 2); if (LaraItem->hitPoints > 0) LaraItem->hitPoints -= 2; } } if (minIndex != -1) { BAT_STRUCT* bat = &Bats[minIndex]; if (!(GetRandomControl() & 4)) SoundEffect(SFX_BATS_1, &bat->pos, 0); } } void UpdateDebris() { for (int i = 0; i < 256; i++) { DEBRIS_STRUCT* debris = &Debris[i]; if (debris->on) { debris->yVel += debris->gravity; if (debris->yVel > 4096) debris->yVel = 4096; debris->speed -= debris->speed >> 4; debris->x += debris->speed * SIN(debris->dir) >> W2V_SHIFT; debris->y += debris->yVel >> 4; debris->z += debris->speed * COS(debris->dir) >> W2V_SHIFT; FLOOR_INFO* floor = GetFloor(debris->x, debris->y, debris->z, &debris->roomNumber); int height = GetFloorHeight(floor, debris->x, debris->y, debris->z); int ceiling = GetCeiling(floor, debris->x, debris->y, debris->z); if (debris->y >= height || debris->y < ceiling) debris->on--; else { debris->xRot += debris->yVel >> 6; if (debris->yVel) debris->yRot += debris->speed >> 5; if (Rooms[debris->roomNumber].flags & ENV_FLAG_WATER) debris->yVel -= debris->yVel >> 2; } } } } void UpdateRats() { if (Objects[ID_RATS].loaded) { for (int i = 0; i < 32; i++) { RAT_STRUCT* rat = &Rats[i]; if (rat->on) { int oldX = rat->pos.xPos; int oldY = rat->pos.yPos; int oldZ = rat->pos.zPos; rat->pos.xPos += rat->speed * SIN(rat->pos.yRot) >> W2V_SHIFT; rat->pos.yPos += rat->fallspeed; rat->pos.zPos += rat->speed * COS(rat->pos.yRot) >> W2V_SHIFT; rat->fallspeed += 6; int dx = LaraItem->pos.xPos - rat->pos.xPos; int dy = LaraItem->pos.yPos - rat->pos.yPos; int dz = LaraItem->pos.zPos - rat->pos.zPos; short angle; if (rat->flags >= 170) angle = rat->pos.yRot - ATAN(dz, dx); else angle = ATAN(dz, dx) - rat->pos.yRot; if (abs(dx) < 85 && abs(dy) < 85 && abs(dz) < 85) { LaraItem->hitPoints--; LaraItem->hitStatus = true; } if (rat->flags & 1) { if (abs(dz) + abs(dx) <= 1024) { if (rat->speed & 1) rat->pos.yRot += 512; else rat->pos.yRot -= 512; rat->speed = 48 - (abs(angle) >> 10); } else { if (rat->speed < (i & 0x1F) + 24) rat->speed++; if (abs(angle) >= 2048) { if (angle >= 0) rat->pos.yRot += 1024; else rat->pos.yRot -= 1024; } else { rat->pos.yRot += 8 * (Wibble - i); } } } __int16 oldRoomNumber = rat->roomNumber; FLOOR_INFO* floor = GetFloor(rat->pos.xPos, rat->pos.yPos, rat->pos.zPos, &rat->roomNumber); int height = GetFloorHeight(floor, rat->pos.xPos, rat->pos.yPos, rat->pos.zPos); if (height < rat->pos.yPos - 1280 || height == NO_HEIGHT) { if (rat->flags > 170) { rat->on = false; NextRat = 0; } if (angle <= 0) rat->pos.yRot -= ANGLE(90); else rat->pos.yRot += ANGLE(90); rat->pos.xPos = oldX; rat->pos.yPos = oldY; rat->pos.zPos = oldZ; rat->fallspeed = 0; } else { if (height >= rat->pos.yPos - 64) { if (rat->pos.yPos <= height) { if (rat->fallspeed >= 500 || rat->flags >= 200) { rat->on = 0; NextRat = 0; } else { rat->pos.xRot = -128 * rat->fallspeed; } } else { rat->pos.yPos = height; rat->fallspeed = 0; rat->flags |= 1; } } else { rat->pos.xRot = 14336; rat->pos.xPos = oldX; rat->pos.yPos = oldY - 24; rat->pos.zPos = oldZ; rat->fallspeed = 0; } } if (!(Wibble & 0x3C)) rat->flags += 2; ROOM_INFO* r = &Rooms[rat->roomNumber]; if (r->flags & ENV_FLAG_WATER) { rat->fallspeed = 0; rat->speed = 16; rat->pos.yPos = r->maxceiling + 50; if (Rooms[oldRoomNumber].flags & ENV_FLAG_WATER) { if (!(GetRandomControl() & 0xF)) { SetupRipple(rat->pos.xPos, r->maxceiling, rat->pos.zPos, (GetRandomControl() & 3) + 48, 2); } } else { AddWaterSparks(rat->pos.xPos, r->maxceiling, rat->pos.zPos, 16); SetupRipple(rat->pos.xPos, r->maxceiling, rat->pos.zPos, (GetRandomControl() & 3) + 48, 2); SoundEffect(SFX_RATSPLASH, &rat->pos, 0); } } if (!i && !(GetRandomControl() & 4)) SoundEffect(SFX_RATS_1, &rat->pos, 0); } } } } /*char __usercall UpdateSpiders@ (signed int a1@ ) { if (Objects[ID_SPIDER].loaded) { for (int i = 0; i < 64; i++) { SPIDER_STRUCT* spider = &Spiders[i]; if (spider->on) { } } } LOBYTE(a1) = *(&objects[95] + 50); if (*(&objects[95] + 50) & 1) { v1 = Spiders; v23 = 0; do { if (BYTE2(v1[1].yPos)) { LOWORD(a1) = v1->yRot; v2 = SHIWORD(v1[1].xPos); v3 = v1->yPos; v4 = (a1 >> 3) & 0x1FFE; v25 = v1->xPos; v5 = v1->zPos; v6 = v1[1].yPos; v1->xPos += v2 * 4 * rcossin_tbl[v4] >> 14; v26 = v3; v1->yPos = v3 + v6; v27 = v5; v7 = v5 + (v2 * 4 * rcossin_tbl[v4 + 1] >> 14); LOWORD(v1[1].yPos) = v6 + 6; v1->zPos = v7; v8 = v3 + v6; v9 = LaraItem->pos.zPos - v1->zPos; v10 = LaraItem->pos.xPos - v1->xPos; v11 = LaraItem->pos.yPos - v8; v12 = phd_atan(v9, v10) - v1->yRot; v24 = v12; v13 = abs(v9); if (v13 < 85 && abs(v11) < 85 && abs(v10) < 85) { LaraItem->hit_points -= 3; LaraItem->_bf15ea |= 0x10u; TriggerBlood(v1->xPos, v1->yPos, v1->zPos, v1->yRot, 1); v12 = v24; } if (HIBYTE(v1[1].yPos)) { if (v13 + abs(v10) <= 768) { if (v1[1].xPos & 0x10000) v1->yRot += 512; else v1->yRot -= 512; HIWORD(v1[1].xPos) = 48 - (abs(v12) >> 10); } else { v14 = HIWORD(v1[1].xPos); if (v14 < (v23 & 0x1F) + 24) HIWORD(v1[1].xPos) = v14 + 1; if (abs(v12) >= 2048) { if (v12 >= 0) v1->yRot += 1024; else v1->yRot -= 1024; } else { v1->yRot += 8 * (wibble - v23); } } } v15 = v1 + 1; v16 = GetFloor(v1->xPos, v1->yPos, v1->zPos, &v1[1]); v17 = GetHeight(v16, v1->xPos, v1->yPos, v1->zPos); v18 = v1->yPos; if (v17 >= v18 - 1280 || v17 == -32512) { if (v17 >= v18 - 64) { if (v18 <= v17) { if (SLOWORD(v1[1].yPos) >= 500) { BYTE2(v1[1].yPos) = 0; NextSpider = 0; } else { v1->xRot = -128 * LOWORD(v1[1].yPos); } } else { v1->yPos = v17; LOWORD(v1[1].yPos) = 0; HIBYTE(v1[1].yPos) = 1; } } else { v1->xRot = 14336; v1->xPos = v25; v1->yPos = v26 - 8; if (!(GetRandomControl() & 0x1F)) v1->yRot += -32768; LOWORD(v1[1].yPos) = 0; v1->zPos = v27; } } else { if (v24 <= 0) v1->yRot -= 0x4000; else v1->yRot += 0x4000; v1->xPos = v25; v1->yPos = v26; v1->zPos = v27; LOWORD(v1[1].yPos) = 0; } if (v1->yPos < room[SLOWORD(v15->xPos)].maxceiling + 50) { v19 = room[SLOWORD(v15->xPos)].maxceiling; LOWORD(v1[1].yPos) = 1; v1->yRot += -32768; v1->yPos = v19 + 50; } if (!v23 && !(GetRandomControl() & 4)) SoundEffect(276, v1, 0); } v1 = (v1 + 26); a1 = v23 + 1; v21 = __OFSUB__(v23 + 1, 64); v20 = v23++ - 63 < 0; } while (v20 ^ v21); } return a1; }*/ int LOS(GAME_VECTOR* start, GAME_VECTOR* end) { int result1, result2; end->roomNumber = start->roomNumber; if (abs(end->z - start->z) > abs(end->x - start->x)) { result1 = xLOS(start, end); result2 = zLOS(start, end); } else { result1 = zLOS(start, end); result2 = xLOS(start, end); } if (result2) { GetFloor(end->x, end->y, end->z, &end->roomNumber); if (ClipTarget(start, end) && result1 == 1 && result2 == 1) { return 1; } } return 0; } int xLOS(GAME_VECTOR* start, GAME_VECTOR* end) { int dx, dy, dz, x, y, z, flag; short room, room2; FLOOR_INFO* floor; dx = end->x - start->x; if (!dx) return 1; dy = (end->y - start->y << 10) / dx; dz = (end->z - start->z << 10) / dx; number_los_rooms = 1; los_rooms[0] = start->roomNumber; room = start->roomNumber; room2 = start->roomNumber; flag = 1; if (dx < 0) { x = start->x & 0xFFFFFC00; y = ((x - start->x) * dy >> 10) + start->y; z = ((x - start->x) * dz >> 10) + start->z; while (x > end->x) { floor = GetFloor(x, y, z, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x, y, z) || y < GetCeiling(floor, x, y, z)) { flag = -1; break; } floor = GetFloor(x - 1, y, z, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x - 1, y, z) || y < GetCeiling(floor, x - 1, y, z)) { flag = 0; break; } x -= 1024; y -= dy; z -= dz; } if (flag != 1) { end->x = x; end->y = y; end->z = z; } end->roomNumber = flag ? room : room2; } else { x = start->x | 0x3FF; y = ((x - start->x) * dy >> 10) + start->y; z = ((x - start->x) * dz >> 10) + start->z; while (x < end->x) { floor = GetFloor(x, y, z, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x, y, z) || y < GetCeiling(floor, x, y, z)) { flag = -1; break; } floor = GetFloor(x + 1, y, z, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x + 1, y, z) || y < GetCeiling(floor, x + 1, y, z)) { flag = 0; break; } x += 1024; y += dy; z += dz; } if (flag != 1) { end->x = x; end->y = y; end->z = z; } end->roomNumber = flag ? room : room2; } return flag; } int zLOS(GAME_VECTOR* start, GAME_VECTOR* end) { int dx, dy, dz, x, y, z, flag; short room, room2; FLOOR_INFO* floor; dz = end->z - start->z; if (!dz) return 1; dx = (end->x - start->x << 10) / dz; dy = (end->y - start->y << 10) / dz; number_los_rooms = 1; los_rooms[0] = start->roomNumber; room = start->roomNumber; room2 = start->roomNumber; flag = 1; if (dz < 0) { z = start->z & 0xFFFFFC00; x = ((z - start->z) * dx >> 10) + start->x; y = ((z - start->z) * dy >> 10) + start->y; while (z > end->z) { floor = GetFloor(x, y, z, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x, y, z) || y < GetCeiling(floor, x, y, z)) { flag = -1; break; } floor = GetFloor(x, y, z - 1, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x, y, z - 1) || y < GetCeiling(floor, x, y, z - 1)) { flag = 0; break; } z -= 1024; x -= dx; y -= dy; } if (flag != 1) { end->x = x; end->y = y; end->z = z; } end->roomNumber = flag ? room : room2; } else { z = start->z | 0x3FF; x = ((z - start->z) * dx >> 10) + start->x; y = ((z - start->z) * dy >> 10) + start->y; while (z < end->z) { floor = GetFloor(x, y, z, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x, y, z) || y < GetCeiling(floor, x, y, z)) { flag = -1; break; } floor = GetFloor(x, y, z + 1, &room); if (room != room2) { room2 = room; los_rooms[number_los_rooms] = room; ++number_los_rooms; } if (y > GetFloorHeight(floor, x, y, z + 1) || y < GetCeiling(floor, x, y, z + 1)) { flag = 0; break; } z += 1024; x += dx; y += dy; } if (flag != 1) { end->x = x; end->y = y; end->z = z; } end->roomNumber = flag ? room : room2; } return flag; } int ClipTarget(GAME_VECTOR* start, GAME_VECTOR* target) { short room; int x, y, z, wx, wy, wz; room = target->roomNumber; if (target->y > GetFloorHeight(GetFloor(target->x, target->y, target->z, &room), target->x, target->y, target->z)) { x = (7 * (target->x - start->x) >> 3) + start->x; y = (7 * (target->y - start->y) >> 3) + start->y; z = (7 * (target->z - start->z) >> 3) + start->z; for (int i = 3; i > 0; --i) { wx = ((target->x - x) * i >> 2) + x; wy = ((target->y - y) * i >> 2) + y; wz = ((target->z - z) * i >> 2) + z; if (wy < GetFloorHeight(GetFloor(wx, wy, wz, &room), wx, wy, wz)) break; } target->x = wx; target->y = wy; target->z = wz; target->roomNumber = room; return 0; } room = target->roomNumber; if (target->y < GetCeiling(GetFloor(target->x, target->y, target->z, &room), target->x, target->y, target->z)) { x = (7 * (target->x - start->x) >> 3) + start->x; y = (7 * (target->y - start->y) >> 3) + start->y; z = (7 * (target->z - start->z) >> 3) + start->z; for (int i = 3; i > 0; --i) { wx = ((target->x - x) * i >> 2) + x; wy = ((target->y - y) * i >> 2) + y; wz = ((target->z - z) * i >> 2) + z; if (wy > GetCeiling(GetFloor(wx, wy, wz, &room), wx, wy, wz)) break; } target->x = wx; target->y = wy; target->z = wz; target->roomNumber = room; return 0; } return 1; } void Inject_Control() { INJECT(0x00416760, TestTriggers); INJECT(0x004167B0, TestTriggers); INJECT(0x00415960, TranslateItem); INJECT(0x00415B20, GetFloor); }