scummvm/engines/icb/line_of_sight.h
2020-10-06 14:32:14 +02:00

234 lines
9.7 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1999-2000 Revolution Software Ltd.
* This code is based on source code created by Revolution Software,
* used with permission.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef ICB_LINEOFSIGHT_H_INCLUDED
#define ICB_LINEOFSIGHT_H_INCLUDED
#include "engines/icb/p4.h"
#include "engines/icb/debug.h"
#include "engines/icb/event_list.h"
#include "engines/icb/event_manager.h"
#include "engines/icb/common/px_linkeddatafile.h"
#include "engines/icb/common/px_route_barriers.h"
#include "engines/icb/common/px_string.h"
#include "engines/icb/common/px_3drealpoint.h"
namespace ICB {
#define LOS_LOG "LOS_log.txt"
// This sets the default field-of-view for mega characters.
#define LOS_DEFAULT_MEGA_FIELD_OF_VIEW 180
// This sets the default view height for mega characters (+/- this linear distance in cm).
// BIG NOTE: This value must be LESS than the difference between the crouched height and the normal eye-height,
// otherwise LOS will always return false when one mega is crouched and another is standing.
#define LOS_DEFAULT_OBJECT_HEIGHT_OF_VIEW (100 * REAL_ONE)
// This sets how far an object can see by default.
#define LOS_DEFAULT_OBJECT_SEEING_DISTANCE 2000
// This defines the default number of subscribers to process per cycle of the LOS logic.
#define LOS_DEFAULT_SUBSCRIBERS_PER_CYCLE 3
// This is the size of the 2D and 1D tables.
#define LOS_1D_SIZE (MAX_session_objects)
#define LOS_2D_ROWSIZE_PACKED (MAX_session_objects / 8)
#define LOS_2D_SIZE_PACKED (MAX_session_objects * LOS_2D_ROWSIZE_PACKED)
// Module to calculate which objects (voxel characters and other objects) can see which other objects. The information
// is used to generate events, which are given to the event manager, so it can decide what needs to hear about the change.
class _line_of_sight {
public:
// Definitions used by this class.
enum { ACTOR_EYE_HEIGHT = 160 };
enum { ACTOR_CROUCHED_HEIGHT = 61 };
enum ActorEyeMode { USE_OBJECT_VALUE = 0, FORCE_EYE_HEIGHT, FORCE_CROUCHED_HEIGHT };
// Default constructor and destructor.
inline _line_of_sight();
inline ~_line_of_sight();
// This attaches this object to its data files and sets other start-up values.
void Initialise();
// These add and remove objects from the service.
void Subscribe(uint32 nObserverID, uint32 nTargetID);
void UnSubscribe(uint32 nObserverID, uint32 nTargetID);
// These allow an object to be temporarily suspended in the LOS engine, but the objects they
// are registered to see are remembered for when the suspension is removed.
void Suspend(uint32 nObserverID);
inline void Unsuspend(uint32 nObserverID);
// These two turn off the whole of line-of-sight processing for everybody. All the registrations are kept
// though, so it can resume where it left off.
void SwitchOff();
void SwitchOn() { m_bSwitchedOn = TRUE8; }
// The engine actually calls this function instead of Cycle() directly, so that the amount of time
// spent in line-of-sight calculation can be controlled.
void DutyCycle();
// This function allows the how many subscribers to do per cycle
inline void SetDutyCycle(uint32 nSubsPerCycle) { m_nSubsPerCycle = nSubsPerCycle; }
// This allows the field-of-view to be changed from the default 180 for a mega character.
void SetFieldOfView(uint32 nID, uint32 nFieldOfView);
// This allows the range an object sees to be changed from the default.
void SetSightRange(uint32 nID, uint32 nRange);
// This allows the height range an object sees to be changed from the default.
void SetSightHeight(uint32 nID, uint32 nHeight);
// This sets whether or not an object can see in the dark.
inline void SetCanSeeInDarkFlag(uint32 nID, bool8 bState);
// This sets whether or not a mega is always seen regardless of shadows or not.
inline void SetNeverInShadowFlag(uint32 nID, bool8 bState);
// This turns off shadow-handling for the whole LOS engine.
inline void ShadowsOnOff(bool8 bState) { m_bHandleShadows = bState; }
// This checks line-of-sight between two objects, accounting for field-of-view if observer is an actor,
bool8 ObjectToObject(uint32 nObserverID, uint32 nTargetID, _barrier_ray_type eRayType, bool8 bCanSeeUs, ActorEyeMode eEyeMode, bool8 bOverrideHeightLimit = FALSE8);
// This checks line-of-sight using the current truth table values and so is fast.
inline bool8 LineOfSight(uint32 nObserverID, uint32 nTargetID);
// And these two return extra information after a call to ObjectToObject().
const px3DRealPoint GetLastImpactPoint() const { return (m_oImpactPoint); }
_barrier_logic_value GetLastImpactType() const { return (m_eImpactType); }
// This allows other classes to get at the barrier slices (the Remora needs this).
_linked_data_file *GetSlicesPointer() const { return (m_pyLOSData); }
bool8 FailingOnHeight() const { return (m_bFailingOnHeight); }
private:
_linked_data_file *m_pyLOSData; // Pointer to the loaded line-of-sight data file.
uint32 m_nSubsPerCycle; // How many subscribers to process per cycle.
uint32 m_nFirstSubscriber; // Number of first subscriber to process
uint32 m_nNumObjects; // Number of subscribers to this service.
uint32 m_nTotalCurrentSubscribers; // Total number of current subscribers to this service.
uint32 m_pnFieldOfView[LOS_1D_SIZE]; // Current field-of-view for each mega.
uint32 m_pnSeeingDistance[LOS_1D_SIZE]; // Maximum distance an object can see.
PXreal m_pfHeightOfView[LOS_1D_SIZE]; // Height restriction on view.
int32 m_pnSubscribeNum[LOS_1D_SIZE]; // How many times a game object has subscribed.
uint8 m_pnTable[LOS_2D_SIZE_PACKED]; // The truth-table of who can see who.
uint8 m_pnSubscribers[LOS_2D_SIZE_PACKED]; // Housekeeping table of current subscribers to the service.
bool8 m_pbSuspended[LOS_1D_SIZE]; // If true, the object has been temporarily suspended.
bool8 m_pbCanSeeInDark[LOS_1D_SIZE]; // Housekeeping table of objects that should ignore shadows.
bool8 m_pbIgnoreShadows[LOS_1D_SIZE]; // Table of megas that are always seen regardless of shadows.
px3DRealPoint m_oImpactPoint; // Holds impact point from last call to ObjectToObject().
_barrier_logic_value m_eImpactType; // Holds type of impact from last call to ObjectToObject().
bool8 m_bSwitchedOn; // Flag that allows LOS processing to be suspended.
bool8 m_bFailingOnHeight; // Debug flag that gets set when a barrier height check fails.
bool8 m_bHandleShadows; // Turns shadow handling on/off.
uint8 m_nPad;
// Here I block the use of the default '='.
_line_of_sight(const _line_of_sight &) {}
void operator=(const _line_of_sight &) {}
// Functions used internally by this class.
void WhatSeesWhat();
bool8 InFieldOfView(PXreal fLookingX, PXreal fLookingZ, PXfloat fLookingDirection, PXreal fObservedX, PXreal fObservedZ, uint32 nFieldOfView) const;
inline void SetPackedBit(uint8 *pnArray, uint32 i, uint32 j, bool8 bValue);
inline bool8 GetPackedBit(uint8 *pnArray, uint32 i, uint32 j) const;
};
extern _line_of_sight *g_oLineOfSight;
inline _line_of_sight::_line_of_sight() {
m_bSwitchedOn = TRUE8;
m_nSubsPerCycle = LOS_DEFAULT_SUBSCRIBERS_PER_CYCLE;
m_nFirstSubscriber = 0;
m_pyLOSData = NULL;
m_nNumObjects = 0;
m_nTotalCurrentSubscribers = 0;
m_bHandleShadows = TRUE8;
m_nPad = 0;
}
inline _line_of_sight::~_line_of_sight() {
m_pyLOSData = NULL;
m_nNumObjects = 0;
Zdebug("Destroyed line-of-sight object");
}
inline void _line_of_sight::Unsuspend(uint32 nObserverID) {
// Set the flag for the object.
m_pbSuspended[nObserverID] = FALSE8;
}
inline void _line_of_sight::SetCanSeeInDarkFlag(uint32 nID, bool8 bState) {
// Set the flag for the object.
m_pbCanSeeInDark[nID] = bState;
}
inline void _line_of_sight::SetNeverInShadowFlag(uint32 nID, bool8 bState) {
// Set the flag for the object.
m_pbIgnoreShadows[nID] = bState;
}
inline bool8 _line_of_sight::LineOfSight(uint32 nObserverID, uint32 nTargetID) { return (GetPackedBit(m_pnTable, nObserverID, nTargetID)); }
inline void _line_of_sight::SwitchOff() {
m_bSwitchedOn = FALSE8;
memset((unsigned char *)m_pnTable, 0, (size_t)(LOS_2D_SIZE_PACKED * sizeof(uint8)));
}
inline void _line_of_sight::SetPackedBit(uint8 *pnArray, uint32 i, uint32 j, bool8 bValue) {
uint32 nJIndex = j >> 3;
uint32 nJRemainder = (j & 0x00000007);
if (bValue)
pnArray[i * LOS_2D_ROWSIZE_PACKED + nJIndex] |= (uint8)(1 << nJRemainder);
else
pnArray[i * LOS_2D_ROWSIZE_PACKED + nJIndex] &= (uint8)(~(1 << nJRemainder));
}
inline bool8 _line_of_sight::GetPackedBit(uint8 *pnArray, uint32 i, uint32 j) const {
uint32 nJIndex = j >> 3;
uint32 nJRemainder = (j & 0x00000007);
if ((pnArray[i * LOS_2D_ROWSIZE_PACKED + nJIndex] & (uint8)(1 << nJRemainder)) != 0)
return (TRUE8);
else
return (FALSE8);
}
} // End of namespace ICB
#endif // #if !defined( LINEOFSIGHT_H_INCLUDED )