www.pudn.com > Sterren_ASe_Explorer.rar > ase_threatboard.cpp
/* Copyright (C) William van der Sterren, 2002. * All rights reserved worldwide. * * This software is provided "as is" without express or implied * warranties. You may freely copy and compile this source into * applications you distribute provided that the copyright text * below is included in the resulting source code, for example: * "Portions Copyright (C) William van der Sterren, 2002" */ /*************************************************************************** * * purpose: threat and line-of-fire lookup table and overlay */ #include "stdafx.h" #include "ase_threatboard.h" #include "ase_terrain.h" #include#include // though MSVC6 doesn't feature min/max there using namespace std; #include #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ASE_ThreatBoard::ASE_ThreatBoard(unsigned int aNumberOfRows, unsigned int aNumberOfColumns, const ASE_TerrainBoard* theTerrain ) : ASE_Board(aNumberOfRows, aNumberOfColumns, 16, m_ThreatBrushes), m_RiskModus(eAnyLineOfFire), m_kTerrainBoard(theTerrain), m_kThreatLineOfFire(1), m_kThreatPosition(15), m_RiskFactor(4), m_LineOfFireReach(32), m_ThreatMovement(0), m_ThreatBrushes(0), m_kBaseRisk(0.15f), m_kMaxLOFCount(6) { assert( m_kTerrainBoard ); assert( GetNumberOfColumns() == m_kTerrainBoard->GetNumberOfColumns() ); assert( GetNumberOfRows() == m_kTerrainBoard->GetNumberOfRows() ); FillOutPalette(); // prepare the line-of-fire template m_PieTemplate.PrepareScan(m_LineOfFireReach); } ASE_ThreatBoard::~ASE_ThreatBoard() { delete[] m_ThreatBrushes; RemoveAllThreats(); } void ASE_ThreatBoard::FillOutPalette() { assert( m_ThreatBrushes == 0 ); assert( GetNumberOfValues() > 2 ); /*! fill out brush table as follows: part1: line-of-fire 3 x GetNumberOfValues() clear terrain, all values terrain 1, all values terrain 2, all values */ unsigned int totalnumber; unsigned int steps; steps = GetNumberOfValues(); totalnumber = 3 * steps; m_ThreatBrushes = new COLORREF[totalnumber]; COLORREF startrgb; COLORREF endrgb; float step_r, step_g, step_b; float r, g, b; int idx; idx = 0; // fill out first steps entries startrgb = RGB(255,255,224); endrgb = RGB(255,255,128); step_r = static_cast (GetRValue(endrgb) - GetRValue(startrgb)); step_g = static_cast (GetGValue(endrgb) - GetGValue(startrgb)); step_b = static_cast (GetBValue(endrgb) - GetBValue(startrgb)); step_r /= (steps - 1); step_g /= (steps - 1); step_b /= (steps - 1); r = GetRValue(startrgb); g = GetGValue(startrgb); b = GetBValue(startrgb); unsigned int step; for ( step = 0; step < steps; ++step, ++idx ) { m_ThreatBrushes[idx] = RGB(r,g,b); r += step_r; g += step_g; b += step_b; r = min(255.0f, max(0.0f, r)); g = min(255.0f, max(0.0f, g)); b = min(255.0f, max(0.0f, b)); } // fill out second steps entries startrgb = RGB(192,192,168); endrgb = RGB(192,192, 96); step_r = static_cast (GetRValue(endrgb) - GetRValue(startrgb)); step_g = static_cast (GetGValue(endrgb) - GetGValue(startrgb)); step_b = static_cast (GetBValue(endrgb) - GetBValue(startrgb)); step_r /= (steps - 1); step_g /= (steps - 1); step_b /= (steps - 1); r = GetRValue(startrgb); g = GetGValue(startrgb); b = GetBValue(startrgb); for ( step = 0; step < steps; ++step, ++idx ) { m_ThreatBrushes[idx] = RGB(r,g,b); r += step_r; g += step_g; b += step_b; r = min(255.0f, max(0.0f, r)); g = min(255.0f, max(0.0f, g)); b = min(255.0f, max(0.0f, b)); } // fill out third steps entries startrgb = RGB(128,128,112); endrgb = RGB(128,128, 64); step_r = static_cast (GetRValue(endrgb) - GetRValue(startrgb)); step_g = static_cast (GetGValue(endrgb) - GetGValue(startrgb)); step_b = static_cast (GetBValue(endrgb) - GetBValue(startrgb)); step_r /= (steps - 1); step_g /= (steps - 1); step_b /= (steps - 1); r = GetRValue(startrgb); g = GetGValue(startrgb); b = GetBValue(startrgb); for ( step = 0; step < steps; ++step, ++idx ) { m_ThreatBrushes[idx] = RGB(r,g,b); r += step_r; g += step_g; b += step_b; r = min(255.0f, max(0.0f, r)); g = min(255.0f, max(0.0f, g)); b = min(255.0f, max(0.0f, b)); } } int ASE_ThreatBoard::GetMaxThreatReach() const { return static_cast (m_LineOfFireReach); } void ASE_ThreatBoard::MarkThreatPosition(int aRow, int aCol) { unsigned int idx; idx = GetIndexForRowColumn(aRow, aCol); // mark cell on the board if ( GetCellValue(idx) == m_kThreatPosition ) return; SetCellValue(idx, m_kThreatPosition); // create threat ASE_Threat* threat; threat = new ASE_Threat(idx); AddThreat(threat); ComputeLinesOfFire(); } void ASE_ThreatBoard::ClearThreatPosition(int aRow, int aCol) { unsigned int idx; idx = GetIndexForRowColumn(aRow, aCol); // mark cell on the board if ( GetCellValue(idx) == 0 ) return; // remove threat if ( GetCellValue(idx) == m_kThreatPosition ) RemoveThreat(idx); // mark cell on the board SetCellValue(idx, 0); ComputeLinesOfFire(); } bool ASE_ThreatBoard::IsThreatPosition(int aRow, int aCol) const { return ( m_kThreatPosition == GetCellValue(aRow, aCol) ); } bool ASE_ThreatBoard::IsThreatPosition(unsigned int anIndex) const { return ( m_kThreatPosition == GetCellValue(anIndex) ); } void ASE_ThreatBoard::SetRiskModus(RiskModus aRiskModus) { assert( ( aRiskModus == eAnyLineOfFire ) || ( aRiskModus == eMinLineOfFireDistance ) || ( aRiskModus == eMinLineOfFireCount ) || ( aRiskModus == eMinLineOfFireCountAndDistance ) ); m_RiskModus = aRiskModus; } ASE_ThreatBoard::RiskModus ASE_ThreatBoard::GetRiskModus() const { return m_RiskModus; } COLORREF ASE_ThreatBoard::GetCellColorForOffset(unsigned int anIndex, CellValue anOffset) const { assert( !IsEmptyCell(anIndex) ); CellValue value; value = GetCellValue(anIndex); unsigned int idx; idx = anOffset * GetNumberOfValues(); idx += static_cast (value); return m_ThreatBrushes[idx]; } COLORREF ASE_ThreatBoard::GetLineOfFireColor() const { return m_ThreatBrushes[GetNumberOfValues() * (m_kThreatLineOfFire - 1)]; } unsigned int ASE_ThreatBoard::GetAssumedThreatMovement() const { return m_ThreatMovement; } void ASE_ThreatBoard::SetAssumedThreatMovement(unsigned int theAssumedThreatMovement) { m_ThreatMovement = theAssumedThreatMovement; } void ASE_ThreatBoard::ClearLinesOfFire() { const unsigned int maxidx = GetNumberOfColumns() * GetNumberOfRows(); for ( unsigned int idx = 0; idx < maxidx; ++idx ) { if ( GetCellValue(idx) != m_kThreatPosition ) { SetCellValue(idx, 0); } } } void ASE_ThreatBoard::InitializeLinesOfFire() { // clear old threats RemoveAllThreats(); // locate new threats for ( unsigned int idx = 0; idx < (GetNumberOfRows() * GetNumberOfColumns()); ++idx ) { if ( GetCellValue(idx) == m_kThreatPosition ) { ASE_Threat* threat; threat = new ASE_Threat(idx); AddThreat(threat); } } ComputeLinesOfFire(); } void ASE_ThreatBoard::ComputeLinesOfFire() { ClearLinesOfFire(); for ( unsigned int r = 0; r < GetNumberOfRows(); ++r ) { for ( unsigned int c = 0; c < GetNumberOfColumns(); ++c ) { if ( GetCellValue(r, c) == m_kThreatPosition ) { ComputeLinesOfFireForMovingThreat(r, c, m_ThreatMovement); } } } // select appropriate cell value function typedef CellValue (ASE_ThreatBoard::*MemberFnDetermineRisk)(unsigned int anIndex) const; MemberFnDetermineRisk pMemberFunction; if ( m_RiskModus == eAnyLineOfFire ) pMemberFunction = DetermineLineOfFireRiskAnyLOF; else if ( m_RiskModus == eMinLineOfFireDistance ) pMemberFunction = DetermineLineOfFireRiskMinDistance; else if ( m_RiskModus == eMinLineOfFireCount ) pMemberFunction = DetermineLineOfFireRiskLOFCount; else if ( m_RiskModus == eMinLineOfFireCountAndDistance ) pMemberFunction = DetermineLineOfFireRiskDistanceLOFCount; else { assert( !"unsupported risk modus" ); } // now start marking lines of fire ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { const ASE_Threat::PositionsUnderFire* positionsunderfire; positionsunderfire = (*threat)->GetPositionsUnderFire(); assert( !positionsunderfire->empty() ); ASE_Threat::PositionsUnderFire::const_iterator position; for ( position = positionsunderfire->begin(); position != positionsunderfire->end(); ++position ) { if ( IsEmptyCell(*position) ) { // call member function via pointer to member function SetCellValue(*position, (this->*pMemberFunction)(*position)); } } } } void ASE_ThreatBoard::ComputeLinesOfFireForMovingThreat (unsigned int aRow, unsigned int aCol, unsigned int aMaximumMovement ) { CoordPairList theReachablePositions; theReachablePositions.reserve( (2 + aMaximumMovement) * (2 + aMaximumMovement) ); ConstructPossibleThreatPositions(aRow, aCol, aMaximumMovement, theReachablePositions); assert( !theReachablePositions.empty() ); CoordPairList theCoveredPositions; theCoveredPositions.reserve((m_LineOfFireReach+aMaximumMovement) * (m_LineOfFireReach+aMaximumMovement) * 4); CoordPairList::const_iterator position; for ( position = theReachablePositions.begin(); position != theReachablePositions.end(); ++position ) { ComputeLinesOfFireFromCell(position->x, position->y, theCoveredPositions); } assert( !theCoveredPositions.empty() ); // now set these locations as under fire by this threat ASE_Threat::PositionsUnderFire positionsUnderFire; positionsUnderFire.reserve(theCoveredPositions.size()); CoordPairList::const_iterator coveredposition; for ( coveredposition = theCoveredPositions.begin(); coveredposition != theCoveredPositions.end(); ++coveredposition ) { unsigned int idx; idx = GetIndexForRowColumn(coveredposition->x, coveredposition->y); positionsUnderFire.push_back(idx); } ASE_Threat* threat; unsigned int idx; idx = GetIndexForRowColumn(aRow, aCol); threat = GetThreatForLocation(idx); threat->SetPositionsUnderFire(positionsUnderFire); assert( threat->GetPositionsUnderFire()->size() == positionsUnderFire.size() ); } void ASE_ThreatBoard::ConstructPossibleThreatPositions(unsigned int aRow, unsigned int aCol, unsigned int aMaximumMovement, CoordPairList& thePositions ) const { //! recursive implementation if ( aMaximumMovement == 0 ) { ASE_CoordPair pair(aRow, aCol); thePositions.push_back(pair); } else { // move one step from aRow, aCol for ( int nx = -1; nx < 2; ++nx ) { for ( int ny = -1; ny < 2; ++ny ) { int r, c; r = nx + aRow; c = ny + aCol; if ( !m_kTerrainBoard->IsAccessibleBoardLocation(r, c) ) continue; ASE_CoordPair pair(r, c); if ( thePositions.end() != find(thePositions.begin(), thePositions.end(), pair) ) continue; // add pair and move from there as well ConstructPossibleThreatPositions(r, c, aMaximumMovement - 1, thePositions); } } } } void ASE_ThreatBoard::ComputeLinesOfFireFromCell (unsigned int aRow, unsigned int aCol, CoordPairList& theCoveredPositions /*! in+out */ ) const { // obtain rays from template // for each ray // - test until // - invalid location // - blocked assert( !m_kTerrainBoard->IsLocationImpassable(aRow, aCol) ); ASE_CoordPair here(aRow, aCol); theCoveredPositions.push_back(here); unsigned int nr_of_rays; nr_of_rays = m_PieTemplate.GetNumberOfRays(); CoordPairList raytrace; for ( unsigned int ray = 0; ray < nr_of_rays; ++ray ) { raytrace.clear(); m_PieTemplate.GetRayForLocation(ray, here, raytrace); CoordPairList::const_iterator pos; for ( pos = raytrace.begin(); pos != raytrace.end(); ++pos ) { if ( !IsValidBoardLocation(pos->x, pos->y) ) break; if ( m_kTerrainBoard->IsLocationImpassable(pos->x, pos->y) ) break; // location pos can be seen, so add it to the list of coveredpositions // unless it is already part of it if ( theCoveredPositions.end() == find(theCoveredPositions.begin(), theCoveredPositions.end(), *pos) ) { theCoveredPositions.push_back(*pos); } } } } ASE_Board::CellValue ASE_ThreatBoard::DetermineLineOfFireRiskAnyLOF(unsigned int anIndex) const { // set 1 if under fire from any threat, 0 otherwise CellValue result; result = 0; ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { if ( (*threat)->IsAbleToFireAtLocation(anIndex) ) { result = 1; break; } } return result; } ASE_Board::CellValue ASE_ThreatBoard::DetermineLineOfFireRiskMinDistance(unsigned int anIndex) const { // set minimum distance to threat as value unsigned int x; unsigned int y; GetRowAndColumnForIndex(anIndex, x, y); int minDistance; minDistance = ( m_LineOfFireReach + m_ThreatMovement ) * ( m_LineOfFireReach + m_ThreatMovement ); // iterate over all threats, and figure out which one is the closest able to see x(,y) ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { if ( (*threat)->IsAbleToFireAtLocation(anIndex) ) { unsigned int x2, y2; GetRowAndColumnForIndex((*threat)->GetLocation(), x2, y2); int dx, dy; dx = x - x2; dy = y - y2; int dist; dist = dx * dx + dy * dy; minDistance= min(dist, minDistance); } } float value; value = sqrt(static_cast (minDistance)); value /= m_LineOfFireReach; value = 1 - value; value *= (GetNumberOfValues() - 2); value += 1; CellValue result; result = static_cast (value); assert( result < (GetNumberOfValues() - 1) ); return result; } ASE_ThreatBoard::CellValue ASE_ThreatBoard::DetermineLineOfFireRiskLOFCount(unsigned int anIndex) const { // set lof count from threat as value unsigned int x; unsigned int y; GetRowAndColumnForIndex(anIndex, x, y); CellValue lofcount; lofcount = 0; // iterate over all threats, and count the number of threats able to see position ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { if ( (*threat)->IsAbleToFireAtLocation(anIndex) ) { lofcount++; } } lofcount = min(lofcount, m_kMaxLOFCount); float value; value = static_cast (lofcount); value /= m_kMaxLOFCount; value *= (GetNumberOfValues() - 2); CellValue result; result = static_cast (value); return result; } ASE_Board::CellValue ASE_ThreatBoard::DetermineLineOfFireRiskDistanceLOFCount(unsigned int anIndex) const { // set a weighted combination of threat count and distance to threat as value unsigned int x; unsigned int y; GetRowAndColumnForIndex(anIndex, x, y); const float kMaxDistance = 1.0f + static_cast ( m_LineOfFireReach + m_ThreatMovement ); const float kMaxValue = 4.0f /* threats */ * (kMaxDistance / 3.0f); float threatlofs; threatlofs = 0; // iterate over all threats, and figure out which one is the closest able to see x(,y) ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { if ( (*threat)->IsAbleToFireAtLocation(anIndex) ) { unsigned int x2, y2; GetRowAndColumnForIndex((*threat)->GetLocation(), x2, y2); int dx, dy; dx = x - x2; dy = y - y2; int dist; dist = dx * dx + dy * dy; float fdist; fdist = sqrt(static_cast (dist)); assert( kMaxDistance > fdist ); threatlofs += (kMaxDistance - fdist); } } float value; value = min(1.0f, threatlofs / kMaxValue ); value *= (GetNumberOfValues() - 3); value += 1; CellValue result; result = static_cast (value); assert( result < (GetNumberOfValues() - 1) ); return result; } float ASE_ThreatBoard::GetRiskValue(int aRow, int aCol) const { unsigned int idx; idx = GetIndexForRowColumn(aRow, aCol); return GetRiskValue(idx); } float ASE_ThreatBoard::GetRiskValue(unsigned int anIndex) const { if ( m_RiskModus == eAnyLineOfFire ) { return IsEmptyCell(anIndex) ? 0.0f : 1.0f; } else { CellValue value; value = GetCellValue(anIndex); if ( value == 0 ) return 0; float result; result = static_cast (value) / GetMaxRiskValue(); result *= (1.0f - m_kBaseRisk); result += m_kBaseRisk; return result; } } float ASE_ThreatBoard::GetMaxRiskValue() const { if ( m_RiskModus == eAnyLineOfFire ) return 1.0f; else { assert( ( m_RiskModus == eMinLineOfFireDistance ) || ( m_RiskModus == eMinLineOfFireCount ) || ( m_RiskModus == eMinLineOfFireCountAndDistance ) ); return static_cast (m_kThreatPosition); } } void ASE_ThreatBoard::GetThreatPositions(CoordPairList& thePositions /*! out */) const { ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { int idx; idx = (*threat)->GetLocation(); unsigned int row; unsigned int col; GetRowAndColumnForIndex(idx, row, col); thePositions.push_back(ASE_CoordPair(row, col)); } } const ASE_PieScan* ASE_ThreatBoard::GetLineOfSightTemplate() const { return &m_PieTemplate; } void ASE_ThreatBoard::AddThreat(ASE_Threat* theThreat) { unsigned int idx; idx = theThreat->GetLocation(); assert( !DoesSomeThreatOccupyLocation(idx) ); m_Threats.push_back(theThreat); } void ASE_ThreatBoard::RemoveThreat(unsigned int aLocation) { assert( DoesSomeThreatOccupyLocation(aLocation) ); ThreatList::iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { if ( aLocation == (*threat)->GetLocation() ) { delete (*threat); m_Threats.erase(threat); return; } } assert( !"failed to find threat for aLocation" ); } bool ASE_ThreatBoard::DoesSomeThreatOccupyLocation(unsigned int aLocation) const { ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { if ( aLocation == (*threat)->GetLocation() ) return true; } return false; } ASE_Threat* ASE_ThreatBoard::GetThreatForLocation(unsigned int aLocation) const { assert( DoesSomeThreatOccupyLocation(aLocation) ); ThreatList::const_iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { if ( aLocation == (*threat)->GetLocation() ) return (*threat); } return 0; } void ASE_ThreatBoard::RemoveAllThreats() { ThreatList::iterator threat; for ( threat = m_Threats.begin(); threat != m_Threats.end(); ++threat ) { delete (*threat); } m_Threats.clear(); }