mirror of
https://github.com/electronicarts/CnC_Generals_Zero_Hour.git
synced 2025-12-16 23:51:41 -05:00
Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
848
Generals/Code/GameEngine/Source/Common/Audio/AudioEventRTS.cpp
Normal file
848
Generals/Code/GameEngine/Source/Common/Audio/AudioEventRTS.cpp
Normal file
@@ -0,0 +1,848 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: AudioEventRTS.cpp
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* EA Pacific */
|
||||
/* Confidential Information */
|
||||
/* Copyright (C) 2001 - All Rights Reserved */
|
||||
/* DO NOT DISTRIBUTE */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Project: RTS3 */
|
||||
/* File name: AudioEventRTS.cpp */
|
||||
/* Created: John K. McDonald, Jr., 3/21/2002 */
|
||||
/* Desc: AudioEventRTS constructors and assignment operator, etc. */
|
||||
/* Revision History: */
|
||||
/* 3/21/2002 : Initial creation */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/AudioEventRTS.h"
|
||||
|
||||
#include "Common/AudioEventInfo.h"
|
||||
#include "Common/AudioRandomValue.h"
|
||||
#include "Common/AudioSettings.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "Common/GameSounds.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Registry.h"
|
||||
|
||||
#include "GameLogic/GameLogic.h" // For getObjectByID
|
||||
#include "GameLogic/LogicRandomValue.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
#include "GameClient/Drawable.h" // For getPosition
|
||||
#include "GameClient/GameClient.h" // For getDrawableByID
|
||||
|
||||
#ifdef _INTERNAL
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS()
|
||||
: m_eventName(AsciiString::TheEmptyString),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
m_positionOfAudio.zero();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
m_positionOfAudio.zero();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, ObjectID ownerID )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_objectID(ownerID),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
|
||||
if( m_objectID )
|
||||
{
|
||||
m_ownerType = OT_Object;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_objectID = INVALID_ID;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, DrawableID drawableID )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_drawableID(drawableID),
|
||||
m_ownerType(OT_INVALID),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
|
||||
if( m_drawableID )
|
||||
{
|
||||
m_ownerType = OT_Drawable;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_drawableID = INVALID_DRAWABLE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AsciiString& eventName, const Coord3D *positionOfAudio )
|
||||
: m_eventName(eventName),
|
||||
m_priority(AP_NORMAL),
|
||||
m_volume(-1.0),
|
||||
m_timeOfDay(TIME_OF_DAY_AFTERNOON),
|
||||
m_ownerType(OT_Positional),
|
||||
m_shouldFade(false),
|
||||
m_isLogicalAudio(false),
|
||||
m_filenameToLoad(AsciiString::TheEmptyString),
|
||||
m_eventInfo(NULL),
|
||||
m_playingHandle(0),
|
||||
m_killThisHandle(0),
|
||||
m_pitchShift(1.0),
|
||||
m_volumeShift(0.0),
|
||||
m_loopCount(1),
|
||||
m_playingAudioIndex(-1),
|
||||
m_allCount(0),
|
||||
m_playerIndex(-1),
|
||||
m_delay(0.0f),
|
||||
m_uninterruptable(FALSE)
|
||||
{
|
||||
m_positionOfAudio.set( positionOfAudio );
|
||||
m_attackName.clear();
|
||||
m_decayName.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::AudioEventRTS( const AudioEventRTS& right )
|
||||
{
|
||||
m_filenameToLoad = right.m_filenameToLoad;
|
||||
m_eventInfo = right.m_eventInfo;
|
||||
m_playingHandle = right.m_playingHandle;
|
||||
m_killThisHandle = right.m_killThisHandle;
|
||||
m_eventName = right.m_eventName;
|
||||
m_priority = right.m_priority;
|
||||
m_volume = right.m_volume;
|
||||
m_timeOfDay = right.m_timeOfDay;
|
||||
m_ownerType = right.m_ownerType;
|
||||
m_shouldFade = right.m_shouldFade;
|
||||
m_isLogicalAudio = right.m_isLogicalAudio;
|
||||
m_pitchShift = right.m_pitchShift;
|
||||
m_volumeShift = right.m_volumeShift;
|
||||
m_loopCount = right.m_loopCount;
|
||||
m_playingAudioIndex = right.m_playingAudioIndex;
|
||||
m_allCount = right.m_allCount;
|
||||
m_playerIndex = right.m_playerIndex;
|
||||
m_delay = right.m_delay;
|
||||
m_attackName = right.m_attackName;
|
||||
m_decayName = right.m_decayName;
|
||||
m_portionToPlayNext = right.m_portionToPlayNext;
|
||||
m_uninterruptable = right.m_uninterruptable;
|
||||
|
||||
if( m_ownerType == OT_Positional || m_ownerType == OT_Dead )
|
||||
{
|
||||
m_positionOfAudio.set( &right.m_positionOfAudio );
|
||||
}
|
||||
else if( m_ownerType == OT_Drawable )
|
||||
{
|
||||
m_drawableID = right.m_drawableID;
|
||||
}
|
||||
else if( m_ownerType == OT_Object )
|
||||
{
|
||||
m_objectID = right.m_objectID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS& AudioEventRTS::operator=( const AudioEventRTS& right )
|
||||
{
|
||||
m_filenameToLoad = right.m_filenameToLoad;
|
||||
m_eventInfo = right.m_eventInfo;
|
||||
m_playingHandle = right.m_playingHandle;
|
||||
m_killThisHandle = right.m_killThisHandle;
|
||||
m_eventName = right.m_eventName;
|
||||
m_priority = right.m_priority;
|
||||
m_volume = right.m_volume;
|
||||
m_timeOfDay = right.m_timeOfDay;
|
||||
m_ownerType = right.m_ownerType;
|
||||
m_shouldFade = right.m_shouldFade;
|
||||
m_isLogicalAudio = right.m_isLogicalAudio;
|
||||
m_pitchShift = right.m_pitchShift;
|
||||
m_volumeShift = right.m_volumeShift;
|
||||
m_loopCount = right.m_loopCount;
|
||||
m_playingAudioIndex = right.m_playingAudioIndex;
|
||||
m_allCount = right.m_allCount;
|
||||
m_playerIndex = right.m_playerIndex;
|
||||
m_delay = right.m_delay;
|
||||
m_attackName = right.m_attackName;
|
||||
m_decayName = right.m_decayName;
|
||||
m_portionToPlayNext = right.m_portionToPlayNext;
|
||||
m_uninterruptable = right.m_uninterruptable;
|
||||
|
||||
if( m_ownerType == OT_Positional || m_ownerType == OT_Dead )
|
||||
{
|
||||
m_positionOfAudio.set( &right.m_positionOfAudio );
|
||||
}
|
||||
else if( m_ownerType == OT_Drawable )
|
||||
{
|
||||
m_drawableID = right.m_drawableID;
|
||||
}
|
||||
else if( m_ownerType == OT_Object )
|
||||
{
|
||||
m_objectID = right.m_objectID;
|
||||
}
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioEventRTS::~AudioEventRTS()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setEventName( AsciiString name )
|
||||
{
|
||||
if ((name != m_eventName) && m_eventInfo != NULL) {
|
||||
// Clear out the audio event info, cause its not valid for the new event.
|
||||
m_eventInfo = NULL;
|
||||
}
|
||||
|
||||
m_eventName = name;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::generateFilename( void )
|
||||
{
|
||||
// A Logic Random Value is used because we may ask "How long will it take to play this sound?"
|
||||
// In that case, we need the same answer across all pcs.
|
||||
|
||||
if (!m_eventInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_filenameToLoad = generateFilenamePrefix(m_eventInfo->m_soundType, false);
|
||||
|
||||
Int which = 0;
|
||||
|
||||
if (m_eventInfo->m_soundType == AT_Music || m_eventInfo->m_soundType == AT_Streaming) {
|
||||
m_filenameToLoad.concat(m_eventInfo->m_filename);
|
||||
adjustForLocalization(m_filenameToLoad);
|
||||
return;
|
||||
} else {
|
||||
if (m_eventInfo->m_sounds.size() == 0) {
|
||||
m_filenameToLoad = AsciiString::TheEmptyString;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (BitTest(m_eventInfo->m_control, AC_RANDOM))
|
||||
{
|
||||
if (m_isLogicalAudio)
|
||||
{
|
||||
which = GameLogicRandomValue(0, m_eventInfo->m_sounds.size() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
which = GameAudioRandomValue(0, m_eventInfo->m_sounds.size() - 1);
|
||||
}
|
||||
|
||||
if (which == m_playingAudioIndex && m_eventInfo->m_sounds.size() > 2)
|
||||
which = ( which + 1 ) % m_eventInfo->m_sounds.size();
|
||||
|
||||
m_playingAudioIndex = which;//caching random choice to compare next call
|
||||
|
||||
}
|
||||
else
|
||||
which = (++m_playingAudioIndex) % m_eventInfo->m_sounds.size();
|
||||
|
||||
|
||||
}
|
||||
|
||||
m_filenameToLoad.concat(m_eventInfo->m_sounds[which]);
|
||||
m_filenameToLoad.concat(generateFilenameExtension(m_eventInfo->m_soundType));
|
||||
adjustForLocalization(m_filenameToLoad);
|
||||
|
||||
// Note: Also generate Delay when generating a filename, cause
|
||||
// we want delay to apply between every loop of a sound.
|
||||
m_delay = GameAudioRandomValueReal(m_eventInfo->m_delayMin, m_eventInfo->m_delayMax);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::getFilename( void )
|
||||
{
|
||||
return m_filenameToLoad;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::generatePlayInfo( void )
|
||||
{
|
||||
m_pitchShift = GameAudioRandomValueReal(m_eventInfo->m_pitchShiftMin, m_eventInfo->m_pitchShiftMax);
|
||||
m_volumeShift = GameAudioRandomValueReal(1.0f + m_eventInfo->m_volumeShift, 1.0f); // volume shifts are between 0 and 1
|
||||
m_loopCount = m_eventInfo->m_loopCount;
|
||||
|
||||
m_portionToPlayNext = PP_Attack;
|
||||
Int attackSize = m_eventInfo->m_attackSounds.size();
|
||||
if (attackSize > 0) {
|
||||
m_attackName = generateFilenamePrefix(m_eventInfo->m_soundType, false);
|
||||
// needs to be logic because it needs to be the same on all systems.
|
||||
Int attackToPlay;
|
||||
if (m_isLogicalAudio) {
|
||||
attackToPlay = GameLogicRandomValue(0, attackSize - 1);
|
||||
} else {
|
||||
attackToPlay = GameAudioRandomValue(0, attackSize - 1);
|
||||
}
|
||||
|
||||
m_attackName.concat(m_eventInfo->m_attackSounds[attackToPlay]);
|
||||
m_attackName.concat(generateFilenameExtension(m_eventInfo->m_soundType));
|
||||
adjustForLocalization(m_attackName);
|
||||
} else {
|
||||
m_portionToPlayNext = PP_Sound;
|
||||
}
|
||||
|
||||
Int decaySize = m_eventInfo->m_decaySounds.size();
|
||||
if (decaySize > 0) {
|
||||
m_decayName = generateFilenamePrefix(m_eventInfo->m_soundType, false);
|
||||
// needs to be logic because it needs to be the same on all systems.
|
||||
Int decayToPlay;
|
||||
if (m_isLogicalAudio) {
|
||||
decayToPlay = GameLogicRandomValue(0, decaySize - 1);
|
||||
} else {
|
||||
decayToPlay = GameAudioRandomValue(0, decaySize - 1);
|
||||
}
|
||||
|
||||
m_decayName.concat(m_eventInfo->m_decaySounds[decayToPlay]);
|
||||
m_decayName.concat(generateFilenameExtension(m_eventInfo->m_soundType));
|
||||
adjustForLocalization(m_decayName);
|
||||
}
|
||||
|
||||
m_isLogicalAudio = FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getPitchShift( void ) const
|
||||
{
|
||||
return m_pitchShift;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getVolumeShift( void ) const
|
||||
{
|
||||
return m_volumeShift;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::getAttackFilename( void ) const
|
||||
{
|
||||
return m_attackName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::getDecayFilename( void ) const
|
||||
{
|
||||
return m_decayName;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getDelay( void ) const
|
||||
{
|
||||
return m_delay;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::decrementDelay( Real timeToDecrement )
|
||||
{
|
||||
m_delay -= timeToDecrement;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PortionToPlay AudioEventRTS::getNextPlayPortion( void ) const
|
||||
{
|
||||
return m_portionToPlayNext;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::advanceNextPlayPortion( void )
|
||||
{
|
||||
switch (m_portionToPlayNext)
|
||||
{
|
||||
case PP_Attack:
|
||||
m_portionToPlayNext = PP_Sound;
|
||||
break;
|
||||
case PP_Sound:
|
||||
if (m_eventInfo && BitTest(m_eventInfo->m_control, AC_ALL))
|
||||
{
|
||||
if (m_allCount == m_eventInfo->m_sounds.size()) {
|
||||
m_portionToPlayNext = PP_Decay;
|
||||
}
|
||||
|
||||
// Advance the all count so that we move to the next sound.
|
||||
++m_allCount;
|
||||
}
|
||||
if (!m_decayName.isEmpty()) {
|
||||
m_portionToPlayNext = PP_Decay;
|
||||
} else {
|
||||
m_portionToPlayNext = PP_Done;
|
||||
}
|
||||
break;
|
||||
case PP_Decay:
|
||||
m_portionToPlayNext = PP_Done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setNextPlayPortion( PortionToPlay ptp )
|
||||
{
|
||||
m_portionToPlayNext = ptp;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::decreaseLoopCount( void )
|
||||
{
|
||||
if (m_loopCount == 1) {
|
||||
m_loopCount = -1;
|
||||
} else if (m_loopCount > 1) {
|
||||
--m_loopCount;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::hasMoreLoops( void ) const
|
||||
{
|
||||
return (m_loopCount >= 0);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setAudioEventInfo( const AudioEventInfo *eventInfo ) const
|
||||
{
|
||||
m_eventInfo = eventInfo;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const AudioEventInfo *AudioEventRTS::getAudioEventInfo( void ) const
|
||||
{
|
||||
if (m_eventInfo) {
|
||||
if (m_eventInfo->m_audioName == m_eventName) {
|
||||
return m_eventInfo;
|
||||
} else {
|
||||
m_eventInfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return m_eventInfo;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setPlayingHandle( AudioHandle handle )
|
||||
{
|
||||
m_playingHandle = handle;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioHandle AudioEventRTS::getPlayingHandle( void )
|
||||
{
|
||||
return m_playingHandle;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setPosition( const Coord3D *pos )
|
||||
{
|
||||
if (!pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(m_ownerType == OT_Positional || m_ownerType == OT_INVALID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_positionOfAudio = *pos;
|
||||
m_ownerType = OT_Positional;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const Coord3D* AudioEventRTS::getPosition( void )
|
||||
{
|
||||
if( m_ownerType != OT_INVALID )
|
||||
{
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setObjectID( ObjectID objID )
|
||||
{
|
||||
if (!(m_ownerType == OT_Object || m_ownerType == OT_INVALID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_objectID = objID;
|
||||
m_ownerType = OT_Object;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ObjectID AudioEventRTS::getObjectID( void )
|
||||
{
|
||||
if (m_ownerType == OT_Object) {
|
||||
return m_objectID;
|
||||
}
|
||||
|
||||
return INVALID_ID;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setDrawableID( DrawableID drawID )
|
||||
{
|
||||
if (!(m_ownerType == OT_Drawable || m_ownerType == OT_INVALID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_drawableID = drawID;
|
||||
m_ownerType = OT_Drawable;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DrawableID AudioEventRTS::getDrawableID( void )
|
||||
{
|
||||
if (m_ownerType == OT_Drawable) {
|
||||
return m_drawableID;
|
||||
}
|
||||
|
||||
return INVALID_DRAWABLE_ID;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setTimeOfDay( TimeOfDay tod )
|
||||
{
|
||||
m_timeOfDay = tod;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
TimeOfDay AudioEventRTS::getTimeOfDay( void ) const
|
||||
{
|
||||
return m_timeOfDay;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setHandleToKill( AudioHandle handleToKill )
|
||||
{
|
||||
m_killThisHandle = handleToKill;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioHandle AudioEventRTS::getHandleToKill( void ) const
|
||||
{
|
||||
return m_killThisHandle;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setShouldFade( Bool shouldFade )
|
||||
{
|
||||
m_shouldFade = shouldFade;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::getShouldFade( void ) const
|
||||
{
|
||||
return m_shouldFade;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setIsLogicalAudio( Bool isLogicalAudio )
|
||||
{
|
||||
m_isLogicalAudio = isLogicalAudio;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::getIsLogicalAudio( void ) const
|
||||
{
|
||||
return m_isLogicalAudio;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::isPositionalAudio( void ) const
|
||||
{
|
||||
if( m_eventInfo )
|
||||
{
|
||||
if( !BitTest( m_eventInfo->m_type, ST_WORLD ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if( m_ownerType != OT_INVALID )
|
||||
{
|
||||
if( m_drawableID != INVALID_DRAWABLE_ID || m_objectID != INVALID_ID || m_ownerType == OT_Positional )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool AudioEventRTS::isCurrentlyPlaying( void ) const
|
||||
{
|
||||
return TheAudio->isCurrentlyPlaying(m_playingHandle);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AudioPriority AudioEventRTS::getAudioPriority( void ) const
|
||||
{
|
||||
return m_priority;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setAudioPriority( AudioPriority newPriority )
|
||||
{
|
||||
m_priority = newPriority;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real AudioEventRTS::getVolume( void ) const
|
||||
{
|
||||
if (m_volume == -1.0f) {
|
||||
if (m_eventInfo) {
|
||||
return m_eventInfo->m_volume;
|
||||
}
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setVolume( Real vol )
|
||||
{
|
||||
m_volume = vol;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const Coord3D *AudioEventRTS::getCurrentPosition( void )
|
||||
{
|
||||
if (m_ownerType == OT_Positional)
|
||||
{
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
else if (m_ownerType == OT_Object)
|
||||
{
|
||||
Object *obj = TheGameLogic->findObjectByID(m_objectID);
|
||||
if (obj)
|
||||
{
|
||||
m_positionOfAudio.set( obj->getPosition() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ownerType = OT_Dead;
|
||||
}
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
else if (m_ownerType == OT_Drawable)
|
||||
{
|
||||
Drawable *draw = TheGameClient->findDrawableByID(m_drawableID);
|
||||
if( draw )
|
||||
{
|
||||
m_positionOfAudio.set( draw->getPosition() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ownerType = OT_Dead;
|
||||
}
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
else if( m_ownerType == OT_Dead )
|
||||
{
|
||||
return &m_positionOfAudio;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::generateFilenamePrefix( AudioType audioTypeToPlay, Bool localized )
|
||||
{
|
||||
AsciiString retStr;
|
||||
retStr = TheAudio->getAudioSettings()->m_audioRoot;
|
||||
retStr.concat("\\");
|
||||
if (audioTypeToPlay == AT_Music) {
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_musicFolder);
|
||||
} else if (audioTypeToPlay == AT_Streaming) {
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_streamingFolder);
|
||||
} else {
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_soundsFolder);
|
||||
}
|
||||
retStr.concat("\\");
|
||||
|
||||
if (localized) {
|
||||
retStr.concat(GetRegistryLanguage());
|
||||
retStr.concat("\\");
|
||||
}
|
||||
|
||||
return retStr;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString AudioEventRTS::generateFilenameExtension( AudioType audioTypeToPlay )
|
||||
{
|
||||
AsciiString retStr = AsciiString::TheEmptyString;
|
||||
if (audioTypeToPlay != AT_Music) {
|
||||
retStr = ".";
|
||||
retStr.concat(TheAudio->getAudioSettings()->m_soundsExtension);
|
||||
}
|
||||
|
||||
return retStr;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::adjustForLocalization(AsciiString &strToAdjust)
|
||||
{
|
||||
if (TheFileSystem->doesFileExist(strToAdjust.str()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const char *str = strToAdjust.reverseFind('\\');
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsciiString filename = str;
|
||||
|
||||
strToAdjust = generateFilenamePrefix(m_eventInfo->m_soundType, TRUE);
|
||||
strToAdjust.concat(filename);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int AudioEventRTS::getPlayerIndex( void ) const
|
||||
{
|
||||
if (m_ownerType == OT_Object) {
|
||||
Object *obj = TheGameLogic->findObjectByID(m_objectID);
|
||||
if (obj) {
|
||||
return obj->getControllingPlayer()->getPlayerIndex();
|
||||
}
|
||||
} else if (m_ownerType == OT_Drawable) {
|
||||
Drawable *draw = TheGameClient->findDrawableByID(m_drawableID);
|
||||
if (draw) {
|
||||
Object *obj = draw->getObject();
|
||||
if (obj) {
|
||||
return obj->getControllingPlayer()->getPlayerIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_playerIndex;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void AudioEventRTS::setPlayerIndex( Int playerNdx )
|
||||
{
|
||||
m_playerIndex = playerNdx;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/AudioRequest.h"
|
||||
|
||||
|
||||
AudioRequest::~AudioRequest()
|
||||
{
|
||||
|
||||
}
|
||||
1118
Generals/Code/GameEngine/Source/Common/Audio/GameAudio.cpp
Normal file
1118
Generals/Code/GameEngine/Source/Common/Audio/GameAudio.cpp
Normal file
File diff suppressed because it is too large
Load Diff
125
Generals/Code/GameEngine/Source/Common/Audio/GameMusic.cpp
Normal file
125
Generals/Code/GameEngine/Source/Common/Audio/GameMusic.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: GameMusic.cpp
|
||||
//
|
||||
// Created: 5/01/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameMusic.h"
|
||||
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/AudioRequest.h"
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/INI.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
#define MUSIC_PATH "Data\\Audio\\Tracks" // directory path to the music files
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** The INI data fields for music tracks */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const FieldParse MusicTrack::m_musicTrackFieldParseTable[] =
|
||||
{
|
||||
|
||||
{ "Filename", INI::parseAsciiString, NULL, offsetof( MusicTrack, filename ) },
|
||||
{ "Volume", INI::parsePercentToReal, NULL, offsetof( MusicTrack, volume ) },
|
||||
{ "Ambient", INI::parseBool, NULL, offsetof( MusicTrack, ambient ) },
|
||||
{ NULL, NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MusicManager::MusicManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MusicManager::~MusicManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::playTrack( AudioEventRTS *eventToUse )
|
||||
{
|
||||
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( true );
|
||||
audioRequest->m_pendingEvent = eventToUse;
|
||||
audioRequest->m_request = AR_Play;
|
||||
TheAudio->appendAudioRequest( audioRequest );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::stopTrack( AudioHandle eventToRemove )
|
||||
{
|
||||
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( false );
|
||||
audioRequest->m_handleToInteractOn = eventToRemove;
|
||||
audioRequest->m_request = AR_Stop;
|
||||
TheAudio->appendAudioRequest( audioRequest );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::addAudioEvent( AudioEventRTS *eventToAdd )
|
||||
{
|
||||
playTrack( eventToAdd );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void MusicManager::removeAudioEvent( AudioHandle eventToRemove )
|
||||
{
|
||||
stopTrack( eventToRemove );
|
||||
}
|
||||
|
||||
333
Generals/Code/GameEngine/Source/Common/Audio/GameSounds.cpp
Normal file
333
Generals/Code/GameEngine/Source/Common/Audio/GameSounds.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: GameSounds.cpp
|
||||
//
|
||||
// Created: 5/02/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Lib/Basetype.h"
|
||||
#include "Common/GameSounds.h"
|
||||
|
||||
#include "Common/AudioEventInfo.h"
|
||||
#include "Common/AudioEventRTS.h"
|
||||
#include "Common/AudioRequest.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _INTERNAL
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SoundManager::SoundManager()
|
||||
{
|
||||
// nada to do
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SoundManager::~SoundManager()
|
||||
{
|
||||
// nada to do
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::init( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::postProcessLoad()
|
||||
{
|
||||
// The AudioManager should actually be live now, so go ahead and get the info we need from it
|
||||
// here
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::update( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::reset( void )
|
||||
{
|
||||
m_numPlaying2DSamples = 0;
|
||||
m_numPlaying3DSamples = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::loseFocus( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::regainFocus( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::setListenerPosition( const Coord3D *position )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::setViewRadius( Real viewRadius )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::setCameraAudibleDistance( Real audibleDistance )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real SoundManager::getCameraAudibleDistance( void )
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::addAudioEvent(AudioEventRTS *eventToAdd)
|
||||
{
|
||||
if (m_num2DSamples == 0 && m_num3DSamples == 0) {
|
||||
m_num2DSamples = TheAudio->getNum2DSamples();
|
||||
m_num3DSamples = TheAudio->getNum3DSamples();
|
||||
}
|
||||
|
||||
if (canPlayNow(eventToAdd)) {
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG((" - appended to request list with handle '%d'.\n", (UnsignedInt) eventToAdd->getPlayingHandle()));
|
||||
#endif
|
||||
AudioRequest *audioRequest = TheAudio->allocateAudioRequest( true );
|
||||
audioRequest->m_pendingEvent = eventToAdd;
|
||||
audioRequest->m_request = AR_Play;
|
||||
TheAudio->appendAudioRequest(audioRequest);
|
||||
} else {
|
||||
TheAudio->releaseAudioEventRTS(eventToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf2DSampleStart( void )
|
||||
{
|
||||
++m_numPlaying2DSamples;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf3DSampleStart( void )
|
||||
{
|
||||
++m_numPlaying3DSamples;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf2DSampleCompletion( void )
|
||||
{
|
||||
if (m_numPlaying2DSamples > 0) {
|
||||
--m_numPlaying2DSamples;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SoundManager::notifyOf3DSampleCompletion( void )
|
||||
{
|
||||
if (m_numPlaying3DSamples > 0) {
|
||||
--m_numPlaying3DSamples;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int SoundManager::getAvailableSamples( void )
|
||||
{
|
||||
return (m_num2DSamples - m_numPlaying2DSamples);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int SoundManager::getAvailable3DSamples( void )
|
||||
{
|
||||
return (m_num3DSamples - m_numPlaying3DSamples);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString SoundManager::getFilenameForPlayFromAudioEvent( const AudioEventRTS *eventToGetFrom )
|
||||
{
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SoundManager::canPlayNow( AudioEventRTS *event )
|
||||
{
|
||||
Bool retVal = false;
|
||||
// 1) Are we muted because we're beyond our maximum distance?
|
||||
// 2) Are we shrouded and this is a shroud sound?
|
||||
// 3) Are we violating our voice count or are we playing above the limit? (If so, stop now)
|
||||
// 4) is there an avaiable channel open?
|
||||
// 5) if not, then determine if there is anything of lower priority that we can kill
|
||||
// 6) if not, are we an interrupt-sound type?
|
||||
// if so, are there any sounds of our type playing right now that we can interrupt?
|
||||
// potentially here: Are there any sounds that are playing that are now beyond their distance?
|
||||
// if so, kill them and start our sound
|
||||
// if not, we're done. Can't play dude.
|
||||
|
||||
if( event->isPositionalAudio() && !BitTest( event->getAudioEventInfo()->m_type, ST_GLOBAL) && event->getAudioEventInfo()->m_priority != AP_CRITICAL )
|
||||
{
|
||||
Coord3D distance = *TheAudio->getListenerPosition();
|
||||
const Coord3D *pos = event->getCurrentPosition();
|
||||
if (pos)
|
||||
{
|
||||
distance.sub(pos);
|
||||
if (distance.length() >= event->getAudioEventInfo()->m_maxDistance)
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to distance (%.2f).\n", distance.length()));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
Int localPlayerNdx = ThePlayerList->getLocalPlayer()->getPlayerIndex();
|
||||
if( (event->getAudioEventInfo()->m_type & ST_SHROUDED) &&
|
||||
ThePartitionManager->getShroudStatusForPlayer(localPlayerNdx, pos) != CELLSHROUD_CLEAR )
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to shroud.\n"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (violatesVoice(event))
|
||||
{
|
||||
retVal = isInterrupting(event);
|
||||
if (retVal)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to voice.\n"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( TheAudio->doesViolateLimit( event ) )
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to limit.\n" ));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
else if( isInterrupting( event ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event->isPositionalAudio())
|
||||
{
|
||||
if (m_numPlaying3DSamples < m_num3DSamples)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- %d samples playing, %d samples available", m_numPlaying3DSamples, m_num3DSamples));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// its a UI sound (and thus, 2-D)
|
||||
if (m_numPlaying2DSamples < m_num2DSamples)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (TheAudio->isPlayingLowerPriority(event))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isInterrupting(event))
|
||||
{
|
||||
retVal = TheAudio->isPlayingAlready(event);
|
||||
if (retVal)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("- culled due to no channels available and non-interrupting.\n" ));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef INTENSIVE_AUDIO_DEBUG
|
||||
DEBUG_LOG(("culled due to unavailable channels"));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SoundManager::violatesVoice( AudioEventRTS *event )
|
||||
{
|
||||
if (event->getAudioEventInfo()->m_type & ST_VOICE) {
|
||||
return (event->getObjectID() && TheAudio->isObjectPlayingVoice(event->getObjectID()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SoundManager::isInterrupting( AudioEventRTS *event )
|
||||
{
|
||||
return event->getAudioEventInfo()->m_control & AC_INTERRUPT;
|
||||
}
|
||||
1683
Generals/Code/GameEngine/Source/Common/Audio/GameSpeech.cpp
Normal file
1683
Generals/Code/GameEngine/Source/Common/Audio/GameSpeech.cpp
Normal file
File diff suppressed because it is too large
Load Diff
708
Generals/Code/GameEngine/Source/Common/Audio/simpleplayer.cpp
Normal file
708
Generals/Code/GameEngine/Source/Common/Audio/simpleplayer.cpp
Normal file
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "Common/SimplePlayer.h"
|
||||
#include "Common/URLLaunch.h"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
CSimplePlayer::CSimplePlayer( HRESULT* phr )
|
||||
{
|
||||
m_cRef = 1;
|
||||
m_cBuffersOutstanding = 0;
|
||||
|
||||
m_pReader = NULL;
|
||||
|
||||
m_pHeader = NULL;
|
||||
m_hwo = NULL;
|
||||
|
||||
m_fEof = FALSE;
|
||||
m_pszUrl = NULL;
|
||||
|
||||
*phr = S_OK;
|
||||
|
||||
m_hOpenEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_OPEN_EVENT );
|
||||
if ( NULL == m_hOpenEvent )
|
||||
{
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
m_hCloseEvent = CreateEvent( NULL, FALSE, FALSE, SIMPLE_PLAYER_CLOSE_EVENT );
|
||||
if ( NULL == m_hCloseEvent )
|
||||
{
|
||||
*phr = E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
m_hrOpen = S_OK;
|
||||
|
||||
m_hCompletionEvent = NULL;
|
||||
|
||||
InitializeCriticalSection( &m_CriSec );
|
||||
m_whdrHead = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
CSimplePlayer::~CSimplePlayer()
|
||||
{
|
||||
DEBUG_ASSERTCRASH( 0 == m_cBuffersOutstanding,("CSimplePlayer destructor m_cBuffersOutstanding != 0") );
|
||||
|
||||
Close();
|
||||
|
||||
//
|
||||
// final remove of everything in the wave header list
|
||||
//
|
||||
RemoveWaveHeaders();
|
||||
DeleteCriticalSection( &m_CriSec );
|
||||
|
||||
if( m_pHeader != NULL )
|
||||
{
|
||||
m_pHeader->Release();
|
||||
m_pHeader = NULL;
|
||||
}
|
||||
|
||||
if( m_pReader != NULL )
|
||||
{
|
||||
m_pReader->Release();
|
||||
m_pReader = NULL;
|
||||
}
|
||||
|
||||
if( m_hwo != NULL )
|
||||
{
|
||||
waveOutClose( m_hwo );
|
||||
}
|
||||
|
||||
delete [] m_pszUrl;
|
||||
|
||||
if ( m_hOpenEvent )
|
||||
{
|
||||
CloseHandle( m_hOpenEvent );
|
||||
}
|
||||
if ( m_hCloseEvent )
|
||||
{
|
||||
CloseHandle( m_hCloseEvent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT STDMETHODCALLTYPE CSimplePlayer::QueryInterface(
|
||||
REFIID riid,
|
||||
void **ppvObject )
|
||||
{
|
||||
return( E_NOINTERFACE );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
ULONG STDMETHODCALLTYPE CSimplePlayer::AddRef()
|
||||
{
|
||||
return( InterlockedIncrement( &m_cRef ) );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
ULONG STDMETHODCALLTYPE CSimplePlayer::Release()
|
||||
{
|
||||
ULONG uRet = InterlockedDecrement( &m_cRef );
|
||||
|
||||
if( 0 == uRet )
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
return( uRet );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT STDMETHODCALLTYPE CSimplePlayer::OnSample(
|
||||
/* [in] */ DWORD dwOutputNum,
|
||||
/* [in] */ QWORD cnsSampleTime,
|
||||
/* [in] */ QWORD cnsSampleDuration,
|
||||
/* [in] */ DWORD dwFlags,
|
||||
/* [in] */ INSSBuffer __RPC_FAR *pSample,
|
||||
/* [in] */ VOID *pvContext )
|
||||
{
|
||||
if( 0 != dwOutputNum )
|
||||
{
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
BYTE *pData;
|
||||
DWORD cbData;
|
||||
|
||||
//
|
||||
// first UnprepareHeader and remove everthing in the ready list
|
||||
//
|
||||
RemoveWaveHeaders( );
|
||||
|
||||
hr = pSample->GetBufferAndLength( &pData, &cbData );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
DEBUG_LOG(( " New Sample of length %d and PS time %d ms\n",
|
||||
cbData, ( DWORD ) ( cnsSampleTime / 10000 ) ));
|
||||
|
||||
LPWAVEHDR pwh = (LPWAVEHDR) new BYTE[ sizeof( WAVEHDR ) + cbData ];
|
||||
|
||||
if( NULL == pwh )
|
||||
{
|
||||
DEBUG_LOG(( "OnSample OUT OF MEMORY! \n"));
|
||||
|
||||
*m_phrCompletion = E_OUTOFMEMORY;
|
||||
SetEvent( m_hCompletionEvent );
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
pwh->lpData = (LPSTR)&pwh[1];
|
||||
pwh->dwBufferLength = cbData;
|
||||
pwh->dwBytesRecorded = cbData;
|
||||
pwh->dwUser = 0;
|
||||
pwh->dwLoops = 0;
|
||||
pwh->dwFlags = 0;
|
||||
|
||||
CopyMemory( pwh->lpData, pData, cbData );
|
||||
|
||||
MMRESULT mmr;
|
||||
|
||||
mmr = waveOutPrepareHeader( m_hwo, pwh, sizeof(WAVEHDR) );
|
||||
mmr = MMSYSERR_NOERROR;
|
||||
|
||||
if( mmr != MMSYSERR_NOERROR )
|
||||
{
|
||||
DEBUG_LOG(( "failed to prepare wave buffer, error=%lu\n" , mmr ));
|
||||
*m_phrCompletion = E_UNEXPECTED;
|
||||
SetEvent( m_hCompletionEvent );
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
mmr = waveOutWrite( m_hwo, pwh, sizeof(WAVEHDR) );
|
||||
mmr = MMSYSERR_NOERROR;
|
||||
|
||||
if( mmr != MMSYSERR_NOERROR )
|
||||
{
|
||||
delete pwh;
|
||||
|
||||
DEBUG_LOG(( "failed to write wave sample, error=%lu\n" , mmr ));
|
||||
*m_phrCompletion = E_UNEXPECTED;
|
||||
SetEvent( m_hCompletionEvent );
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
InterlockedIncrement( &m_cBuffersOutstanding );
|
||||
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CSimplePlayer::Play( LPCWSTR pszUrl, DWORD dwSecDuration, HANDLE hCompletionEvent, HRESULT *phrCompletion )
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
//
|
||||
// If the URL is not a UNC path, a full path, or an Internet-style URL then assume it is
|
||||
// a relative local file name that needs to be expanded to a full path.
|
||||
//
|
||||
WCHAR wszFullUrl[ MAX_PATH ];
|
||||
|
||||
if( ( 0 == wcsstr( pszUrl, L"\\\\" ) )
|
||||
&& ( 0 == wcsstr( pszUrl, L":\\" ) )
|
||||
&& ( 0 == wcsstr( pszUrl, L"://" ) ) )
|
||||
{
|
||||
//
|
||||
// Expand to a full path name
|
||||
//
|
||||
LPWSTR pszCheck = _wfullpath( wszFullUrl, pszUrl, MAX_PATH );
|
||||
|
||||
if( NULL == pszCheck )
|
||||
{
|
||||
DEBUG_LOG(( "internal error %lu\n" , GetLastError() ));
|
||||
return E_UNEXPECTED ;
|
||||
}
|
||||
|
||||
pszUrl = wszFullUrl;
|
||||
}
|
||||
|
||||
//
|
||||
// Save a copy of the URL
|
||||
//
|
||||
delete[] m_pszUrl;
|
||||
|
||||
m_pszUrl = new WCHAR[ wcslen( pszUrl ) + 1 ];
|
||||
|
||||
if( NULL == m_pszUrl )
|
||||
{
|
||||
DEBUG_LOG(( "insufficient Memory\n" )) ;
|
||||
return( E_OUTOFMEMORY );
|
||||
}
|
||||
|
||||
wcscpy( m_pszUrl, pszUrl );
|
||||
|
||||
//
|
||||
// Attempt to open the URL
|
||||
//
|
||||
m_hCompletionEvent = hCompletionEvent;
|
||||
|
||||
m_phrCompletion = phrCompletion;
|
||||
|
||||
#ifdef SUPPORT_DRM
|
||||
|
||||
hr = WMCreateReader( NULL, WMT_RIGHT_PLAYBACK, &m_pReader );
|
||||
|
||||
#else
|
||||
|
||||
hr = WMCreateReader( NULL, 0, &m_pReader );
|
||||
|
||||
#endif
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed to create audio reader (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
//
|
||||
// Open the file
|
||||
//
|
||||
hr = m_pReader->Open( m_pszUrl, this, NULL );
|
||||
if ( SUCCEEDED( hr ) )
|
||||
{
|
||||
WaitForSingleObject( m_hOpenEvent, INFINITE );
|
||||
hr = m_hrOpen;
|
||||
}
|
||||
if ( NS_E_NO_STREAM == hr )
|
||||
{
|
||||
DEBUG_LOG(( "Waiting for transmission to begin...\n" ));
|
||||
WaitForSingleObject( m_hOpenEvent, INFINITE );
|
||||
hr = m_hrOpen;
|
||||
}
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed to open (hr=0x%08x)\n", hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// It worked! Display various attributes
|
||||
//
|
||||
hr = m_pReader->QueryInterface( IID_IWMHeaderInfo, ( VOID ** )&m_pHeader );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed to qi for header interface (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
WORD i, wAttrCnt;
|
||||
|
||||
hr = m_pHeader->GetAttributeCount( 0, &wAttrCnt );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "GetAttributeCount Failed (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
WCHAR *pwszName = NULL;
|
||||
BYTE *pValue = NULL;
|
||||
|
||||
for ( i = 0; i < wAttrCnt ; i++ )
|
||||
{
|
||||
WORD wStream = 0;
|
||||
WORD cchNamelen = 0;
|
||||
WMT_ATTR_DATATYPE type;
|
||||
WORD cbLength = 0;
|
||||
|
||||
hr = m_pHeader->GetAttributeByIndex( i, &wStream, NULL, &cchNamelen, &type, NULL, &cbLength );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
|
||||
break;
|
||||
}
|
||||
|
||||
pwszName = new WCHAR[ cchNamelen ];
|
||||
pValue = new BYTE[ cbLength ];
|
||||
|
||||
if( NULL == pwszName || NULL == pValue )
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
hr = m_pHeader->GetAttributeByIndex( i, &wStream, pwszName, &cchNamelen, &type, pValue, &cbLength );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "GetAttributeByIndex Failed (hr=0x%08x)\n" , hr ));
|
||||
break;
|
||||
}
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case WMT_TYPE_DWORD:
|
||||
DEBUG_LOG(("%ws: %u\n" , pwszName, *((DWORD *) pValue) ));
|
||||
break;
|
||||
case WMT_TYPE_STRING:
|
||||
DEBUG_LOG(("%ws: %ws\n" , pwszName, (WCHAR *) pValue ));
|
||||
break;
|
||||
case WMT_TYPE_BINARY:
|
||||
DEBUG_LOG(("%ws: Type = Binary of Length %u\n" , pwszName, cbLength ));
|
||||
break;
|
||||
case WMT_TYPE_BOOL:
|
||||
DEBUG_LOG(("%ws: %s\n" , pwszName, ( * ( ( BOOL * ) pValue) ? _T( "true" ) : _T( "false" ) ) ));
|
||||
break;
|
||||
case WMT_TYPE_WORD:
|
||||
DEBUG_LOG(("%ws: %hu\n" , pwszName, *((WORD *) pValue) ));
|
||||
break;
|
||||
case WMT_TYPE_QWORD:
|
||||
DEBUG_LOG(("%ws: %I64u\n" , pwszName, *((QWORD *) pValue) ));
|
||||
break;
|
||||
case WMT_TYPE_GUID:
|
||||
DEBUG_LOG(("%ws: %I64x%I64x\n" , pwszName, *((QWORD *) pValue), *((QWORD *) pValue + 1) ));
|
||||
break;
|
||||
default:
|
||||
DEBUG_LOG(("%ws: Type = %d, Length %u\n" , pwszName, type, cbLength ));
|
||||
break;
|
||||
}
|
||||
|
||||
delete pwszName;
|
||||
pwszName = NULL;
|
||||
|
||||
delete pValue;
|
||||
pValue = NULL;
|
||||
}
|
||||
|
||||
delete pwszName;
|
||||
pwszName = NULL;
|
||||
|
||||
delete pValue;
|
||||
pValue = NULL;
|
||||
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
return( hr );
|
||||
}
|
||||
|
||||
//
|
||||
// Make sure we're audio only
|
||||
//
|
||||
DWORD cOutputs;
|
||||
|
||||
hr = m_pReader->GetOutputCount( &cOutputs );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed GetOutputCount(), (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
if ( cOutputs != 1 )
|
||||
{
|
||||
DEBUG_LOG(( "Not audio only (cOutputs = %d).\n" , cOutputs ));
|
||||
// return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
IWMOutputMediaProps *pProps;
|
||||
hr = m_pReader->GetOutputProps( 0, &pProps );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed GetOutputProps(), (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
DWORD cbBuffer = 0;
|
||||
|
||||
hr = pProps->GetMediaType( NULL, &cbBuffer );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
pProps->Release( );
|
||||
DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
WM_MEDIA_TYPE *pMediaType = ( WM_MEDIA_TYPE * ) new BYTE[cbBuffer] ;
|
||||
|
||||
hr = pProps->GetMediaType( pMediaType, &cbBuffer );
|
||||
if ( FAILED( hr ) )
|
||||
{
|
||||
pProps->Release( );
|
||||
DEBUG_LOG(( "GetMediaType failed (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
pProps->Release( );
|
||||
|
||||
if ( pMediaType->majortype != WMMEDIATYPE_Audio )
|
||||
{
|
||||
delete[] (BYTE *) pMediaType ;
|
||||
DEBUG_LOG(( "Not audio only (major type mismatch).\n" ));
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
//
|
||||
// Set up for audio playback
|
||||
//
|
||||
WAVEFORMATEX *pwfx = ( WAVEFORMATEX * )pMediaType->pbFormat;
|
||||
memcpy( &m_wfx, pwfx, sizeof( WAVEFORMATEX ) + pwfx->cbSize );
|
||||
|
||||
delete[] (BYTE *)pMediaType ;
|
||||
pMediaType = NULL ;
|
||||
|
||||
MMRESULT mmr;
|
||||
|
||||
mmr = waveOutOpen( &m_hwo,
|
||||
WAVE_MAPPER,
|
||||
&m_wfx,
|
||||
(DWORD)WaveProc,
|
||||
(DWORD)this,
|
||||
CALLBACK_FUNCTION );
|
||||
mmr = MMSYSERR_NOERROR;
|
||||
|
||||
if( mmr != MMSYSERR_NOERROR )
|
||||
{
|
||||
|
||||
DEBUG_LOG(( "failed to open wav output device, error=%lu\n" , mmr ));
|
||||
return( E_UNEXPECTED );
|
||||
}
|
||||
|
||||
//
|
||||
// Start reading the data (and rendering the audio)
|
||||
//
|
||||
QWORD cnsDuration = ( QWORD ) dwSecDuration * 10000000;
|
||||
hr = m_pReader->Start( 0, cnsDuration, 1.0, NULL );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "failed Start(), (hr=0x%08x)\n" , hr ));
|
||||
return( hr );
|
||||
}
|
||||
|
||||
return( hr );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT STDMETHODCALLTYPE CSimplePlayer::OnStatus(
|
||||
/* [in] */ WMT_STATUS Status,
|
||||
/* [in] */ HRESULT hr,
|
||||
/* [in] */ WMT_ATTR_DATATYPE dwType,
|
||||
/* [in] */ BYTE __RPC_FAR *pValue,
|
||||
/* [in] */ void __RPC_FAR *pvContext)
|
||||
{
|
||||
switch( Status )
|
||||
{
|
||||
case WMT_OPENED:
|
||||
DEBUG_LOG(( "OnStatus( WMT_OPENED )\n" ));
|
||||
m_hrOpen = hr;
|
||||
SetEvent( m_hOpenEvent );
|
||||
break;
|
||||
|
||||
case WMT_SOURCE_SWITCH:
|
||||
DEBUG_LOG(( "OnStatus( WMT_SOURCE_SWITCH )\n" ));
|
||||
m_hrOpen = hr;
|
||||
SetEvent( m_hOpenEvent );
|
||||
break;
|
||||
|
||||
case WMT_ERROR:
|
||||
DEBUG_LOG(( "OnStatus( WMT_ERROR )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_STARTED:
|
||||
DEBUG_LOG(( "OnStatus( WMT_STARTED )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_STOPPED:
|
||||
DEBUG_LOG(( "OnStatus( WMT_STOPPED )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_BUFFERING_START:
|
||||
DEBUG_LOG(( "OnStatus( WMT_BUFFERING START)\n" ));
|
||||
break;
|
||||
|
||||
case WMT_BUFFERING_STOP:
|
||||
DEBUG_LOG(( "OnStatus( WMT_BUFFERING STOP)\n" ));
|
||||
break;
|
||||
|
||||
case WMT_EOF:
|
||||
DEBUG_LOG(( "OnStatus( WMT_EOF )\n" ));
|
||||
|
||||
//
|
||||
// cleanup and exit
|
||||
//
|
||||
|
||||
m_fEof = TRUE;
|
||||
|
||||
if( 0 == m_cBuffersOutstanding )
|
||||
{
|
||||
SetEvent( m_hCompletionEvent );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMT_END_OF_SEGMENT:
|
||||
DEBUG_LOG(( "OnStatus( WMT_END_OF_SEGMENT )\n" ));
|
||||
|
||||
//
|
||||
// cleanup and exit
|
||||
//
|
||||
|
||||
m_fEof = TRUE;
|
||||
|
||||
if( 0 == m_cBuffersOutstanding )
|
||||
{
|
||||
SetEvent( m_hCompletionEvent );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WMT_LOCATING:
|
||||
DEBUG_LOG(( "OnStatus( WMT_LOCATING )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_CONNECTING:
|
||||
DEBUG_LOG(( "OnStatus( WMT_CONNECTING )\n" ));
|
||||
break;
|
||||
|
||||
case WMT_NO_RIGHTS:
|
||||
{
|
||||
LPWSTR pwszEscapedURL = NULL;
|
||||
|
||||
hr = MakeEscapedURL( m_pszUrl, &pwszEscapedURL );
|
||||
|
||||
if( SUCCEEDED( hr ) )
|
||||
{
|
||||
WCHAR wszURL[ 0x1000 ];
|
||||
|
||||
swprintf( wszURL, L"%s&filename=%s&embedded=false", pValue, pwszEscapedURL );
|
||||
|
||||
hr = LaunchURL( wszURL );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
DEBUG_LOG(( "Unable to launch web browser to retrieve playback license (hr=0x%08x)\n" , hr ));
|
||||
}
|
||||
|
||||
delete [] pwszEscapedURL;
|
||||
pwszEscapedURL = NULL ;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WMT_MISSING_CODEC:
|
||||
{
|
||||
DEBUG_LOG(( "Missing codec: (hr=0x%08x)\n" , hr ));
|
||||
break;
|
||||
}
|
||||
|
||||
case WMT_CLOSED:
|
||||
SetEvent( m_hCloseEvent );
|
||||
break;
|
||||
};
|
||||
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CSimplePlayer::Close()
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if( NULL != m_pReader )
|
||||
{
|
||||
hr = m_pReader->Close();
|
||||
|
||||
if( SUCCEEDED( hr ) )
|
||||
{
|
||||
WaitForSingleObject( m_hCloseEvent, INFINITE );
|
||||
}
|
||||
}
|
||||
|
||||
return( hr );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void CSimplePlayer::OnWaveOutMsg( UINT uMsg, DWORD dwParam1, DWORD dwParam2 )
|
||||
{
|
||||
if( WOM_DONE == uMsg )
|
||||
{
|
||||
//
|
||||
// add the wave header to ready-to-free list for the caller
|
||||
// to pick up and free in the next OnSample call
|
||||
//
|
||||
AddWaveHeader( ( LPWAVEHDR )dwParam1 );
|
||||
|
||||
InterlockedDecrement( &m_cBuffersOutstanding );
|
||||
|
||||
if( m_fEof && ( 0 == m_cBuffersOutstanding ) )
|
||||
{
|
||||
SetEvent( m_hCompletionEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void CALLBACK CSimplePlayer::WaveProc(
|
||||
HWAVEOUT hwo,
|
||||
UINT uMsg,
|
||||
DWORD dwInstance,
|
||||
DWORD dwParam1,
|
||||
DWORD dwParam2 )
|
||||
{
|
||||
CSimplePlayer *pThis = (CSimplePlayer*)dwInstance;
|
||||
|
||||
pThis->OnWaveOutMsg( uMsg, dwParam1, dwParam2 );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT CSimplePlayer::AddWaveHeader( LPWAVEHDR pwh )
|
||||
{
|
||||
WAVEHDR_LIST *tmp = new WAVEHDR_LIST;
|
||||
if( NULL == tmp )
|
||||
{
|
||||
return( E_OUTOFMEMORY );
|
||||
}
|
||||
tmp->pwh = pwh;
|
||||
|
||||
EnterCriticalSection( &m_CriSec );
|
||||
tmp->next = m_whdrHead;
|
||||
m_whdrHead = tmp;
|
||||
LeaveCriticalSection( &m_CriSec );
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
void CSimplePlayer::RemoveWaveHeaders( )
|
||||
{
|
||||
WAVEHDR_LIST *tmp;
|
||||
|
||||
EnterCriticalSection( &m_CriSec );
|
||||
while( NULL != m_whdrHead )
|
||||
{
|
||||
tmp = m_whdrHead->next;
|
||||
DEBUG_ASSERTCRASH( m_whdrHead->pwh->dwFlags & WHDR_DONE, ("RemoveWaveHeaders!") );
|
||||
waveOutUnprepareHeader( m_hwo, m_whdrHead->pwh, sizeof( WAVEHDR ) );
|
||||
delete m_whdrHead->pwh;
|
||||
delete m_whdrHead;
|
||||
m_whdrHead = tmp;
|
||||
}
|
||||
LeaveCriticalSection( &m_CriSec );
|
||||
}
|
||||
338
Generals/Code/GameEngine/Source/Common/Audio/urllaunch.cpp
Normal file
338
Generals/Code/GameEngine/Source/Common/Audio/urllaunch.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "Common/URLLaunch.h"
|
||||
|
||||
#define FILE_PREFIX L"file://"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT MakeEscapedURL( LPWSTR pszInURL, LPWSTR *ppszOutURL )
|
||||
{
|
||||
if( ( NULL == pszInURL ) || ( NULL == ppszOutURL ) )
|
||||
{
|
||||
return( E_INVALIDARG );
|
||||
}
|
||||
|
||||
//
|
||||
// Do we need to pre-pend file://?
|
||||
//
|
||||
BOOL fNeedFilePrefix = ( 0 == wcsstr( pszInURL, L"://" ) );
|
||||
|
||||
//
|
||||
// Count how many characters need to be escaped
|
||||
//
|
||||
LPWSTR pszTemp = pszInURL;
|
||||
DWORD cEscapees = 0;
|
||||
|
||||
while( TRUE )
|
||||
{
|
||||
LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
|
||||
|
||||
if( NULL == pchToEscape )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cEscapees++;
|
||||
|
||||
pszTemp = pchToEscape + 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate sufficient outgoing buffer space
|
||||
//
|
||||
int cchNeeded = wcslen( pszInURL ) + ( 2 * cEscapees ) + 1;
|
||||
|
||||
if( fNeedFilePrefix )
|
||||
{
|
||||
cchNeeded += wcslen( FILE_PREFIX );
|
||||
}
|
||||
|
||||
*ppszOutURL = new WCHAR[ cchNeeded ];
|
||||
|
||||
if( NULL == *ppszOutURL )
|
||||
{
|
||||
return( E_OUTOFMEMORY );
|
||||
}
|
||||
|
||||
//
|
||||
// Fill in the outgoing escaped buffer
|
||||
//
|
||||
pszTemp = pszInURL;
|
||||
|
||||
LPWSTR pchNext = *ppszOutURL;
|
||||
|
||||
if( fNeedFilePrefix )
|
||||
{
|
||||
wcscpy( *ppszOutURL, FILE_PREFIX );
|
||||
pchNext += wcslen( FILE_PREFIX );
|
||||
}
|
||||
|
||||
while( TRUE )
|
||||
{
|
||||
LPWSTR pchToEscape = wcspbrk( pszTemp, L" #$%&\\+,;=@[]^{}" );
|
||||
|
||||
if( NULL == pchToEscape )
|
||||
{
|
||||
//
|
||||
// Copy the rest of the input string and get out
|
||||
//
|
||||
wcscpy( pchNext, pszTemp );
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Copy all characters since the previous escapee
|
||||
//
|
||||
int cchToCopy = pchToEscape - pszTemp;
|
||||
|
||||
if( cchToCopy > 0 )
|
||||
{
|
||||
wcsncpy( pchNext, pszTemp, cchToCopy );
|
||||
|
||||
pchNext += cchToCopy;
|
||||
}
|
||||
|
||||
//
|
||||
// Expand this character into an escape code and move on
|
||||
//
|
||||
pchNext += swprintf( pchNext, L"%%%02x", *pchToEscape );
|
||||
|
||||
pszTemp = pchToEscape + 1;
|
||||
}
|
||||
|
||||
return( S_OK );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT GetShellOpenCommand( LPTSTR ptszShellOpenCommand, DWORD cbShellOpenCommand )
|
||||
{
|
||||
LONG lResult;
|
||||
|
||||
HKEY hKey = NULL;
|
||||
HKEY hFileKey = NULL;
|
||||
|
||||
BOOL fFoundExtensionCommand = FALSE;
|
||||
|
||||
do
|
||||
{
|
||||
//
|
||||
// Look for the file type associated with .html files
|
||||
//
|
||||
TCHAR szFileType[ MAX_PATH ];
|
||||
|
||||
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( ".html" ), 0, KEY_READ, &hKey );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD dwLength = sizeof( szFileType );
|
||||
|
||||
lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)szFileType, &dwLength );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Find the command for the shell's open verb associated with this file type
|
||||
//
|
||||
TCHAR szKeyName[ MAX_PATH + 20 ];
|
||||
|
||||
wsprintf( szKeyName, _T( "%s\\shell\\open\\command" ), szFileType );
|
||||
|
||||
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, szKeyName, 0, KEY_READ, &hFileKey );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
dwLength = cbShellOpenCommand;
|
||||
|
||||
lResult = RegQueryValueEx( hFileKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
|
||||
|
||||
if( 0 == lResult )
|
||||
{
|
||||
fFoundExtensionCommand = TRUE;
|
||||
}
|
||||
}
|
||||
while( FALSE );
|
||||
|
||||
//
|
||||
// If there was no application associated with .html files by extension, look for
|
||||
// an application associated with the http protocol
|
||||
//
|
||||
if( !fFoundExtensionCommand )
|
||||
{
|
||||
if( NULL != hKey )
|
||||
{
|
||||
RegCloseKey( hKey );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
//
|
||||
// Find the command for the shell's open verb associated with the http protocol
|
||||
//
|
||||
lResult = RegOpenKeyEx( HKEY_CLASSES_ROOT, _T( "http\\shell\\open\\command" ), 0, KEY_READ, &hKey );
|
||||
|
||||
if( ERROR_SUCCESS != lResult )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD dwLength = cbShellOpenCommand;
|
||||
|
||||
lResult = RegQueryValueEx( hKey, NULL, 0, NULL, (BYTE *)ptszShellOpenCommand, &dwLength );
|
||||
}
|
||||
while( FALSE );
|
||||
}
|
||||
|
||||
if( NULL != hKey )
|
||||
{
|
||||
RegCloseKey( hKey );
|
||||
}
|
||||
|
||||
if( NULL != hFileKey )
|
||||
{
|
||||
RegCloseKey( hFileKey );
|
||||
}
|
||||
|
||||
return( HRESULT_FROM_WIN32( lResult ) );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
HRESULT LaunchURL( LPCWSTR pszURL )
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
//
|
||||
// Find the appropriate command to launch URLs with
|
||||
//
|
||||
TCHAR szShellOpenCommand[ MAX_PATH * 2 ];
|
||||
|
||||
hr = GetShellOpenCommand( szShellOpenCommand, sizeof( szShellOpenCommand ) );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
return( hr );
|
||||
}
|
||||
|
||||
//
|
||||
// Build the appropriate command line, substituting our URL parameter
|
||||
//
|
||||
TCHAR szLaunchCommand[ 2000 ];
|
||||
|
||||
LPTSTR pszParam = _tcsstr( szShellOpenCommand, _T( "\"%1\"" ) );
|
||||
|
||||
if( NULL == pszParam )
|
||||
{
|
||||
pszParam = _tcsstr( szShellOpenCommand, _T( "\"%*\"" ) );
|
||||
}
|
||||
|
||||
if( NULL != pszParam )
|
||||
{
|
||||
*pszParam = _T( '\0' ) ;
|
||||
|
||||
wsprintf( szLaunchCommand, _T( "%s%ws%s" ), szShellOpenCommand, pszURL, pszParam + 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
wsprintf( szLaunchCommand, _T( "%s %ws" ), szShellOpenCommand, pszURL );
|
||||
}
|
||||
|
||||
//
|
||||
// Find the application name, stripping quotes if necessary
|
||||
//
|
||||
TCHAR szExe[ MAX_PATH * 2 ];
|
||||
LPTSTR pchFirst = szShellOpenCommand;
|
||||
LPTSTR pchNext = NULL;
|
||||
|
||||
while( _T( ' ' ) == *pchFirst )
|
||||
{
|
||||
pchFirst++;
|
||||
}
|
||||
|
||||
if( _T( '"' ) == *pchFirst )
|
||||
{
|
||||
pchFirst++;
|
||||
|
||||
pchNext = _tcschr( pchFirst, _T( '"' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
pchNext = _tcschr( pchFirst + 1, _T( ' ' ) );
|
||||
}
|
||||
|
||||
if( NULL == pchNext )
|
||||
{
|
||||
pchNext = szShellOpenCommand + _tcslen( szShellOpenCommand );
|
||||
}
|
||||
|
||||
_tcsncpy( szExe, pchFirst, pchNext - pchFirst );
|
||||
szExe[ pchNext - pchFirst ] = _T( '\0' ) ;
|
||||
|
||||
//
|
||||
// Because of the extremely long length of the URLs, neither
|
||||
// WinExec, nor ShellExecute, were working correctly. For this reason
|
||||
// we use CreateProcess. The CreateProcess documentation in MSDN says
|
||||
// that the most robust way to call CreateProcess is to pass the full
|
||||
// command line, where the first element is the application name, in the
|
||||
// lpCommandLine parameter. In our case this is necesssary to get Netscape
|
||||
// to function properly.
|
||||
//
|
||||
PROCESS_INFORMATION ProcInfo;
|
||||
ZeroMemory( (LPVOID)&ProcInfo, sizeof( PROCESS_INFORMATION ) );
|
||||
|
||||
STARTUPINFO StartUp;
|
||||
ZeroMemory( (LPVOID)&StartUp, sizeof( STARTUPINFO ) );
|
||||
|
||||
StartUp.cb = sizeof(STARTUPINFO);
|
||||
|
||||
if( !CreateProcess( szExe, szLaunchCommand, NULL, NULL,
|
||||
FALSE, 0, NULL, NULL, &StartUp, &ProcInfo) )
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// CreateProcess succeeded and we do not need the handles to the thread
|
||||
// or the process, so close them now.
|
||||
//
|
||||
if( NULL != ProcInfo.hThread )
|
||||
{
|
||||
CloseHandle( ProcInfo.hThread );
|
||||
}
|
||||
|
||||
if( NULL != ProcInfo.hProcess )
|
||||
{
|
||||
CloseHandle( ProcInfo.hProcess );
|
||||
}
|
||||
}
|
||||
|
||||
return( hr );
|
||||
}
|
||||
124
Generals/Code/GameEngine/Source/Common/Bezier/BezFwdIterator.cpp
Normal file
124
Generals/Code/GameEngine/Source/Common/Bezier/BezFwdIterator.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "Common/BezFwdIterator.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BezFwdIterator::BezFwdIterator(): mStep(0), mStepsDesired(0)
|
||||
{
|
||||
// Added by Sadullah Nader
|
||||
mCurrPoint.zero();
|
||||
mDDDq.zero();
|
||||
mDDq.zero();
|
||||
mDq.zero();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BezFwdIterator::BezFwdIterator(Int stepsDesired, const BezierSegment *bezSeg)
|
||||
{
|
||||
// Added by Sadullah Nader
|
||||
mCurrPoint.zero();
|
||||
mDDDq.zero();
|
||||
mDDq.zero();
|
||||
mDq.zero();
|
||||
//
|
||||
|
||||
mStepsDesired = stepsDesired;
|
||||
mBezSeg = (*bezSeg);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void BezFwdIterator::start(void)
|
||||
{
|
||||
mStep = 0;
|
||||
|
||||
if (mStepsDesired <= 1)
|
||||
return;
|
||||
|
||||
float d = 1.0f / (mStepsDesired - 1);
|
||||
float d2 = d * d;
|
||||
float d3 = d * d2;
|
||||
|
||||
D3DXVECTOR4 px(mBezSeg.m_controlPoints[0].x, mBezSeg.m_controlPoints[1].x, mBezSeg.m_controlPoints[2].x, mBezSeg.m_controlPoints[3].x);
|
||||
D3DXVECTOR4 py(mBezSeg.m_controlPoints[0].y, mBezSeg.m_controlPoints[1].y, mBezSeg.m_controlPoints[2].y, mBezSeg.m_controlPoints[3].y);
|
||||
D3DXVECTOR4 pz(mBezSeg.m_controlPoints[0].z, mBezSeg.m_controlPoints[1].z, mBezSeg.m_controlPoints[2].z, mBezSeg.m_controlPoints[3].z);
|
||||
|
||||
D3DXVECTOR4 cVec[3];
|
||||
D3DXVec4Transform(&cVec[0], &px, &BezierSegment::s_bezBasisMatrix);
|
||||
D3DXVec4Transform(&cVec[1], &py, &BezierSegment::s_bezBasisMatrix);
|
||||
D3DXVec4Transform(&cVec[2], &pz, &BezierSegment::s_bezBasisMatrix);
|
||||
|
||||
mCurrPoint = mBezSeg.m_controlPoints[0];
|
||||
|
||||
int i = 3;
|
||||
while (i--) {
|
||||
float a = cVec[i].x;
|
||||
float b = cVec[i].y;
|
||||
float c = cVec[i].z;
|
||||
|
||||
float *pD, *pDD, *pDDD;
|
||||
|
||||
if (i == 2) {
|
||||
pD = &mDq.z;
|
||||
pDD = &mDDq.z;
|
||||
pDDD = &mDDDq.z;
|
||||
} else if (i == 1) {
|
||||
pD = &mDq.y;
|
||||
pDD = &mDDq.y;
|
||||
pDDD = &mDDDq.y;
|
||||
} else if (i == 0) {
|
||||
pD = &mDq.x;
|
||||
pDD = &mDDq.x;
|
||||
pDDD = &mDDDq.x;
|
||||
}
|
||||
|
||||
(*pD) = a * d3 + b * d2 + c * d;
|
||||
(*pDD) = 6 * a * d3 + 2 * b * d2;
|
||||
(*pDDD) = 6 * a * d3;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool BezFwdIterator::done(void)
|
||||
{
|
||||
return (mStep >= mStepsDesired);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const Coord3D& BezFwdIterator::getCurrent(void) const
|
||||
{
|
||||
return mCurrPoint;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void BezFwdIterator::next(void)
|
||||
{
|
||||
mCurrPoint.add(&mDq);
|
||||
mDq.add(&mDDq);
|
||||
mDDq.add(&mDDDq);
|
||||
|
||||
++mStep;
|
||||
}
|
||||
|
||||
246
Generals/Code/GameEngine/Source/Common/Bezier/BezierSegment.cpp
Normal file
246
Generals/Code/GameEngine/Source/Common/Bezier/BezierSegment.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "Common/BezierSegment.h"
|
||||
#include "Common/BezFwdIterator.h"
|
||||
|
||||
#include <D3DX8Math.h>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BezierSegment::BezierSegment()
|
||||
{
|
||||
for(int i=0; i < 4; i++)
|
||||
m_controlPoints[i].zero();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
BezierSegment::BezierSegment(Real x0, Real y0, Real z0,
|
||||
Real x1, Real y1, Real z1,
|
||||
Real x2, Real y2, Real z2,
|
||||
Real x3, Real y3, Real z3)
|
||||
{
|
||||
m_controlPoints[0].x = x0;
|
||||
m_controlPoints[0].y = y0;
|
||||
m_controlPoints[0].z = z0;
|
||||
|
||||
m_controlPoints[1].x = x1;
|
||||
m_controlPoints[1].y = y1;
|
||||
m_controlPoints[1].z = z1;
|
||||
|
||||
m_controlPoints[2].x = x2;
|
||||
m_controlPoints[2].y = y2;
|
||||
m_controlPoints[2].z = z2;
|
||||
|
||||
m_controlPoints[3].x = x3;
|
||||
m_controlPoints[3].y = y3;
|
||||
m_controlPoints[3].z = z3;
|
||||
}
|
||||
|
||||
BezierSegment::BezierSegment(Real cp[12])
|
||||
{
|
||||
m_controlPoints[0].x = cp[0];
|
||||
m_controlPoints[0].y = cp[1];
|
||||
m_controlPoints[0].z = cp[2];
|
||||
|
||||
m_controlPoints[1].x = cp[3];
|
||||
m_controlPoints[1].y = cp[4];
|
||||
m_controlPoints[1].z = cp[5];
|
||||
|
||||
m_controlPoints[2].x = cp[6];
|
||||
m_controlPoints[2].y = cp[7];
|
||||
m_controlPoints[2].z = cp[8];
|
||||
|
||||
m_controlPoints[3].x = cp[9];
|
||||
m_controlPoints[3].y = cp[10];
|
||||
m_controlPoints[3].z = cp[11];
|
||||
}
|
||||
|
||||
BezierSegment::BezierSegment(const Coord3D& cp0, const Coord3D& cp1, const Coord3D& cp2, const Coord3D& cp3)
|
||||
{
|
||||
m_controlPoints[0] = cp0;
|
||||
m_controlPoints[1] = cp1;
|
||||
m_controlPoints[2] = cp2;
|
||||
m_controlPoints[3] = cp3;
|
||||
}
|
||||
|
||||
BezierSegment::BezierSegment(Coord3D cp[4])
|
||||
{
|
||||
m_controlPoints[0] = cp[0];
|
||||
m_controlPoints[1] = cp[1];
|
||||
m_controlPoints[2] = cp[2];
|
||||
m_controlPoints[3] = cp[3];
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void BezierSegment::evaluateBezSegmentAtT(Real tValue, Coord3D *outResult) const
|
||||
|
||||
{
|
||||
if (!outResult)
|
||||
return;
|
||||
|
||||
D3DXVECTOR4 tVec(tValue * tValue * tValue, tValue * tValue, tValue, 1);
|
||||
|
||||
D3DXVECTOR4 xCoords(m_controlPoints[0].x, m_controlPoints[1].x, m_controlPoints[2].x, m_controlPoints[3].x);
|
||||
D3DXVECTOR4 yCoords(m_controlPoints[0].y, m_controlPoints[1].y, m_controlPoints[2].y, m_controlPoints[3].y);
|
||||
D3DXVECTOR4 zCoords(m_controlPoints[0].z, m_controlPoints[1].z, m_controlPoints[2].z, m_controlPoints[3].z);
|
||||
|
||||
D3DXVECTOR4 tResult;
|
||||
D3DXVec4Transform(&tResult, &tVec, &BezierSegment::s_bezBasisMatrix);
|
||||
|
||||
outResult->x = D3DXVec4Dot(&xCoords, &tResult);
|
||||
outResult->y = D3DXVec4Dot(&yCoords, &tResult);
|
||||
outResult->z = D3DXVec4Dot(&zCoords, &tResult);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void BezierSegment::getSegmentPoints(Int numSegments, VecCoord3D *outResult) const
|
||||
{
|
||||
if (!outResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
outResult->clear();
|
||||
outResult->resize(numSegments);
|
||||
|
||||
BezFwdIterator iter(numSegments, this);
|
||||
iter.start();
|
||||
Int i = 0;
|
||||
while (!iter.done()) {
|
||||
(*outResult)[i] = iter.getCurrent();
|
||||
++i;
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// This function isn't terribly fast. There are alternatives, and if this is too slow, we can
|
||||
// take a look at the other approximations.
|
||||
// There is no known close-form solution to this problem.
|
||||
Real BezierSegment::getApproximateLength(Real withinTolerance) const
|
||||
{
|
||||
/*
|
||||
How this works:
|
||||
We can determine the approximate length of a bezier segment by
|
||||
L0 = |(P0,P1)| + |(P1,P2)| + |(P2,P3)|
|
||||
L1 = |(P0,P3)|
|
||||
|
||||
The length of the segment is approximately 1/2 L0 + 1/2 L1
|
||||
|
||||
P1__P2
|
||||
/ \
|
||||
P0----P3
|
||||
|
||||
The error in this is L1 - L0. If the error is too much, then we subdivide the curve and
|
||||
try again.
|
||||
*/
|
||||
|
||||
Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
|
||||
m_controlPoints[1].y - m_controlPoints[0].y,
|
||||
m_controlPoints[1].z - m_controlPoints[0].z, };
|
||||
|
||||
Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
|
||||
m_controlPoints[2].y - m_controlPoints[1].y,
|
||||
m_controlPoints[2].z - m_controlPoints[1].z, };
|
||||
|
||||
Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
|
||||
m_controlPoints[3].y - m_controlPoints[2].y,
|
||||
m_controlPoints[3].z - m_controlPoints[2].z, };
|
||||
|
||||
Coord3D p0p3 = { m_controlPoints[3].x - m_controlPoints[0].x,
|
||||
m_controlPoints[3].y - m_controlPoints[0].y,
|
||||
m_controlPoints[3].z - m_controlPoints[0].z, };
|
||||
|
||||
Real length0 = p0p3.length();
|
||||
Real length1 = p0p1.length() + p1p2.length() + p2p3.length();
|
||||
|
||||
if ((length1 - length0) > withinTolerance) {
|
||||
BezierSegment seg1, seg2;
|
||||
splitSegmentAtT(0.5f, seg1, seg2);
|
||||
return (seg1.getApproximateLength(withinTolerance) + seg2.getApproximateLength(withinTolerance));
|
||||
}
|
||||
|
||||
return (INT_TO_REAL(length0 + length1) / 2.0f);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void BezierSegment::splitSegmentAtT(Real tValue, BezierSegment &outSeg1, BezierSegment &outSeg2) const
|
||||
{
|
||||
// I think there are faster ways to do this. Could someone clue me in?
|
||||
|
||||
Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
|
||||
m_controlPoints[1].y - m_controlPoints[0].y,
|
||||
m_controlPoints[1].z - m_controlPoints[0].z, };
|
||||
|
||||
Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
|
||||
m_controlPoints[2].y - m_controlPoints[1].y,
|
||||
m_controlPoints[2].z - m_controlPoints[1].z, };
|
||||
|
||||
Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
|
||||
m_controlPoints[3].y - m_controlPoints[2].y,
|
||||
m_controlPoints[3].z - m_controlPoints[2].z, };
|
||||
|
||||
p0p1.scale(tValue);
|
||||
p1p2.scale(tValue);
|
||||
p2p3.scale(tValue);
|
||||
|
||||
p0p1.add(&m_controlPoints[0]);
|
||||
p1p2.add(&m_controlPoints[1]);
|
||||
p2p3.add(&m_controlPoints[2]);
|
||||
|
||||
Coord3D triLeft = { p1p2.x - p0p1.x,
|
||||
p1p2.y - p0p1.y,
|
||||
p1p2.z - p0p1.z, };
|
||||
|
||||
Coord3D triRight = { p2p3.x - p1p2.x,
|
||||
p2p3.y - p1p2.y,
|
||||
p2p3.z - p1p2.z, };
|
||||
|
||||
triLeft.scale(tValue);
|
||||
triRight.scale(tValue);
|
||||
|
||||
triLeft.add(&p0p1);
|
||||
triRight.add(&p1p2);
|
||||
|
||||
outSeg1.m_controlPoints[0] = m_controlPoints[0];
|
||||
outSeg1.m_controlPoints[1] = p0p1;
|
||||
outSeg1.m_controlPoints[2] = triLeft;
|
||||
evaluateBezSegmentAtT(tValue, &outSeg1.m_controlPoints[3]);
|
||||
|
||||
outSeg2.m_controlPoints[0] = outSeg1.m_controlPoints[3];
|
||||
outSeg2.m_controlPoints[1] = triRight;
|
||||
outSeg2.m_controlPoints[2] = p2p3;
|
||||
outSeg2.m_controlPoints[3] = m_controlPoints[3];
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// The Basis Matrix for a bezier segment
|
||||
const D3DXMATRIX BezierSegment::s_bezBasisMatrix(
|
||||
-1.0f, 3.0f, -3.0f, 1.0f,
|
||||
3.0f, -6.0f, 3.0f, 0.0f,
|
||||
-3.0f, 3.0f, 0.0f, 0.0f,
|
||||
1.0f, 0.0f, 0.0f, 0.0f
|
||||
);
|
||||
160
Generals/Code/GameEngine/Source/Common/BitFlags.cpp
Normal file
160
Generals/Code/GameEngine/Source/Common/BitFlags.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: BitFlags.cpp ///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Used to set detail levels of various game systems.
|
||||
// Steven Johnson Wilczynski, Sept 2002
|
||||
//
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
|
||||
|
||||
#include "Common/BitFlags.h"
|
||||
#include "Common/BitFlagsIO.h"
|
||||
#include "Common/ModelState.h"
|
||||
#include "GameLogic/ArmorSet.h"
|
||||
|
||||
const char* ModelConditionFlags::s_bitNameList[] =
|
||||
{
|
||||
"TOPPLED",
|
||||
"FRONTCRUSHED",
|
||||
"BACKCRUSHED",
|
||||
"DAMAGED",
|
||||
"REALLYDAMAGED",
|
||||
"RUBBLE",
|
||||
"SPECIAL_DAMAGED",
|
||||
"NIGHT",
|
||||
"SNOW",
|
||||
"PARACHUTING",
|
||||
"GARRISONED",
|
||||
"ENEMYNEAR",
|
||||
"WEAPONSET_VETERAN",
|
||||
"WEAPONSET_ELITE",
|
||||
"WEAPONSET_HERO",
|
||||
"WEAPONSET_CRATEUPGRADE_ONE",
|
||||
"WEAPONSET_CRATEUPGRADE_TWO",
|
||||
"WEAPONSET_PLAYER_UPGRADE",
|
||||
"DOOR_1_OPENING",
|
||||
"DOOR_1_CLOSING",
|
||||
"DOOR_1_WAITING_OPEN",
|
||||
"DOOR_1_WAITING_TO_CLOSE",
|
||||
"DOOR_2_OPENING",
|
||||
"DOOR_2_CLOSING",
|
||||
"DOOR_2_WAITING_OPEN",
|
||||
"DOOR_2_WAITING_TO_CLOSE",
|
||||
"DOOR_3_OPENING",
|
||||
"DOOR_3_CLOSING",
|
||||
"DOOR_3_WAITING_OPEN",
|
||||
"DOOR_3_WAITING_TO_CLOSE",
|
||||
"DOOR_4_OPENING",
|
||||
"DOOR_4_CLOSING",
|
||||
"DOOR_4_WAITING_OPEN",
|
||||
"DOOR_4_WAITING_TO_CLOSE",
|
||||
"ATTACKING",
|
||||
"PREATTACK_A",
|
||||
"FIRING_A",
|
||||
"BETWEEN_FIRING_SHOTS_A",
|
||||
"RELOADING_A",
|
||||
"PREATTACK_B",
|
||||
"FIRING_B",
|
||||
"BETWEEN_FIRING_SHOTS_B",
|
||||
"RELOADING_B",
|
||||
"PREATTACK_C",
|
||||
"FIRING_C",
|
||||
"BETWEEN_FIRING_SHOTS_C",
|
||||
"RELOADING_C",
|
||||
"TURRET_ROTATE",
|
||||
"POST_COLLAPSE",
|
||||
"MOVING",
|
||||
"DYING",
|
||||
"AWAITING_CONSTRUCTION",
|
||||
"PARTIALLY_CONSTRUCTED",
|
||||
"ACTIVELY_BEING_CONSTRUCTED",
|
||||
"PRONE",
|
||||
"FREEFALL",
|
||||
"ACTIVELY_CONSTRUCTING",
|
||||
"CONSTRUCTION_COMPLETE",
|
||||
"RADAR_EXTENDING",
|
||||
"RADAR_UPGRADED",
|
||||
"PANICKING", // yes, it's spelled with a "k". look it up.
|
||||
"AFLAME",
|
||||
"SMOLDERING",
|
||||
"BURNED",
|
||||
"DOCKING",
|
||||
"DOCKING_BEGINNING",
|
||||
"DOCKING_ACTIVE",
|
||||
"DOCKING_ENDING",
|
||||
"CARRYING",
|
||||
"FLOODED",
|
||||
"LOADED",
|
||||
"JETAFTERBURNER",
|
||||
"JETEXHAUST",
|
||||
"PACKING",
|
||||
"UNPACKING",
|
||||
"DEPLOYED",
|
||||
"OVER_WATER",
|
||||
"POWER_PLANT_UPGRADED",
|
||||
"CLIMBING",
|
||||
"SOLD",
|
||||
#ifdef ALLOW_SURRENDER
|
||||
"SURRENDER",
|
||||
#endif
|
||||
"RAPPELLING",
|
||||
"ARMED",
|
||||
"POWER_PLANT_UPGRADING",
|
||||
|
||||
"SPECIAL_CHEERING",
|
||||
|
||||
"CONTINUOUS_FIRE_SLOW",
|
||||
"CONTINUOUS_FIRE_MEAN",
|
||||
"CONTINUOUS_FIRE_FAST",
|
||||
|
||||
"RAISING_FLAG",
|
||||
"CAPTURED",
|
||||
|
||||
"EXPLODED_FLAILING",
|
||||
"EXPLODED_BOUNCING",
|
||||
"SPLATTED",
|
||||
|
||||
"USING_WEAPON_A",
|
||||
"USING_WEAPON_B",
|
||||
"USING_WEAPON_C",
|
||||
|
||||
"PREORDER",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
const char* ArmorSetFlags::s_bitNameList[] =
|
||||
{
|
||||
"VETERAN",
|
||||
"ELITE",
|
||||
"HERO",
|
||||
"PLAYER_UPGRADE",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
273
Generals/Code/GameEngine/Source/Common/CRCDebug.cpp
Normal file
273
Generals/Code/GameEngine/Source/Common/CRCDebug.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/CRCDebug.h"
|
||||
#include "Common/Debug.h"
|
||||
#include "Common/PerfTimer.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
#include "GameNetwork/IPEnumeration.h"
|
||||
#include <cstdarg>
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_CRC
|
||||
|
||||
static const Int MaxStrings = 64000;
|
||||
|
||||
static char DebugStrings[MaxStrings][1024];
|
||||
static Int nextDebugString = 0;
|
||||
static Int numDebugStrings = 0;
|
||||
//static char DumpStrings[MaxStrings][1024];
|
||||
//static Int nextDumpString = 0;
|
||||
//static Int numDumpStrings = 0;
|
||||
|
||||
#define IS_FRAME_OK_TO_LOG TheGameLogic->isInGame() && !TheGameLogic->isInShellGame() && !TheDebugIgnoreSyncErrors && \
|
||||
TheCRCFirstFrameToLog >= 0 && TheCRCFirstFrameToLog <= TheGameLogic->getFrame() \
|
||||
&& TheGameLogic->getFrame() <= TheCRCLastFrameToLog
|
||||
|
||||
CRCVerification::CRCVerification()
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
/**/
|
||||
if (g_verifyClientCRC && (IS_FRAME_OK_TO_LOG))
|
||||
{
|
||||
m_startCRC = TheGameLogic->getCRC(CRC_RECALC, (g_clientDeepCRC)?"clientPre.crc":"");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_startCRC = 0;
|
||||
}
|
||||
/**/
|
||||
#endif
|
||||
}
|
||||
|
||||
CRCVerification::~CRCVerification()
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
/**/
|
||||
UnsignedInt endCRC = 0;
|
||||
if (g_verifyClientCRC && (IS_FRAME_OK_TO_LOG))
|
||||
{
|
||||
endCRC = TheGameLogic->getCRC(CRC_RECALC, (g_clientDeepCRC)?"clientPost.crc":"");
|
||||
}
|
||||
DEBUG_ASSERTCRASH(!TheGameLogic->isInGame() || m_startCRC == endCRC, ("GameLogic changed outside of GameLogic::update() on frame %d!", TheGameLogic->getFrame()));
|
||||
if (TheGameLogic->isInMultiplayerGame() && m_startCRC != endCRC)
|
||||
{
|
||||
if (TheInGameUI)
|
||||
{
|
||||
TheInGameUI->message(UnicodeString(L"GameLogic changed outside of GameLogic::update() - call Matt (x36804)!"));
|
||||
}
|
||||
CRCDEBUG_LOG(("GameLogic changed outside of GameLogic::update()!!!\n"));
|
||||
}
|
||||
/**/
|
||||
#endif
|
||||
}
|
||||
|
||||
static Bool dumped = FALSE;
|
||||
void outputCRCDebugLines( void )
|
||||
{
|
||||
if (dumped)
|
||||
return;
|
||||
dumped = TRUE;
|
||||
IPEnumeration ips;
|
||||
AsciiString fname;
|
||||
fname.format("crcDebug%s.txt", ips.getMachineName().str());
|
||||
FILE *fp = fopen(fname.str(), "wt");
|
||||
int start = 0;
|
||||
int end = nextDebugString;
|
||||
if (numDebugStrings >= MaxStrings)
|
||||
start = nextDebugString - MaxStrings;
|
||||
|
||||
for (Int i=start; i<end; ++i)
|
||||
{
|
||||
const char *line = DebugStrings[ (i + MaxStrings) % MaxStrings ];
|
||||
DEBUG_LOG(("%s\n", line));
|
||||
if (fp) fprintf(fp, "%s\n", line);
|
||||
}
|
||||
|
||||
if (fp) fclose(fp);
|
||||
}
|
||||
|
||||
void outputCRCDumpLines( void )
|
||||
{
|
||||
/*
|
||||
int start = 0;
|
||||
int end = nextDumpString;
|
||||
if (numDumpStrings >= MaxStrings)
|
||||
start = nextDumpString - MaxStrings;
|
||||
|
||||
for (Int i=start; i<end; ++i)
|
||||
{
|
||||
const char *line = DumpStrings[ (i + MaxStrings) % MaxStrings ];
|
||||
DEBUG_LOG(("%s", line));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static AsciiString getFname(AsciiString path)
|
||||
{
|
||||
return path.reverseFind('\\') + 1;
|
||||
}
|
||||
|
||||
Int lastCRCDebugFrame = 0;
|
||||
Int lastCRCDebugIndex = 0;
|
||||
extern Bool inCRCGen;
|
||||
void addCRCDebugLine(const char *fmt, ...)
|
||||
{
|
||||
if (dumped)// || inCRCGen /*|| !TheGameLogic->isInGameLogicUpdate()*/)
|
||||
return;
|
||||
|
||||
if (IS_FRAME_OK_TO_LOG)
|
||||
{
|
||||
|
||||
if (lastCRCDebugFrame != TheGameLogic->getFrame())
|
||||
{
|
||||
lastCRCDebugFrame = TheGameLogic->getFrame();
|
||||
lastCRCDebugIndex = 0;
|
||||
}
|
||||
|
||||
sprintf(DebugStrings[nextDebugString], "%d:%d ", TheGameLogic->getFrame(), lastCRCDebugIndex++);
|
||||
//DebugStrings[nextDebugString][0] = 0;
|
||||
Int len = strlen(DebugStrings[nextDebugString]);
|
||||
|
||||
va_list va;
|
||||
va_start( va, fmt );
|
||||
_vsnprintf(DebugStrings[nextDebugString]+len, 1024-len, fmt, va );
|
||||
DebugStrings[nextDebugString][1023] = 0;
|
||||
va_end( va );
|
||||
|
||||
char *tmp = DebugStrings[nextDebugString];
|
||||
while (tmp && *tmp)
|
||||
{
|
||||
if (*tmp == '\r' || *tmp == '\n')
|
||||
{
|
||||
*tmp = ' ';
|
||||
}
|
||||
++tmp;
|
||||
}
|
||||
|
||||
//DEBUG_LOG(("%s\n", DebugStrings[nextDebugString]));
|
||||
|
||||
++nextDebugString;
|
||||
++numDebugStrings;
|
||||
if (nextDebugString == MaxStrings)
|
||||
nextDebugString = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void addCRCGenLine(const char *fmt, ...)
|
||||
{
|
||||
if (dumped || !(IS_FRAME_OK_TO_LOG))
|
||||
return;
|
||||
|
||||
static char buf[1024];
|
||||
va_list va;
|
||||
va_start( va, fmt );
|
||||
_vsnprintf(buf, 1024, fmt, va );
|
||||
va_end( va );
|
||||
buf[1023] = 0;
|
||||
addCRCDebugLine("%s", buf);
|
||||
|
||||
//DEBUG_LOG(("%s", buf));
|
||||
}
|
||||
|
||||
void addCRCDumpLine(const char *fmt, ...)
|
||||
{
|
||||
/*
|
||||
va_list va;
|
||||
va_start( va, fmt );
|
||||
_vsnprintf(DumpStrings[nextDumpString], 1024, fmt, va );
|
||||
DumpStrings[nextDumpString][1023] = 0;
|
||||
va_end( va );
|
||||
|
||||
++nextDumpString;
|
||||
++numDumpStrings;
|
||||
if (nextDumpString == MaxStrings)
|
||||
nextDumpString = 0;
|
||||
*/
|
||||
}
|
||||
|
||||
void dumpVector3(const Vector3 *v, AsciiString name, AsciiString fname, Int line)
|
||||
{
|
||||
if (dumped)
|
||||
return;
|
||||
|
||||
if (!(IS_FRAME_OK_TO_LOG)) return;
|
||||
fname.toLower();
|
||||
fname = getFname(fname);
|
||||
addCRCDebugLine("dumpVector3() %s:%d %s %8.8X %8.8X %8.8X\n",
|
||||
fname.str(), line, name.str(),
|
||||
AS_INT(v->X), AS_INT(v->Y), AS_INT(v->Z));
|
||||
}
|
||||
|
||||
void dumpCoord3D(const Coord3D *c, AsciiString name, AsciiString fname, Int line)
|
||||
{
|
||||
if (dumped)
|
||||
return;
|
||||
|
||||
if (!(IS_FRAME_OK_TO_LOG)) return;
|
||||
fname.toLower();
|
||||
fname = getFname(fname);
|
||||
addCRCDebugLine("dumpCoord3D() %s:%d %s %8.8X %8.8X %8.8X\n",
|
||||
fname.str(), line, name.str(),
|
||||
AS_INT(c->x), AS_INT(c->y), AS_INT(c->z));
|
||||
}
|
||||
|
||||
void dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int line)
|
||||
{
|
||||
if (dumped)
|
||||
return;
|
||||
|
||||
if (!(IS_FRAME_OK_TO_LOG)) return;
|
||||
fname.toLower();
|
||||
fname = getFname(fname);
|
||||
const Real *matrix = (const Real *)m;
|
||||
addCRCDebugLine("dumpMatrix3D() %s:%d %s\n",
|
||||
fname.str(), line, name.str());
|
||||
for (Int i=0; i<3; ++i)
|
||||
addCRCDebugLine(" 0x%08X 0x%08X 0x%08X 0x%08X\n",
|
||||
AS_INT(matrix[(i<<2)+0]), AS_INT(matrix[(i<<2)+1]), AS_INT(matrix[(i<<2)+2]), AS_INT(matrix[(i<<2)+3]));
|
||||
}
|
||||
|
||||
void dumpReal(Real r, AsciiString name, AsciiString fname, Int line)
|
||||
{
|
||||
if (dumped)
|
||||
return;
|
||||
|
||||
if (!(IS_FRAME_OK_TO_LOG)) return;
|
||||
fname.toLower();
|
||||
fname = getFname(fname);
|
||||
addCRCDebugLine("dumpReal() %s:%d %s %8.8X (%f)\n",
|
||||
fname.str(), line, name.str(), AS_INT(r), r);
|
||||
}
|
||||
|
||||
#endif // DEBUG_CRC
|
||||
1244
Generals/Code/GameEngine/Source/Common/CommandLine.cpp
Normal file
1244
Generals/Code/GameEngine/Source/Common/CommandLine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
315
Generals/Code/GameEngine/Source/Common/DamageFX.cpp
Normal file
315
Generals/Code/GameEngine/Source/Common/DamageFX.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DamageFX.cpp ///////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, November 2001
|
||||
// Desc: DamageFX descriptions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
|
||||
|
||||
#define DEFINE_DAMAGE_NAMES // for DamageNames[]
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/DamageFX.h"
|
||||
#include "Common/GameAudio.h"
|
||||
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameLogic/Damage.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameClient/GameClient.h"
|
||||
#include "GameClient/InGameUI.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DamageFXStore *TheDamageFXStore = NULL; ///< the DamageFX store definition
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE CLASSES ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DamageFX::DamageFX()
|
||||
{
|
||||
// not necessary.
|
||||
//clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DamageFX::clear()
|
||||
{
|
||||
for (Int dt = 0; dt < DAMAGE_NUM_TYPES; ++dt)
|
||||
{
|
||||
for (Int v = LEVEL_FIRST; v <= LEVEL_LAST; ++v)
|
||||
{
|
||||
m_dfx[dt][v].clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
UnsignedInt DamageFX::getDamageFXThrottleTime(DamageType t, const Object* source) const
|
||||
{
|
||||
return m_dfx[t][source ? source->getVeterancyLevel() : LEVEL_REGULAR].m_damageFXThrottleTime;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DamageFX::doDamageFX(DamageType t, Real damageAmount, const Object* source, const Object* victim) const
|
||||
{
|
||||
ConstFXListPtr fx = getDamageFXList(t, damageAmount, source);
|
||||
// since the victim is receiving the damage, it's the "primary" object.
|
||||
// the source is the "secondary" object -- unused by most fx, but could be
|
||||
// useful in some cases.
|
||||
FXList::doFXObj(fx, victim, source);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
ConstFXListPtr DamageFX::getDamageFXList(DamageType t, Real damageAmount, const Object* source) const
|
||||
{
|
||||
/*
|
||||
if damage is zero, never do damage fx. this is by design, since "zero" damage can happen
|
||||
with some special weapons, like the battleship, which is a "faux" weapon that never does damage.
|
||||
if you really need to change this for some reason, consider carefully... (srj)
|
||||
*/
|
||||
if (damageAmount == 0.0f)
|
||||
return NULL;
|
||||
|
||||
const DFX& dfx = m_dfx[t][source ? source->getVeterancyLevel() : LEVEL_REGULAR];
|
||||
ConstFXListPtr fx =
|
||||
damageAmount >= dfx.m_amountForMajorFX ?
|
||||
dfx.m_majorDamageFXList :
|
||||
dfx.m_minorDamageFXList;
|
||||
|
||||
return fx;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const FieldParse* DamageFX::getFieldParse() const
|
||||
{
|
||||
static const FieldParse myFieldParse[] =
|
||||
{
|
||||
{ "AmountForMajorFX", parseAmount, NULL, 0 },
|
||||
{ "MajorFX", parseMajorFXList, NULL, 0 },
|
||||
{ "MinorFX", parseMinorFXList, NULL, 0 },
|
||||
{ "ThrottleTime", parseTime, NULL, 0 },
|
||||
{ "VeterancyAmountForMajorFX", parseAmount, TheVeterancyNames, 0 },
|
||||
{ "VeterancyMajorFX", parseMajorFXList, TheVeterancyNames, 0 },
|
||||
{ "VeterancyMinorFX", parseMinorFXList, TheVeterancyNames, 0 },
|
||||
{ "VeterancyThrottleTime", parseTime, TheVeterancyNames, 0 },
|
||||
{ 0, 0, 0,0 }
|
||||
};
|
||||
return myFieldParse;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------Static
|
||||
static void parseCommonStuff(
|
||||
INI* ini,
|
||||
ConstCharPtrArray names,
|
||||
VeterancyLevel& vetFirst,
|
||||
VeterancyLevel& vetLast,
|
||||
DamageType& damageFirst,
|
||||
DamageType& damageLast
|
||||
)
|
||||
{
|
||||
if (names)
|
||||
{
|
||||
vetFirst = (VeterancyLevel)INI::scanIndexList(ini->getNextToken(), names);
|
||||
vetLast = vetFirst;
|
||||
}
|
||||
else
|
||||
{
|
||||
vetFirst = LEVEL_FIRST;
|
||||
vetLast = LEVEL_LAST;
|
||||
}
|
||||
|
||||
const char* damageName = ini->getNextToken();
|
||||
if (stricmp(damageName, "Default") == 0)
|
||||
{
|
||||
damageFirst = (DamageType)0;
|
||||
damageLast = (DamageType)(DAMAGE_NUM_TYPES - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
damageFirst = (DamageType)INI::scanIndexList(damageName, TheDamageNames);
|
||||
damageLast = damageFirst;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------Static
|
||||
/*static*/ void DamageFX::parseAmount( INI* ini, void* instance, void* /*store*/, const void* userData )
|
||||
{
|
||||
DamageFX* self = (DamageFX*)instance;
|
||||
ConstCharPtrArray names = (ConstCharPtrArray)userData;
|
||||
|
||||
VeterancyLevel vetFirst, vetLast;
|
||||
DamageType damageFirst, damageLast;
|
||||
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
|
||||
|
||||
Real amt = INI::scanReal(ini->getNextToken());
|
||||
|
||||
for (Int dt = damageFirst; dt <= damageLast; ++dt)
|
||||
{
|
||||
for (Int v = vetFirst; v <= vetLast; ++v)
|
||||
{
|
||||
self->m_dfx[dt][v].m_amountForMajorFX = amt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------Static
|
||||
/*static*/ void DamageFX::parseMajorFXList( INI* ini, void* instance, void* /*store*/, const void* userData )
|
||||
{
|
||||
DamageFX* self = (DamageFX*)instance;
|
||||
ConstCharPtrArray names = (ConstCharPtrArray)userData;
|
||||
|
||||
VeterancyLevel vetFirst, vetLast;
|
||||
DamageType damageFirst, damageLast;
|
||||
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
|
||||
|
||||
ConstFXListPtr fx;
|
||||
INI::parseFXList(ini, NULL, &fx, NULL);
|
||||
|
||||
for (Int dt = damageFirst; dt <= damageLast; ++dt)
|
||||
{
|
||||
for (Int v = vetFirst; v <= vetLast; ++v)
|
||||
{
|
||||
self->m_dfx[dt][v].m_majorDamageFXList = fx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------Static
|
||||
/*static*/ void DamageFX::parseMinorFXList( INI* ini, void* instance, void* /*store*/, const void* userData )
|
||||
{
|
||||
DamageFX* self = (DamageFX*)instance;
|
||||
ConstCharPtrArray names = (ConstCharPtrArray)userData;
|
||||
|
||||
VeterancyLevel vetFirst, vetLast;
|
||||
DamageType damageFirst, damageLast;
|
||||
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
|
||||
|
||||
ConstFXListPtr fx;
|
||||
INI::parseFXList(ini, NULL, &fx, NULL);
|
||||
|
||||
for (Int dt = damageFirst; dt <= damageLast; ++dt)
|
||||
{
|
||||
for (Int v = vetFirst; v <= vetLast; ++v)
|
||||
{
|
||||
self->m_dfx[dt][v].m_minorDamageFXList = fx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------Static
|
||||
/*static*/ void DamageFX::parseTime( INI* ini, void* instance, void* /*store*/, const void* userData )
|
||||
{
|
||||
DamageFX* self = (DamageFX*)instance;
|
||||
ConstCharPtrArray names = (ConstCharPtrArray)userData;
|
||||
|
||||
VeterancyLevel vetFirst, vetLast;
|
||||
DamageType damageFirst, damageLast;
|
||||
parseCommonStuff(ini, names, vetFirst, vetLast, damageFirst, damageLast);
|
||||
|
||||
UnsignedInt t;
|
||||
INI::parseDurationUnsignedInt(ini, NULL, &t, NULL);
|
||||
|
||||
for (Int dt = damageFirst; dt <= damageLast; ++dt)
|
||||
{
|
||||
for (Int v = vetFirst; v <= vetLast; ++v)
|
||||
{
|
||||
self->m_dfx[dt][v].m_damageFXThrottleTime = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DamageFXStore::DamageFXStore()
|
||||
{
|
||||
m_dfxmap.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DamageFXStore::~DamageFXStore()
|
||||
{
|
||||
m_dfxmap.clear();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const DamageFX *DamageFXStore::findDamageFX(AsciiString name) const
|
||||
{
|
||||
NameKeyType namekey = TheNameKeyGenerator->nameToKey(name);
|
||||
DamageFXMap::const_iterator it = m_dfxmap.find(namekey);
|
||||
if (it == m_dfxmap.end())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &(*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DamageFXStore::init()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DamageFXStore::reset()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DamageFXStore::update()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static */ void DamageFXStore::parseDamageFXDefinition(INI* ini)
|
||||
{
|
||||
|
||||
const char *c = ini->getNextToken();
|
||||
NameKeyType key = TheNameKeyGenerator->nameToKey(c);
|
||||
DamageFX& dfx = TheDamageFXStore->m_dfxmap[key];
|
||||
dfx.clear();
|
||||
ini->initFromINI(&dfx, dfx.getFieldParse());
|
||||
}
|
||||
550
Generals/Code/GameEngine/Source/Common/Dict.cpp
Normal file
550
Generals/Code/GameEngine/Source/Common/Dict.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Dict.cpp
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: Dict.cpp
|
||||
//
|
||||
// Created: Steven Johnson, November 2001
|
||||
//
|
||||
// Desc: General-purpose dictionary class
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
|
||||
|
||||
#include "Common/Dict.h"
|
||||
#include "Common/GameMemory.h"
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::DictPair::copyFrom(DictPair* that)
|
||||
{
|
||||
Dict::DataType curType = this->getType();
|
||||
Dict::DataType newType = that->getType();
|
||||
if (curType != newType)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
switch(newType)
|
||||
{
|
||||
case DICT_BOOL:
|
||||
case DICT_INT:
|
||||
case DICT_REAL:
|
||||
*this = *that;
|
||||
break;
|
||||
case DICT_ASCIISTRING:
|
||||
this->m_key = that->m_key;
|
||||
*this->asAsciiString() = *that->asAsciiString();
|
||||
break;
|
||||
case DICT_UNICODESTRING:
|
||||
this->m_key = that->m_key;
|
||||
*this->asUnicodeString() = *that->asUnicodeString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::DictPair::clear()
|
||||
{
|
||||
switch (getType())
|
||||
{
|
||||
case DICT_BOOL:
|
||||
case DICT_INT:
|
||||
case DICT_REAL:
|
||||
m_value = 0;
|
||||
break;
|
||||
case DICT_ASCIISTRING:
|
||||
asAsciiString()->clear();
|
||||
break;
|
||||
case DICT_UNICODESTRING:
|
||||
asUnicodeString()->clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::DictPair::setNameAndType(NameKeyType key, Dict::DataType type)
|
||||
{
|
||||
Dict::DataType curType = getType();
|
||||
if (curType != type)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
m_key = createKey(key, type);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
#ifdef _DEBUG
|
||||
void Dict::validate() const
|
||||
{
|
||||
if (!m_data) return;
|
||||
DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
|
||||
DEBUG_ASSERTCRASH(m_data->m_refCount < 32000, ("m_refCount is suspiciously large"));
|
||||
DEBUG_ASSERTCRASH(m_data->m_numPairsAllocated > 0, ("m_numPairsAllocated is zero"));
|
||||
DEBUG_ASSERTCRASH(m_data->m_numPairsUsed >= 0, ("m_numPairsUsed is neg"));
|
||||
DEBUG_ASSERTCRASH(m_data->m_numPairsAllocated >= m_data->m_numPairsUsed, ("m_numPairsAllocated too small"));
|
||||
DEBUG_ASSERTCRASH(m_data->m_numPairsAllocated < 1024, ("m_numPairsAllocated suspiciously large"));
|
||||
}
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------
|
||||
Dict::DictPair* Dict::findPairByKey(NameKeyType key) const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(key != NAMEKEY_INVALID, ("invalid namekey!"));
|
||||
DEBUG_ASSERTCRASH((UnsignedInt)key < (1L<<23), ("namekey too large!"));
|
||||
if (!m_data)
|
||||
return NULL;
|
||||
DictPair* base = m_data->peek();
|
||||
Int minIdx = 0;
|
||||
Int maxIdx = m_data->m_numPairsUsed;
|
||||
while (minIdx < maxIdx)
|
||||
{
|
||||
Int midIdx = (((minIdx + maxIdx) - 1) >> 1);
|
||||
DictPair* mid = base + midIdx;
|
||||
NameKeyType midKey = mid->getName();
|
||||
if (key > midKey)
|
||||
minIdx = midIdx + 1;
|
||||
else if (key < midKey)
|
||||
maxIdx = midIdx;
|
||||
else
|
||||
return mid;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Dict::DictPair *Dict::ensureUnique(int numPairsNeeded, Bool preserveData, DictPair *pairToTranslate)
|
||||
{
|
||||
if (m_data &&
|
||||
m_data->m_refCount == 1 &&
|
||||
m_data->m_numPairsAllocated >= numPairsNeeded)
|
||||
{
|
||||
// no buffer manhandling is needed (it's already large enough, and unique to us)
|
||||
return pairToTranslate;
|
||||
}
|
||||
|
||||
if (numPairsNeeded > MAX_LEN)
|
||||
throw ERROR_OUT_OF_MEMORY;
|
||||
|
||||
Dict::DictPairData* newData = NULL;
|
||||
if (numPairsNeeded > 0)
|
||||
{
|
||||
int minBytes = sizeof(Dict::DictPairData) + numPairsNeeded*sizeof(Dict::DictPair);
|
||||
int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
|
||||
// note: be certain to alloc with zero; we'll take advantage of the fact that all-zero
|
||||
// is a bit-pattern that happens to init all our pairs to legal values:
|
||||
// type BOOL, key INVALID, value FALSE.
|
||||
newData = (Dict::DictPairData*)TheDynamicMemoryAllocator->allocateBytes(actualBytes, "Dict::ensureUnique");
|
||||
newData->m_refCount = 1;
|
||||
newData->m_numPairsAllocated = (actualBytes - sizeof(Dict::DictPairData))/sizeof(Dict::DictPair);
|
||||
newData->m_numPairsUsed = 0;
|
||||
|
||||
if (preserveData && m_data)
|
||||
{
|
||||
Dict::DictPair* src = m_data->peek();
|
||||
Dict::DictPair* dst = newData->peek();
|
||||
for (Int i = 0; i < m_data->m_numPairsUsed; ++i, ++src, ++dst)
|
||||
dst->copyFrom(src);
|
||||
newData->m_numPairsUsed = m_data->m_numPairsUsed;
|
||||
}
|
||||
}
|
||||
|
||||
Int delta;
|
||||
if (pairToTranslate && m_data)
|
||||
delta = pairToTranslate - m_data->peek();
|
||||
|
||||
releaseData();
|
||||
m_data = newData;
|
||||
|
||||
if (pairToTranslate && m_data)
|
||||
pairToTranslate = m_data->peek() + delta;
|
||||
|
||||
return pairToTranslate;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::clear()
|
||||
{
|
||||
releaseData();
|
||||
m_data = NULL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::releaseData()
|
||||
{
|
||||
if (m_data)
|
||||
{
|
||||
if (--m_data->m_refCount == 0)
|
||||
{
|
||||
Dict::DictPair* src = m_data->peek();
|
||||
for (Int i = 0; i < m_data->m_numPairsUsed; ++i, ++src)
|
||||
src->clear();
|
||||
TheDynamicMemoryAllocator->freeBytes(m_data);
|
||||
}
|
||||
m_data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Dict::Dict(Int numPairsToPreAllocate) : m_data(0)
|
||||
{
|
||||
|
||||
/*
|
||||
This class plays some skanky games, in the name of memory and code
|
||||
efficiency; it assumes all the data types will fit into a pointer.
|
||||
This is currently true, but if that assumption ever changes, all hell
|
||||
will break loose. So we do a quick check to assure this...
|
||||
*/
|
||||
DEBUG_ASSERTCRASH(sizeof(Bool) <= sizeof(void*) &&
|
||||
sizeof(Int) <= sizeof(void*) &&
|
||||
sizeof(Real) <= sizeof(void*) &&
|
||||
sizeof(AsciiString) <= sizeof(void*) &&
|
||||
sizeof(UnicodeString) <= sizeof(void*), ("oops, this code needs attention"));
|
||||
|
||||
if (numPairsToPreAllocate)
|
||||
ensureUnique(numPairsToPreAllocate, false, NULL); // will throw on error
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Dict& Dict::operator=(const Dict& src)
|
||||
{
|
||||
validate();
|
||||
if (&src != this)
|
||||
{
|
||||
releaseData();
|
||||
m_data = src.m_data;
|
||||
if (m_data)
|
||||
++m_data->m_refCount;
|
||||
}
|
||||
validate();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Dict::DataType Dict::getType(NameKeyType key) const
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = findPairByKey(key);
|
||||
if (pair)
|
||||
return pair->getType();
|
||||
return DICT_NONE;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Bool Dict::getBool(NameKeyType key, Bool *exists/*=NULL*/) const
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = findPairByKey(key);
|
||||
if (pair && pair->getType() == DICT_BOOL)
|
||||
{
|
||||
if (exists) *exists = true;
|
||||
return *pair->asBool();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(exists != NULL, ("dict key missing, or of wrong type\n")); // only assert if they didn't check result
|
||||
if (exists) *exists = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Int Dict::getInt(NameKeyType key, Bool *exists/*=NULL*/) const
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = findPairByKey(key);
|
||||
if (pair && pair->getType() == DICT_INT)
|
||||
{
|
||||
if (exists) *exists = true;
|
||||
return *pair->asInt();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
|
||||
if (exists) *exists = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Real Dict::getReal(NameKeyType key, Bool *exists/*=NULL*/) const
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = findPairByKey(key);
|
||||
if (pair && pair->getType() == DICT_REAL)
|
||||
{
|
||||
if (exists) *exists = true;
|
||||
return *pair->asReal();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
|
||||
if (exists) *exists = false;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
AsciiString Dict::getAsciiString(NameKeyType key, Bool *exists/*=NULL*/) const
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = findPairByKey(key);
|
||||
if (pair && pair->getType() == DICT_ASCIISTRING)
|
||||
{
|
||||
if (exists) *exists = true;
|
||||
return *pair->asAsciiString();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
|
||||
if (exists) *exists = false;
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
UnicodeString Dict::getUnicodeString(NameKeyType key, Bool *exists/*=NULL*/) const
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = findPairByKey(key);
|
||||
if (pair && pair->getType() == DICT_UNICODESTRING)
|
||||
{
|
||||
if (exists) *exists = true;
|
||||
return *pair->asUnicodeString();
|
||||
}
|
||||
DEBUG_ASSERTCRASH(exists != NULL,("dict key missing, or of wrong type\n")); // only assert if they didn't check result
|
||||
if (exists) *exists = false;
|
||||
return UnicodeString::TheEmptyString;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Bool Dict::getNthBool(Int n) const
|
||||
{
|
||||
validate();
|
||||
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
|
||||
if (m_data)
|
||||
{
|
||||
DictPair* pair = &m_data->peek()[n];
|
||||
if (pair && pair->getType() == DICT_BOOL)
|
||||
return *pair->asBool();
|
||||
}
|
||||
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Int Dict::getNthInt(Int n) const
|
||||
{
|
||||
validate();
|
||||
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
|
||||
if (m_data)
|
||||
{
|
||||
DictPair* pair = &m_data->peek()[n];
|
||||
if (pair && pair->getType() == DICT_INT)
|
||||
return *pair->asInt();
|
||||
}
|
||||
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Real Dict::getNthReal(Int n) const
|
||||
{
|
||||
validate();
|
||||
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
|
||||
if (m_data)
|
||||
{
|
||||
DictPair* pair = &m_data->peek()[n];
|
||||
if (pair && pair->getType() == DICT_REAL)
|
||||
return *pair->asReal();
|
||||
}
|
||||
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
AsciiString Dict::getNthAsciiString(Int n) const
|
||||
{
|
||||
validate();
|
||||
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
|
||||
if (m_data)
|
||||
{
|
||||
DictPair* pair = &m_data->peek()[n];
|
||||
if (pair && pair->getType() == DICT_ASCIISTRING)
|
||||
return *pair->asAsciiString();
|
||||
}
|
||||
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
UnicodeString Dict::getNthUnicodeString(Int n) const
|
||||
{
|
||||
validate();
|
||||
DEBUG_ASSERTCRASH(n >= 0 && n < getPairCount(), ("n out of range\n"));
|
||||
if (m_data)
|
||||
{
|
||||
DictPair* pair = &m_data->peek()[n];
|
||||
if (pair && pair->getType() == DICT_UNICODESTRING)
|
||||
return *pair->asUnicodeString();
|
||||
}
|
||||
DEBUG_CRASH(("dict key missing, or of wrong type\n"));
|
||||
return UnicodeString::TheEmptyString;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Dict::DictPair *Dict::setPrep(NameKeyType key, Dict::DataType type)
|
||||
{
|
||||
DictPair* pair = findPairByKey(key);
|
||||
Int pairsNeeded = getPairCount();
|
||||
if (!pair)
|
||||
++pairsNeeded;
|
||||
pair = ensureUnique(pairsNeeded, true, pair);
|
||||
if (!pair)
|
||||
{
|
||||
pair = &m_data->peek()[m_data->m_numPairsUsed++];
|
||||
}
|
||||
pair->setNameAndType(key, type);
|
||||
DEBUG_ASSERTCRASH(pair, ("pair must not be null here"));
|
||||
return pair;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::sortPairs()
|
||||
{
|
||||
if (!m_data)
|
||||
return;
|
||||
|
||||
// yer basic shellsort.
|
||||
for (Int gap = m_data->m_numPairsUsed >> 1; gap > 0; gap >>= 1)
|
||||
{
|
||||
for (Int i = gap; i < m_data->m_numPairsUsed; i++)
|
||||
{
|
||||
for (Int j = i - gap; j >= 0; j -= gap)
|
||||
{
|
||||
DictPair* a = m_data->peek() + j;
|
||||
DictPair* b = m_data->peek() + j + gap;
|
||||
if (a->getName() > b->getName())
|
||||
{
|
||||
DictPair tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::setBool(NameKeyType key, Bool value)
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = setPrep(key, DICT_BOOL);
|
||||
*pair->asBool() = value;
|
||||
sortPairs();
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::setInt(NameKeyType key, Int value)
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = setPrep(key, DICT_INT);
|
||||
*pair->asInt() = value;
|
||||
sortPairs();
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::setReal(NameKeyType key, Real value)
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = setPrep(key, DICT_REAL);
|
||||
*pair->asReal() = value;
|
||||
sortPairs();
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::setAsciiString(NameKeyType key, const AsciiString& value)
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = setPrep(key, DICT_ASCIISTRING);
|
||||
*pair->asAsciiString() = value;
|
||||
sortPairs();
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::setUnicodeString(NameKeyType key, const UnicodeString& value)
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = setPrep(key, DICT_UNICODESTRING);
|
||||
*pair->asUnicodeString() = value;
|
||||
sortPairs();
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Bool Dict::remove(NameKeyType key)
|
||||
{
|
||||
validate();
|
||||
DictPair* pair = findPairByKey(key);
|
||||
if (pair)
|
||||
{
|
||||
pair = ensureUnique(m_data->m_numPairsUsed, true, pair);
|
||||
pair->setNameAndType((NameKeyType)0x7fffffff, DICT_BOOL);
|
||||
sortPairs();
|
||||
--m_data->m_numPairsUsed;
|
||||
validate();
|
||||
return true;
|
||||
}
|
||||
DEBUG_CRASH(("dict key missing in remove\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void Dict::copyPairFrom(const Dict& that, NameKeyType key)
|
||||
{
|
||||
this->validate();
|
||||
DictPair* thatPair = that.findPairByKey(key);
|
||||
if (thatPair)
|
||||
{
|
||||
DictPair* thisPair = this->setPrep(key, thatPair->getType());
|
||||
thisPair->copyFrom(thatPair);
|
||||
this->sortPairs();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->findPairByKey(key))
|
||||
this->remove(key);
|
||||
}
|
||||
this->validate();
|
||||
}
|
||||
114
Generals/Code/GameEngine/Source/Common/DiscreteCircle.cpp
Normal file
114
Generals/Code/GameEngine/Source/Common/DiscreteCircle.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: DiscreteCircle.cpp ////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// EA Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: DiscreteCircle.cpp
|
||||
//
|
||||
// Created: John McDonald, September 2002
|
||||
//
|
||||
// Desc: ???
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "Common/DiscreteCircle.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
DiscreteCircle::DiscreteCircle(Int xCenter, Int yCenter, Int radius)
|
||||
{
|
||||
m_yPos = yCenter;
|
||||
m_yPosDoubled = (yCenter << 1);
|
||||
m_edges.reserve(radius << 1); // largest that it should ever be.
|
||||
|
||||
generateEdgePairs(xCenter, yCenter, radius);
|
||||
removeDuplicates();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DiscreteCircle::drawCircle(ScanlineDrawFunc functionToDrawWith, void *parmToPass)
|
||||
{
|
||||
for (VecHorzLine::const_iterator it = m_edges.begin(); it != m_edges.end(); ++it) {
|
||||
(functionToDrawWith)(it->xStart, it->xEnd, it->yPos, parmToPass);
|
||||
if (it->yPos != m_yPos) {
|
||||
(functionToDrawWith)(it->xStart, it->xEnd, m_yPosDoubled - it->yPos, parmToPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DiscreteCircle::generateEdgePairs(Int xCenter, Int yCenter, Int radius)
|
||||
{
|
||||
// Uses Bresenham to generate points.
|
||||
Int x = 0;
|
||||
Int y = radius;
|
||||
Int d = (1 - radius) << 1;
|
||||
|
||||
while (y >= 0) {
|
||||
HorzLine hl;
|
||||
hl.xStart = xCenter - x;
|
||||
hl.xEnd = xCenter + x;
|
||||
hl.yPos = yCenter + y;
|
||||
m_edges.push_back(hl);
|
||||
|
||||
if (d + y > 0) {
|
||||
--y;
|
||||
d -= ((y << 1) - 1);
|
||||
}
|
||||
|
||||
if (x > d) {
|
||||
++x;
|
||||
d += ((x << 1) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void DiscreteCircle::removeDuplicates()
|
||||
{
|
||||
VecHorzLineIt it, nextIt;
|
||||
for ( it = m_edges.begin(); it != m_edges.end(); /* empty */) {
|
||||
nextIt = it;
|
||||
++nextIt;
|
||||
if (nextIt == m_edges.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (it->yPos == nextIt->yPos) {
|
||||
it = m_edges.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
933
Generals/Code/GameEngine/Source/Common/GameEngine.cpp
Normal file
933
Generals/Code/GameEngine/Source/Common/GameEngine.cpp
Normal file
@@ -0,0 +1,933 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GameEngine.cpp /////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of the Game Engine singleton
|
||||
// Author: Michael S. Booth, April 2001
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
|
||||
|
||||
#include "Common/ActionManager.h"
|
||||
#include "Common/AudioAffect.h"
|
||||
#include "Common/BuildAssistant.h"
|
||||
#include "Common/CRCDebug.h"
|
||||
#include "Common/Radar.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/Team.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/GameEngine.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/INIException.h"
|
||||
#include "Common/MessageStream.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "Common/ArchiveFileSystem.h"
|
||||
#include "Common/LocalFileSystem.h"
|
||||
#include "Common/CDManager.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/PerfTimer.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/NameKeyGenerator.h"
|
||||
#include "Common/ModuleFactory.h"
|
||||
#include "Common/Debug.h"
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/GameStateMap.h"
|
||||
#include "Common/Science.h"
|
||||
#include "Common/FunctionLexicon.h"
|
||||
#include "Common/CommandLine.h"
|
||||
#include "Common/DamageFX.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "Common/Recorder.h"
|
||||
#include "Common/SpecialPower.h"
|
||||
#include "Common/TerrainTypes.h"
|
||||
#include "Common/Upgrade.h"
|
||||
#include "Common/UserPreferences.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "Common/XferCRC.h"
|
||||
#include "Common/GameLOD.h"
|
||||
#include "Common/Registry.h"
|
||||
|
||||
#include "GameLogic/Armor.h"
|
||||
#include "GameLogic/AI.h"
|
||||
#include "GameLogic/CaveSystem.h"
|
||||
#include "GameLogic/CrateSystem.h"
|
||||
#include "GameLogic/VictoryConditions.h"
|
||||
#include "GameLogic/ObjectCreationList.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Locomotor.h"
|
||||
#include "GameLogic/RankInfo.h"
|
||||
#include "GameLogic/ScriptEngine.h"
|
||||
#include "GameLogic/SidesList.h"
|
||||
|
||||
#include "GameClient/Display.h"
|
||||
#include "GameClient/FXList.h"
|
||||
#include "GameClient/GameClient.h"
|
||||
#include "GameClient/Keyboard.h"
|
||||
#include "GameClient/Shell.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/ParticleSys.h"
|
||||
#include "GameClient/Water.h"
|
||||
#include "GameClient/TerrainRoads.h"
|
||||
#include "GameClient/MetaEvent.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameClient/GameWindowManager.h"
|
||||
#include "GameClient/GlobalLanguage.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/GUICallbacks.h"
|
||||
|
||||
#include "GameNetwork/NetworkInterface.h"
|
||||
#include "GameNetwork/WOLBrowser/WebBrowser.h"
|
||||
#include "GameNetwork/LANAPI.h"
|
||||
#include "GameNetwork/GameSpy/GameResultsThread.h"
|
||||
#include "GameNetwork/GameSpy/PeerDefs.h"
|
||||
#include "GameNetwork/GameSpy/PersistentStorageThread.h"
|
||||
#include "Common/Player.h"
|
||||
|
||||
|
||||
#include "Common/Version.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifdef DEBUG_CRC
|
||||
class DeepCRCSanityCheck : public SubsystemInterface
|
||||
{
|
||||
public:
|
||||
DeepCRCSanityCheck() {}
|
||||
virtual ~DeepCRCSanityCheck() {}
|
||||
|
||||
virtual void init(void) {}
|
||||
virtual void reset(void);
|
||||
virtual void update(void) {}
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
DeepCRCSanityCheck *TheDeepCRCSanityCheck = NULL;
|
||||
|
||||
void DeepCRCSanityCheck::reset(void)
|
||||
{
|
||||
static Int timesThrough = 0;
|
||||
static UnsignedInt lastCRC = 0;
|
||||
|
||||
AsciiString fname;
|
||||
fname.format("%sCRCAfter%dMaps.dat", TheGlobalData->getPath_UserData().str(), timesThrough);
|
||||
UnsignedInt thisCRC = TheGameLogic->getCRC( CRC_RECALC, fname );
|
||||
|
||||
DEBUG_LOG(("DeepCRCSanityCheck: CRC is %X\n", thisCRC));
|
||||
DEBUG_ASSERTCRASH(timesThrough == 0 || thisCRC == lastCRC,
|
||||
("CRC after reset did not match beginning CRC!\nNetwork games won't work after this.\nOld: 0x%8.8X, New: 0x%8.8X",
|
||||
lastCRC, thisCRC));
|
||||
lastCRC = thisCRC;
|
||||
|
||||
timesThrough++;
|
||||
}
|
||||
#endif // DEBUG_CRC
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/// The GameEngine singleton instance
|
||||
GameEngine *TheGameEngine = NULL;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SubsystemInterfaceList* TheSubsystemList = NULL;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
template<class SUBSYSTEM>
|
||||
void initSubsystem(SUBSYSTEM*& sysref, AsciiString name, SUBSYSTEM* sys, Xfer *pXfer, const char* path1 = NULL,
|
||||
const char* path2 = NULL, const char* dirpath = NULL)
|
||||
{
|
||||
sysref = sys;
|
||||
TheSubsystemList->initSubsystem(sys, path1, path2, dirpath, pXfer, name);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
extern HINSTANCE ApplicationHInstance; ///< our application instance
|
||||
extern CComModule _Module;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void updateTGAtoDDS();
|
||||
|
||||
Int GameEngine::getFramesPerSecondLimit( void )
|
||||
{
|
||||
return m_maxFPS;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GameEngine::GameEngine( void )
|
||||
{
|
||||
// Set the time slice size to 1 ms.
|
||||
timeBeginPeriod(1);
|
||||
|
||||
// initialize to non garbage values
|
||||
m_maxFPS = 0;
|
||||
m_quitting = FALSE;
|
||||
m_isActive = FALSE;
|
||||
|
||||
_Module.Init(NULL, ApplicationHInstance);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
GameEngine::~GameEngine()
|
||||
{
|
||||
//extern std::vector<std::string> preloadTextureNamesGlobalHack;
|
||||
//preloadTextureNamesGlobalHack.clear();
|
||||
|
||||
delete TheMapCache;
|
||||
TheMapCache = NULL;
|
||||
|
||||
// delete TheShell;
|
||||
// TheShell = NULL;
|
||||
|
||||
TheGameResultsQueue->endThreads();
|
||||
|
||||
TheSubsystemList->shutdownAll();
|
||||
delete TheSubsystemList;
|
||||
TheSubsystemList = NULL;
|
||||
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
|
||||
delete TheCommandList;
|
||||
TheCommandList = NULL;
|
||||
|
||||
delete TheNameKeyGenerator;
|
||||
TheNameKeyGenerator = NULL;
|
||||
|
||||
delete TheFileSystem;
|
||||
TheFileSystem = NULL;
|
||||
|
||||
Drawable::killStaticImages();
|
||||
|
||||
_Module.Term();
|
||||
|
||||
#ifdef PERF_TIMERS
|
||||
PerfGather::termPerfDump();
|
||||
#endif
|
||||
|
||||
// Restore the previous time slice for Windows.
|
||||
timeEndPeriod(1);
|
||||
}
|
||||
|
||||
void GameEngine::setFramesPerSecondLimit( Int fps )
|
||||
{
|
||||
DEBUG_LOG(("GameEngine::setFramesPerSecondLimit() - setting max fps to %d (TheGlobalData->m_useFpsLimit == %d)\n", fps, TheGlobalData->m_useFpsLimit));
|
||||
m_maxFPS = fps;
|
||||
}
|
||||
|
||||
/** -----------------------------------------------------------------------------------------------
|
||||
* Initialize the game engine by initializing the GameLogic and GameClient.
|
||||
*/
|
||||
void GameEngine::init( void ) {} /// @todo: I changed this to take argc & argv so we can parse those after the GDF is loaded. We need to rethink this immediately as it is a nasty hack
|
||||
void GameEngine::init( int argc, char *argv[] )
|
||||
{
|
||||
try {
|
||||
//create an INI object to use for loading stuff
|
||||
INI ini;
|
||||
|
||||
if (TheVersion)
|
||||
{
|
||||
DEBUG_LOG(("================================================================================\n"));
|
||||
#ifdef DEBUG_LOGGING
|
||||
#if defined _DEBUG
|
||||
const char *buildType = "Debug";
|
||||
#elif defined _INTERNAL
|
||||
const char *buildType = "Internal";
|
||||
#else
|
||||
// const char *buildType = "Release";
|
||||
#endif
|
||||
#endif // DEBUG_LOGGING
|
||||
DEBUG_LOG(("Generals version %s (%s)\n", TheVersion->getAsciiVersion().str(), buildType));
|
||||
DEBUG_LOG(("Build date: %s\n", TheVersion->getAsciiBuildTime().str()));
|
||||
DEBUG_LOG(("Build location: %s\n", TheVersion->getAsciiBuildLocation().str()));
|
||||
DEBUG_LOG(("Built by: %s\n", TheVersion->getAsciiBuildUser().str()));
|
||||
DEBUG_LOG(("================================================================================\n"));
|
||||
}
|
||||
|
||||
m_maxFPS = DEFAULT_MAX_FPS;
|
||||
|
||||
TheSubsystemList = MSGNEW("GameEngineSubsystem") SubsystemInterfaceList;
|
||||
|
||||
TheSubsystemList->addSubsystem(this);
|
||||
|
||||
// initialize the random number system
|
||||
InitRandom();
|
||||
|
||||
// Create the low-level file system interface
|
||||
TheFileSystem = createFileSystem();
|
||||
|
||||
// not part of the subsystem list, because it should normally never be reset!
|
||||
TheNameKeyGenerator = MSGNEW("GameEngineSubsystem") NameKeyGenerator;
|
||||
TheNameKeyGenerator->init();
|
||||
|
||||
// not part of the subsystem list, because it should normally never be reset!
|
||||
TheCommandList = MSGNEW("GameEngineSubsystem") CommandList;
|
||||
TheCommandList->init();
|
||||
|
||||
XferCRC xferCRC;
|
||||
xferCRC.open("lightCRC");
|
||||
|
||||
initSubsystem(TheLocalFileSystem, "TheLocalFileSystem", createLocalFileSystem(), NULL);
|
||||
initSubsystem(TheArchiveFileSystem, "TheArchiveFileSystem", createArchiveFileSystem(), NULL); // this MUST come after TheLocalFileSystem creation
|
||||
initSubsystem(TheWritableGlobalData, "TheWritableGlobalData", MSGNEW("GameEngineSubsystem") GlobalData(), &xferCRC, "Data\\INI\\Default\\GameData.ini", "Data\\INI\\GameData.ini");
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// If we're in Debug or Internal, load the Debug info as well.
|
||||
ini.load( AsciiString( "Data\\INI\\GameDataDebug.ini" ), INI_LOAD_OVERWRITE, NULL );
|
||||
#endif
|
||||
|
||||
// special-case: parse command-line parameters after loading global data
|
||||
parseCommandLine(argc, argv);
|
||||
|
||||
// doesn't require resets so just create a single instance here.
|
||||
TheGameLODManager = MSGNEW("GameEngineSubsystem") GameLODManager;
|
||||
TheGameLODManager->init();
|
||||
|
||||
// after parsing the command line, we may want to perform dds stuff. Do that here.
|
||||
if (TheGlobalData->m_shouldUpdateTGAToDDS) {
|
||||
// update any out of date targas here.
|
||||
updateTGAtoDDS();
|
||||
}
|
||||
|
||||
#if defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
|
||||
DEBUG_LOG(("Calculating CPU frequency for performance timers.\n"));
|
||||
InitPrecisionTimer();
|
||||
#endif
|
||||
#ifdef PERF_TIMERS
|
||||
PerfGather::initPerfDump("AAAPerfStats", PerfGather::PERF_NETTIME);
|
||||
#endif
|
||||
|
||||
// read the water settings from INI (must do prior to initing GameClient, apparently)
|
||||
ini.load( AsciiString( "Data\\INI\\Default\\Water.ini" ), INI_LOAD_OVERWRITE, &xferCRC );
|
||||
ini.load( AsciiString( "Data\\INI\\Water.ini" ), INI_LOAD_OVERWRITE, &xferCRC );
|
||||
|
||||
#ifdef DEBUG_CRC
|
||||
initSubsystem(TheDeepCRCSanityCheck, "TheDeepCRCSanityCheck", MSGNEW("GameEngineSubystem") DeepCRCSanityCheck, NULL, NULL, NULL, NULL);
|
||||
#endif // DEBUG_CRC
|
||||
initSubsystem(TheGameText, "TheGameText", CreateGameTextInterface(), NULL);
|
||||
initSubsystem(TheScienceStore,"TheScienceStore", MSGNEW("GameEngineSubsystem") ScienceStore(), &xferCRC, "Data\\INI\\Default\\Science.ini", "Data\\INI\\Science.ini");
|
||||
initSubsystem(TheMultiplayerSettings,"TheMultiplayerSettings", MSGNEW("GameEngineSubsystem") MultiplayerSettings(), &xferCRC, "Data\\INI\\Default\\Multiplayer.ini", "Data\\INI\\Multiplayer.ini");
|
||||
initSubsystem(TheTerrainTypes,"TheTerrainTypes", MSGNEW("GameEngineSubsystem") TerrainTypeCollection(), &xferCRC, "Data\\INI\\Default\\Terrain.ini", "Data\\INI\\Terrain.ini");
|
||||
initSubsystem(TheTerrainRoads,"TheTerrainRoads", MSGNEW("GameEngineSubsystem") TerrainRoadCollection(), &xferCRC, "Data\\INI\\Default\\Roads.ini", "Data\\INI\\Roads.ini");
|
||||
initSubsystem(TheGlobalLanguageData,"TheGlobalLanguageData",MSGNEW("GameEngineSubsystem") GlobalLanguage, NULL); // must be before the game text
|
||||
initSubsystem(TheCDManager,"TheCDManager", CreateCDManager(), NULL);
|
||||
initSubsystem(TheAudio,"TheAudio", createAudioManager(), NULL);
|
||||
if (!TheAudio->isMusicAlreadyLoaded())
|
||||
setQuitting(TRUE);
|
||||
initSubsystem(TheFunctionLexicon,"TheFunctionLexicon", createFunctionLexicon(), NULL);
|
||||
initSubsystem(TheModuleFactory,"TheModuleFactory", createModuleFactory(), NULL);
|
||||
initSubsystem(TheMessageStream,"TheMessageStream", createMessageStream(), NULL);
|
||||
initSubsystem(TheSidesList,"TheSidesList", MSGNEW("GameEngineSubsystem") SidesList(), NULL);
|
||||
initSubsystem(TheCaveSystem,"TheCaveSystem", MSGNEW("GameEngineSubsystem") CaveSystem(), NULL);
|
||||
initSubsystem(TheRankInfoStore,"TheRankInfoStore", MSGNEW("GameEngineSubsystem") RankInfoStore(), &xferCRC, NULL, "Data\\INI\\Rank.ini");
|
||||
initSubsystem(ThePlayerTemplateStore,"ThePlayerTemplateStore", MSGNEW("GameEngineSubsystem") PlayerTemplateStore(), &xferCRC, "Data\\INI\\Default\\PlayerTemplate.ini", "Data\\INI\\PlayerTemplate.ini");
|
||||
initSubsystem(TheParticleSystemManager,"TheParticleSystemManager", createParticleSystemManager(), NULL);
|
||||
initSubsystem(TheFXListStore,"TheFXListStore", MSGNEW("GameEngineSubsystem") FXListStore(), &xferCRC, "Data\\INI\\Default\\FXList.ini", "Data\\INI\\FXList.ini");
|
||||
initSubsystem(TheWeaponStore,"TheWeaponStore", MSGNEW("GameEngineSubsystem") WeaponStore(), &xferCRC, NULL, "Data\\INI\\Weapon.ini");
|
||||
initSubsystem(TheObjectCreationListStore,"TheObjectCreationListStore", MSGNEW("GameEngineSubsystem") ObjectCreationListStore(), &xferCRC, "Data\\INI\\Default\\ObjectCreationList.ini", "Data\\INI\\ObjectCreationList.ini");
|
||||
initSubsystem(TheLocomotorStore,"TheLocomotorStore", MSGNEW("GameEngineSubsystem") LocomotorStore(), &xferCRC, NULL, "Data\\INI\\Locomotor.ini");
|
||||
initSubsystem(TheSpecialPowerStore,"TheSpecialPowerStore", MSGNEW("GameEngineSubsystem") SpecialPowerStore(), &xferCRC, "Data\\INI\\Default\\SpecialPower.ini", "Data\\INI\\SpecialPower.ini");
|
||||
initSubsystem(TheDamageFXStore,"TheDamageFXStore", MSGNEW("GameEngineSubsystem") DamageFXStore(), &xferCRC, NULL, "Data\\INI\\DamageFX.ini");
|
||||
initSubsystem(TheArmorStore,"TheArmorStore", MSGNEW("GameEngineSubsystem") ArmorStore(), &xferCRC, NULL, "Data\\INI\\Armor.ini");
|
||||
initSubsystem(TheBuildAssistant,"TheBuildAssistant", MSGNEW("GameEngineSubsystem") BuildAssistant, NULL);
|
||||
initSubsystem(TheThingFactory,"TheThingFactory", createThingFactory(), &xferCRC, "Data\\INI\\Default\\Object.ini", NULL, "Data\\INI\\Object");
|
||||
initSubsystem(TheUpgradeCenter,"TheUpgradeCenter", MSGNEW("GameEngineSubsystem") UpgradeCenter, &xferCRC, "Data\\INI\\Default\\Upgrade.ini", "Data\\INI\\Upgrade.ini");
|
||||
initSubsystem(TheGameClient,"TheGameClient", createGameClient(), NULL);
|
||||
initSubsystem(TheAI,"TheAI", MSGNEW("GameEngineSubsystem") AI(), &xferCRC, "Data\\INI\\Default\\AIData.ini", "Data\\INI\\AIData.ini");
|
||||
initSubsystem(TheGameLogic,"TheGameLogic", createGameLogic(), NULL);
|
||||
initSubsystem(TheTeamFactory,"TheTeamFactory", MSGNEW("GameEngineSubsystem") TeamFactory(), NULL);
|
||||
initSubsystem(TheCrateSystem,"TheCrateSystem", MSGNEW("GameEngineSubsystem") CrateSystem(), &xferCRC, "Data\\INI\\Default\\Crate.ini", "Data\\INI\\Crate.ini");
|
||||
initSubsystem(ThePlayerList,"ThePlayerList", MSGNEW("GameEngineSubsystem") PlayerList(), NULL);
|
||||
initSubsystem(TheRecorder,"TheRecorder", createRecorder(), NULL);
|
||||
initSubsystem(TheRadar,"TheRadar", createRadar(), NULL);
|
||||
initSubsystem(TheVictoryConditions,"TheVictoryConditions", createVictoryConditions(), NULL);
|
||||
|
||||
AsciiString fname;
|
||||
fname.format("Data\\%s\\CommandMap.ini", GetRegistryLanguage().str());
|
||||
initSubsystem(TheMetaMap,"TheMetaMap", MSGNEW("GameEngineSubsystem") MetaMap(), NULL, fname.str(), "Data\\INI\\CommandMap.ini");
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
ini.load("Data\\INI\\CommandMapDebug.ini", INI_LOAD_MULTIFILE, NULL);
|
||||
#endif
|
||||
|
||||
initSubsystem(TheActionManager,"TheActionManager", MSGNEW("GameEngineSubsystem") ActionManager(), NULL);
|
||||
//initSubsystem((CComObject<WebBrowser> *)TheWebBrowser,"(CComObject<WebBrowser> *)TheWebBrowser", (CComObject<WebBrowser> *)createWebBrowser(), NULL);
|
||||
initSubsystem(TheGameStateMap,"TheGameStateMap", MSGNEW("GameEngineSubsystem") GameStateMap, NULL, NULL, NULL );
|
||||
initSubsystem(TheGameState,"TheGameState", MSGNEW("GameEngineSubsystem") GameState, NULL, NULL, NULL );
|
||||
|
||||
// Create the interface for sending game results
|
||||
initSubsystem(TheGameResultsQueue,"TheGameResultsQueue", GameResultsInterface::createNewGameResultsInterface(), NULL, NULL, NULL, NULL);
|
||||
|
||||
xferCRC.close();
|
||||
TheWritableGlobalData->m_iniCRC = xferCRC.getCRC();
|
||||
DEBUG_LOG(("INI CRC is 0x%8.8X\n", TheGlobalData->m_iniCRC));
|
||||
|
||||
TheSubsystemList->postProcessLoadAll();
|
||||
|
||||
setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
|
||||
|
||||
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_musicOn, AudioAffect_Music);
|
||||
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_soundsOn, AudioAffect_Sound);
|
||||
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_sounds3DOn, AudioAffect_Sound3D);
|
||||
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_speechOn, AudioAffect_Speech);
|
||||
|
||||
// We're not in a network game yet, so set the network singleton to NULL.
|
||||
TheNetwork = NULL;
|
||||
|
||||
//Create a default ini file for options if it doesn't already exist.
|
||||
//OptionPreferences prefs( TRUE );
|
||||
|
||||
// If we turn m_quitting to FALSE here, then we throw away any requests to quit that
|
||||
// took place during loading. :-\ - jkmcd
|
||||
// If this really needs to take place, please make sure that pressing cancel on the audio
|
||||
// load music dialog will still cause the game to quit.
|
||||
// m_quitting = FALSE;
|
||||
|
||||
// for fingerprinting, we need to ensure the presence of these files
|
||||
AsciiString dirName;
|
||||
dirName = TheArchiveFileSystem->getArchiveFilenameForFile("generalsb.sec");
|
||||
if (dirName.compareNoCase("gensec.big") != 0)
|
||||
{
|
||||
DEBUG_LOG(("generalsb.sec was not found in gensec.big - it was in '%s'\n", dirName.str()));
|
||||
m_quitting = TRUE;
|
||||
}
|
||||
|
||||
dirName = TheArchiveFileSystem->getArchiveFilenameForFile("generalsa.sec");
|
||||
const char *noPath = dirName.reverseFind('\\');
|
||||
if (noPath) {
|
||||
dirName = noPath + 1;
|
||||
}
|
||||
|
||||
if (dirName.compareNoCase("music.big") != 0)
|
||||
{
|
||||
DEBUG_LOG(("generalsa.sec was not found in music.big - it was in '%s'\n", dirName.str()));
|
||||
m_quitting = TRUE;
|
||||
}
|
||||
|
||||
// initialize the MapCache
|
||||
TheMapCache = MSGNEW("GameEngineSubsystem") MapCache;
|
||||
TheMapCache->updateCache();
|
||||
|
||||
if (TheGlobalData->m_buildMapCache)
|
||||
{
|
||||
// just quit, since the map cache has already updated
|
||||
//populateMapListbox(NULL, true, true);
|
||||
m_quitting = TRUE;
|
||||
}
|
||||
|
||||
// load the initial shell screen
|
||||
//TheShell->push( AsciiString("Menus/MainMenu.wnd") );
|
||||
|
||||
#if !defined(_PLAYTEST)
|
||||
// This allows us to run a map/reply from the command line
|
||||
if (TheGlobalData->m_initialFile.isEmpty() == FALSE)
|
||||
{
|
||||
AsciiString fname = TheGlobalData->m_initialFile;
|
||||
fname.toLower();
|
||||
|
||||
if (fname.endsWithNoCase(".map"))
|
||||
{
|
||||
TheWritableGlobalData->m_shellMapOn = FALSE;
|
||||
TheWritableGlobalData->m_playIntro = FALSE;
|
||||
TheWritableGlobalData->m_pendingFile = TheGlobalData->m_initialFile;
|
||||
|
||||
// shutdown the top, but do not pop it off the stack
|
||||
// TheShell->hideShell();
|
||||
|
||||
// send a message to the logic for a new game
|
||||
GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME );
|
||||
msg->appendIntegerArgument(GAME_SINGLE_PLAYER);
|
||||
msg->appendIntegerArgument(DIFFICULTY_NORMAL);
|
||||
msg->appendIntegerArgument(0);
|
||||
InitRandom(0);
|
||||
}
|
||||
else if (fname.endsWithNoCase(".rep"))
|
||||
{
|
||||
TheRecorder->playbackFile(fname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
if (TheMapCache && TheGlobalData->m_shellMapOn)
|
||||
{
|
||||
AsciiString lowerName = TheGlobalData->m_shellMapName;
|
||||
lowerName.toLower();
|
||||
|
||||
MapCache::const_iterator it = TheMapCache->find(lowerName);
|
||||
if (it == TheMapCache->end())
|
||||
{
|
||||
TheWritableGlobalData->m_shellMapOn = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!TheGlobalData->m_playIntro)
|
||||
TheWritableGlobalData->m_afterIntro = TRUE;
|
||||
|
||||
initDisabledMasks();
|
||||
|
||||
}
|
||||
catch (ErrorCode ec)
|
||||
{
|
||||
if (ec == ERROR_INVALID_D3D)
|
||||
{
|
||||
RELEASE_CRASHLOCALIZED("ERROR:D3DFailurePrompt", "ERROR:D3DFailureMessage");
|
||||
}
|
||||
}
|
||||
catch (INIException e)
|
||||
{
|
||||
if (e.mFailureMessage)
|
||||
RELEASE_CRASH((e.mFailureMessage));
|
||||
else
|
||||
RELEASE_CRASH(("Uncaught Exception during initialization."));
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RELEASE_CRASH(("Uncaught Exception during initialization."));
|
||||
}
|
||||
|
||||
if(!TheGlobalData->m_playIntro)
|
||||
TheWritableGlobalData->m_afterIntro = TRUE;
|
||||
|
||||
initDisabledMasks();
|
||||
|
||||
TheSubsystemList->resetAll();
|
||||
HideControlBar();
|
||||
} // end init
|
||||
|
||||
/** -----------------------------------------------------------------------------------------------
|
||||
* Reset all necessary parts of the game engine to be ready to accept new game data
|
||||
*/
|
||||
void GameEngine::reset( void )
|
||||
{
|
||||
|
||||
WindowLayout *background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd");
|
||||
DEBUG_ASSERTCRASH(background,("We Couldn't Load Menus/BlankWindow.wnd"));
|
||||
background->hide(FALSE);
|
||||
background->bringForward();
|
||||
background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE);
|
||||
Bool deleteNetwork = false;
|
||||
if (TheGameLogic->isInMultiplayerGame())
|
||||
deleteNetwork = true;
|
||||
|
||||
TheSubsystemList->resetAll();
|
||||
|
||||
if (deleteNetwork)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(TheNetwork, ("Deleting NULL TheNetwork!"));
|
||||
if (TheNetwork)
|
||||
delete TheNetwork;
|
||||
TheNetwork = NULL;
|
||||
}
|
||||
if(background)
|
||||
{
|
||||
background->destroyWindows();
|
||||
background->deleteInstance();
|
||||
background = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/// -----------------------------------------------------------------------------------------------
|
||||
DECLARE_PERF_TIMER(GameEngine_update)
|
||||
|
||||
/** -----------------------------------------------------------------------------------------------
|
||||
* Update the game engine by updating the GameClient and GameLogic singletons.
|
||||
* @todo Allow the client to run as fast as possible, but limit the execution
|
||||
* of TheNetwork and TheGameLogic to a fixed framerate.
|
||||
*/
|
||||
void GameEngine::update( void )
|
||||
{
|
||||
USE_PERF_TIMER(GameEngine_update)
|
||||
{
|
||||
|
||||
{
|
||||
|
||||
// VERIFY CRC needs to be in this code block. Please to not pull TheGameLogic->update() inside this block.
|
||||
VERIFY_CRC
|
||||
|
||||
TheRadar->UPDATE();
|
||||
|
||||
/// @todo Move audio init, update, etc, into GameClient update
|
||||
|
||||
TheAudio->UPDATE();
|
||||
TheGameClient->UPDATE();
|
||||
TheMessageStream->propagateMessages();
|
||||
|
||||
if (TheNetwork != NULL)
|
||||
{
|
||||
TheNetwork->UPDATE();
|
||||
}
|
||||
|
||||
TheCDManager->UPDATE();
|
||||
}
|
||||
|
||||
|
||||
if ((TheNetwork == NULL && !TheGameLogic->isGamePaused()) || (TheNetwork && TheNetwork->isFrameDataReady()))
|
||||
{
|
||||
TheGameLogic->UPDATE();
|
||||
}
|
||||
|
||||
} // end perfGather
|
||||
|
||||
}
|
||||
|
||||
// Horrible reference, but we really, really need to know if we are windowed.
|
||||
extern bool DX8Wrapper_IsWindowed;
|
||||
extern HWND ApplicationHWnd;
|
||||
|
||||
/** -----------------------------------------------------------------------------------------------
|
||||
* The "main loop" of the game engine. It will not return until the game exits.
|
||||
*/
|
||||
void GameEngine::execute( void )
|
||||
{
|
||||
|
||||
DWORD prevTime = timeGetTime();
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
DWORD startTime = timeGetTime() / 1000;
|
||||
#endif
|
||||
|
||||
// pretty basic for now
|
||||
while( !m_quitting )
|
||||
{
|
||||
|
||||
//if (TheGlobalData->m_vTune)
|
||||
{
|
||||
#ifdef PERF_TIMERS
|
||||
PerfGather::resetAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
{
|
||||
// enter only if in benchmark mode
|
||||
if (TheGlobalData->m_benchmarkTimer > 0)
|
||||
{
|
||||
DWORD currentTime = timeGetTime() / 1000;
|
||||
if (TheGlobalData->m_benchmarkTimer < currentTime - startTime)
|
||||
{
|
||||
if (TheGameLogic->isInGame())
|
||||
{
|
||||
if (TheRecorder->getMode() == RECORDERMODETYPE_RECORD)
|
||||
{
|
||||
TheRecorder->stopRecording();
|
||||
}
|
||||
TheGameLogic->clearGameData();
|
||||
}
|
||||
TheGameEngine->setQuitting(TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
try
|
||||
{
|
||||
// compute a frame
|
||||
update();
|
||||
}
|
||||
catch (INIException e)
|
||||
{
|
||||
// Release CRASH doesn't return, so don't worry about executing additional code.
|
||||
if (e.mFailureMessage)
|
||||
RELEASE_CRASH((e.mFailureMessage));
|
||||
else
|
||||
RELEASE_CRASH(("Uncaught Exception in GameEngine::update"));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// try to save info off
|
||||
try
|
||||
{
|
||||
if (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_RECORD && TheRecorder->isMultiplayer())
|
||||
TheRecorder->cleanUpReplayFile();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
RELEASE_CRASH(("Uncaught Exception in GameEngine::update"));
|
||||
} // catch
|
||||
} // perf
|
||||
|
||||
{
|
||||
|
||||
if (TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast())
|
||||
{
|
||||
|
||||
// I'm disabling this in internal because many people need alt-tab capability. If you happen to be
|
||||
// doing performance tuning, please just change this on your local system. -MDC
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
::Sleep(1); // give everyone else a tiny time slice.
|
||||
#endif
|
||||
|
||||
// limit the framerate
|
||||
DWORD now = timeGetTime();
|
||||
DWORD limit = (1000.0f/m_maxFPS)-1;
|
||||
while (TheGlobalData->m_useFpsLimit && (now - prevTime) < limit)
|
||||
{
|
||||
::Sleep(0);
|
||||
now = timeGetTime();
|
||||
}
|
||||
//Int slept = now - prevTime;
|
||||
//DEBUG_LOG(("delayed %d\n",slept));
|
||||
|
||||
prevTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
} // perfgather for execute_loop
|
||||
|
||||
#ifdef PERF_TIMERS
|
||||
if (!m_quitting && TheGameLogic->isInGame() && !TheGameLogic->isInShellGame() && !TheGameLogic->isGamePaused())
|
||||
{
|
||||
PerfGather::dumpAll(TheGameLogic->getFrame());
|
||||
PerfGather::displayGraph(TheGameLogic->getFrame());
|
||||
PerfGather::resetAll();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** -----------------------------------------------------------------------------------------------
|
||||
* Factory for the message stream
|
||||
*/
|
||||
MessageStream *GameEngine::createMessageStream( void )
|
||||
{
|
||||
// if you change this update the tools that use the engine systems
|
||||
// like GUIEdit, it creates a message stream to run in "test" mode
|
||||
return MSGNEW("GameEngineSubsystem") MessageStream;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FileSystem *GameEngine::createFileSystem( void )
|
||||
{
|
||||
return MSGNEW("GameEngineSubsystem") FileSystem;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool GameEngine::isMultiplayerSession( void )
|
||||
{
|
||||
return TheRecorder->isMultiplayer();
|
||||
}
|
||||
|
||||
/** MW - 6-10-03: I added this function in order to verify that users who quit the
|
||||
application by not using the menus (Alt-F4, etc.) are not doing it in the
|
||||
middle of an internet game. Quitting in this way is considered cheating so
|
||||
we will log the results to a file and transmit the updated stats on the next
|
||||
login to gamespy. I copied most of this code from the normal disconnection
|
||||
logging code that is usually called from another thread. I'm doing it here
|
||||
because there's no way to guarantee the other thread will execute before we
|
||||
exit the app.
|
||||
*/
|
||||
void GameEngine::checkAbnormalQuitting(void)
|
||||
{
|
||||
if (TheRecorder->isMultiplayer() && TheGameLogic->isInInternetGame())
|
||||
{ //Should not be quitting at this time, record it as a cheat.
|
||||
|
||||
Int localID = TheGameSpyInfo->getLocalProfileID();
|
||||
PSPlayerStats stats = TheGameSpyPSMessageQueue->findPlayerStatsByID(localID);
|
||||
|
||||
Player *player=ThePlayerList->getLocalPlayer();
|
||||
|
||||
Int ptIdx;
|
||||
const PlayerTemplate *myTemplate = player->getPlayerTemplate();
|
||||
DEBUG_LOG(("myTemplate = %X(%s)\n", myTemplate, myTemplate->getName().str()));
|
||||
for (ptIdx = 0; ptIdx < ThePlayerTemplateStore->getPlayerTemplateCount(); ++ptIdx)
|
||||
{
|
||||
const PlayerTemplate *nthTemplate = ThePlayerTemplateStore->getNthPlayerTemplate(ptIdx);
|
||||
DEBUG_LOG(("nthTemplate = %X(%s)\n", nthTemplate, nthTemplate->getName().str()));
|
||||
if (nthTemplate == myTemplate)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PSRequest req;
|
||||
|
||||
req.requestType = PSRequest::PSREQUEST_UPDATEPLAYERSTATS;
|
||||
req.email = TheGameSpyInfo->getLocalEmail().str();
|
||||
req.nick = TheGameSpyInfo->getLocalBaseName().str();
|
||||
req.password = TheGameSpyInfo->getLocalPassword().str();
|
||||
req.player = stats;
|
||||
req.addDesync = FALSE;
|
||||
req.addDiscon = TRUE;
|
||||
req.lastHouse = ptIdx;
|
||||
|
||||
UserPreferences pref;
|
||||
AsciiString userPrefFilename;
|
||||
userPrefFilename.format("GeneralsOnline\\MiscPref%d.ini", stats.id);
|
||||
DEBUG_LOG(("using the file %s\n", userPrefFilename.str()));
|
||||
pref.load(userPrefFilename);
|
||||
|
||||
Int addedInDesyncs2 = pref.getInt("0", 0);
|
||||
DEBUG_LOG(("addedInDesyncs2 = %d\n", addedInDesyncs2));
|
||||
if (addedInDesyncs2 < 0)
|
||||
addedInDesyncs2 = 10;
|
||||
Int addedInDesyncs3 = pref.getInt("1", 0);
|
||||
DEBUG_LOG(("addedInDesyncs3 = %d\n", addedInDesyncs3));
|
||||
if (addedInDesyncs3 < 0)
|
||||
addedInDesyncs3 = 10;
|
||||
Int addedInDesyncs4 = pref.getInt("2", 0);
|
||||
DEBUG_LOG(("addedInDesyncs4 = %d\n", addedInDesyncs4));
|
||||
if (addedInDesyncs4 < 0)
|
||||
addedInDesyncs4 = 10;
|
||||
Int addedInDiscons2 = pref.getInt("3", 0);
|
||||
DEBUG_LOG(("addedInDiscons2 = %d\n", addedInDiscons2));
|
||||
if (addedInDiscons2 < 0)
|
||||
addedInDiscons2 = 10;
|
||||
Int addedInDiscons3 = pref.getInt("4", 0);
|
||||
DEBUG_LOG(("addedInDiscons3 = %d\n", addedInDiscons3));
|
||||
if (addedInDiscons3 < 0)
|
||||
addedInDiscons3 = 10;
|
||||
Int addedInDiscons4 = pref.getInt("5", 0);
|
||||
DEBUG_LOG(("addedInDiscons4 = %d\n", addedInDiscons4));
|
||||
if (addedInDiscons4 < 0)
|
||||
addedInDiscons4 = 10;
|
||||
|
||||
DEBUG_LOG(("req.addDesync=%d, req.addDiscon=%d, addedInDesync=%d,%d,%d, addedInDiscon=%d,%d,%d\n",
|
||||
req.addDesync, req.addDiscon, addedInDesyncs2, addedInDesyncs3, addedInDesyncs4,
|
||||
addedInDiscons2, addedInDiscons3, addedInDiscons4));
|
||||
|
||||
if (req.addDesync || req.addDiscon)
|
||||
{
|
||||
AsciiString val;
|
||||
if (req.lastHouse == 2)
|
||||
{
|
||||
val.format("%d", addedInDesyncs2 + req.addDesync);
|
||||
pref["0"] = val;
|
||||
val.format("%d", addedInDiscons2 + req.addDiscon);
|
||||
pref["3"] = val;
|
||||
DEBUG_LOG(("house 2 req.addDesync || req.addDiscon: %d %d\n",
|
||||
addedInDesyncs2 + req.addDesync, addedInDiscons2 + req.addDiscon));
|
||||
}
|
||||
else if (req.lastHouse == 3)
|
||||
{
|
||||
val.format("%d", addedInDesyncs3 + req.addDesync);
|
||||
pref["1"] = val;
|
||||
val.format("%d", addedInDiscons3 + req.addDiscon);
|
||||
pref["4"] = val;
|
||||
DEBUG_LOG(("house 3 req.addDesync || req.addDiscon: %d %d\n",
|
||||
addedInDesyncs3 + req.addDesync, addedInDiscons3 + req.addDiscon));
|
||||
}
|
||||
else
|
||||
{
|
||||
val.format("%d", addedInDesyncs4 + req.addDesync);
|
||||
pref["2"] = val;
|
||||
val.format("%d", addedInDiscons4 + req.addDiscon);
|
||||
pref["5"] = val;
|
||||
DEBUG_LOG(("house 4 req.addDesync || req.addDiscon: %d %d\n",
|
||||
addedInDesyncs4 + req.addDesync, addedInDiscons4 + req.addDiscon));
|
||||
}
|
||||
pref.write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
#define CONVERT_EXEC1 "..\\Build\\nvdxt -list buildDDS.txt -dxt5 -full -outdir Art\\Textures > buildDDS.out"
|
||||
|
||||
void updateTGAtoDDS()
|
||||
{
|
||||
// Here's the scoop. We're going to traverse through all of the files in the Art\Textures folder
|
||||
// and determine if there are any .tga files that are newer than associated .dds files. If there
|
||||
// are, then we will re-run the compression tool on them.
|
||||
|
||||
File *fp = TheLocalFileSystem->openFile("buildDDS.txt", File::WRITE | File::CREATE | File::TRUNCATE | File::TEXT);
|
||||
if (!fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
FilenameList files;
|
||||
TheLocalFileSystem->getFileListInDirectory("Art\\Textures\\", "", "*.tga", files, TRUE);
|
||||
FilenameList::iterator it;
|
||||
for (it = files.begin(); it != files.end(); ++it) {
|
||||
AsciiString filenameTGA = *it;
|
||||
AsciiString filenameDDS = *it;
|
||||
FileInfo infoTGA;
|
||||
TheLocalFileSystem->getFileInfo(filenameTGA, &infoTGA);
|
||||
|
||||
// skip the water textures, since they need to be NOT compressed
|
||||
filenameTGA.toLower();
|
||||
if (strstr(filenameTGA.str(), "caust"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// and the recolored stuff.
|
||||
if (strstr(filenameTGA.str(), "zhca"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// replace tga with dds
|
||||
filenameDDS.removeLastChar(); // a
|
||||
filenameDDS.removeLastChar(); // g
|
||||
filenameDDS.removeLastChar(); // t
|
||||
filenameDDS.concat("dds");
|
||||
|
||||
Bool needsToBeUpdated = FALSE;
|
||||
FileInfo infoDDS;
|
||||
if (TheFileSystem->doesFileExist(filenameDDS.str())) {
|
||||
TheFileSystem->getFileInfo(filenameDDS, &infoDDS);
|
||||
if (infoTGA.timestampHigh > infoDDS.timestampHigh ||
|
||||
(infoTGA.timestampHigh == infoDDS.timestampHigh &&
|
||||
infoTGA.timestampLow > infoDDS.timestampLow)) {
|
||||
needsToBeUpdated = TRUE;
|
||||
}
|
||||
} else {
|
||||
needsToBeUpdated = TRUE;
|
||||
}
|
||||
|
||||
if (!needsToBeUpdated) {
|
||||
continue;
|
||||
}
|
||||
|
||||
filenameTGA.concat("\n");
|
||||
fp->write(filenameTGA.str(), filenameTGA.getLength());
|
||||
}
|
||||
|
||||
fp->close();
|
||||
|
||||
system(CONVERT_EXEC1);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// System things
|
||||
|
||||
// If we're using the Wide character version of MessageBox, then there's no additional
|
||||
// processing necessary. Please note that this is a sleazy way to get this information,
|
||||
// but pending a better one, this'll have to do.
|
||||
extern const Bool TheSystemIsUnicode = (((void*) (::MessageBox)) == ((void*) (::MessageBoxW)));
|
||||
702
Generals/Code/GameEngine/Source/Common/GameLOD.cpp
Normal file
702
Generals/Code/GameEngine/Source/Common/GameLOD.cpp
Normal file
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: GameLOD.cpp ///////////////////////////////////////////////////////////
|
||||
//
|
||||
// Used to set detail levels of various game systems.
|
||||
//
|
||||
// Author: Mark Wilczynski, Sept 2002
|
||||
//
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
|
||||
|
||||
#include "Common/GameLOD.h"
|
||||
#include "GameClient/TerrainVisual.h"
|
||||
#include "GameClient/GameClient.h"
|
||||
#include "Common/UserPreferences.h"
|
||||
|
||||
#define DEFINE_PARTICLE_SYSTEM_NAMES
|
||||
#include "GameClient/ParticleSys.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
#define PROFILE_ERROR_LIMIT 0.94f //fraction of profiled result needed to get a match. Allows some room for error/fluctuation.
|
||||
|
||||
//Hack to get access to a static method on the W3DDevice side. -MW
|
||||
extern Bool testMinimumRequirements(ChipsetType *videoChipType, CpuType *cpuType, Int *cpuFreq, Int *numRAM, Real *intBenchIndex, Real *floatBenchIndex, Real *memBenchIndex);
|
||||
|
||||
GameLODManager *TheGameLODManager=NULL;
|
||||
|
||||
static const FieldParse TheStaticGameLODFieldParseTable[] =
|
||||
{
|
||||
{ "MinimumFPS", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_minFPS)},
|
||||
{ "MinimumProcessorFps", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_minProcessorFPS)},
|
||||
{ "SampleCount2D", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_sampleCount2D ) },
|
||||
{ "SampleCount3D", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_sampleCount3D ) },
|
||||
{ "StreamCount", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_streamCount ) },
|
||||
{ "MaxParticleCount", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxParticleCount ) },
|
||||
{ "UseShadowVolumes", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useShadowVolumes ) },
|
||||
{ "UseShadowDecals", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useShadowDecals ) },
|
||||
{ "UseCloudMap", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useCloudMap ) },
|
||||
{ "UseLightMap", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useLightMap ) },
|
||||
{ "ShowSoftWaterEdge", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_showSoftWaterEdge ) },
|
||||
{ "MaxTankTrackEdges", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackEdges) },
|
||||
{ "MaxTankTrackOpaqueEdges", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackOpaqueEdges) },
|
||||
{ "MaxTankTrackFadeDelay", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_maxTankTrackFadeDelay) },
|
||||
{ "UseBuildupScaffolds", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useBuildupScaffolds ) },
|
||||
{ "UseTreeSway", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useTreeSway ) },
|
||||
{ "UseEmissiveNightMaterials", INI::parseBool, NULL, offsetof( StaticGameLODInfo, m_useEmissiveNightMaterials ) },
|
||||
{ "TextureReductionFactor", INI::parseInt, NULL, offsetof( StaticGameLODInfo, m_textureReduction ) },
|
||||
};
|
||||
|
||||
static const char *StaticGameLODNames[]=
|
||||
{
|
||||
"Low",
|
||||
"Medium",
|
||||
"High",
|
||||
"Custom"
|
||||
};
|
||||
|
||||
StaticGameLODInfo::StaticGameLODInfo(void)
|
||||
{
|
||||
m_minFPS=0;
|
||||
m_minProcessorFPS=0;
|
||||
m_sampleCount2D=6;
|
||||
m_sampleCount3D=24;
|
||||
m_streamCount=2;
|
||||
m_maxParticleCount=2500;
|
||||
|
||||
m_useShadowVolumes=TRUE;
|
||||
m_useShadowDecals=TRUE;
|
||||
m_useCloudMap=TRUE;
|
||||
m_useLightMap=TRUE;
|
||||
m_showSoftWaterEdge=TRUE;
|
||||
m_maxTankTrackEdges=100;
|
||||
m_maxTankTrackOpaqueEdges=25;
|
||||
m_maxTankTrackFadeDelay=300000;
|
||||
m_useBuildupScaffolds=TRUE;
|
||||
m_useTreeSway=TRUE;
|
||||
m_useEmissiveNightMaterials=TRUE;
|
||||
m_textureReduction = 0; //none
|
||||
m_useFpsLimit = TRUE;
|
||||
m_enableDynamicLOD = TRUE;
|
||||
m_useTrees = TRUE;
|
||||
}
|
||||
|
||||
static const FieldParse TheDynamicGameLODFieldParseTable[] =
|
||||
{
|
||||
{ "MinimumFPS", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_minFPS)},
|
||||
{ "ParticleSkipMask", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_dynamicParticleSkipMask)},
|
||||
{ "DebrisSkipMask", INI::parseInt, NULL, offsetof( DynamicGameLODInfo, m_dynamicDebrisSkipMask)},
|
||||
{ "SlowDeathScale", INI::parseReal, NULL, offsetof( DynamicGameLODInfo, m_slowDeathScale)},
|
||||
{ "MinParticlePriority", INI::parseIndexList, ParticlePriorityNames, offsetof( DynamicGameLODInfo, m_minDynamicParticlePriority)},
|
||||
{ "MinParticleSkipPriority", INI::parseIndexList, ParticlePriorityNames, offsetof( DynamicGameLODInfo, m_minDynamicParticleSkipPriority)},
|
||||
};
|
||||
|
||||
static const char *DynamicGameLODNames[]=
|
||||
{
|
||||
"Low",
|
||||
"Medium",
|
||||
"High",
|
||||
"VeryHigh"
|
||||
};
|
||||
|
||||
DynamicGameLODInfo::DynamicGameLODInfo(void)
|
||||
{
|
||||
m_minFPS=0;
|
||||
m_dynamicParticleSkipMask=0;
|
||||
m_dynamicDebrisSkipMask=0;
|
||||
m_slowDeathScale=1.0f;
|
||||
m_minDynamicParticlePriority = PARTICLE_PRIORITY_LOWEST;
|
||||
m_minDynamicParticleSkipPriority = PARTICLE_PRIORITY_LOWEST;
|
||||
};
|
||||
|
||||
//Keep this in sync with enum in GameLOD.h
|
||||
static char *CPUNames[] =
|
||||
{
|
||||
"XX","P3", "P4","K7", NULL
|
||||
};
|
||||
|
||||
//Keep this in sync with enum in GameLOD.h
|
||||
static char *VideoNames[] =
|
||||
{
|
||||
"XX","V2","V3","V4","V5","TNT","TNT2","GF2","R100","PS11","GF3","GF4","PS14","R200","PS20","R300", NULL
|
||||
};
|
||||
|
||||
void parseReallyLowMHz(INI* ini)
|
||||
{
|
||||
Int mhz;
|
||||
INI::parseInt(ini,NULL,&mhz,NULL);
|
||||
if (TheGameLODManager)
|
||||
{
|
||||
TheGameLODManager->setReallyLowMHz(mhz);
|
||||
}
|
||||
}
|
||||
|
||||
void INI::parseBenchProfile( INI* ini)
|
||||
{
|
||||
if( TheGameLODManager )
|
||||
{
|
||||
BenchProfile *preset = TheGameLODManager->newBenchProfile();
|
||||
|
||||
if (preset)
|
||||
{
|
||||
INI::parseIndexList(ini,NULL,&preset->m_cpuType,CPUNames);
|
||||
INI::parseInt(ini,NULL,&preset->m_mhz,NULL);
|
||||
INI::parseReal(ini,NULL,&preset->m_intBenchIndex,NULL);
|
||||
INI::parseReal(ini,NULL,&preset->m_floatBenchIndex,NULL);
|
||||
INI::parseReal(ini,NULL,&preset->m_memBenchIndex,NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**Parse a description of all the LOD settings for a given detail level*/
|
||||
void INI::parseLODPreset( INI* ini )
|
||||
{
|
||||
const char *c;
|
||||
AsciiString name;
|
||||
|
||||
// read the name
|
||||
c = ini->getNextToken();
|
||||
name.set( c ); //name of detail level - low, medium, high
|
||||
|
||||
if( TheGameLODManager )
|
||||
{
|
||||
StaticGameLODLevel index = (StaticGameLODLevel)TheGameLODManager->getStaticGameLODIndex(name);
|
||||
if (index != STATIC_GAME_LOD_UNKNOWN)
|
||||
{
|
||||
LODPresetInfo *preset = TheGameLODManager->newLODPreset(index);
|
||||
|
||||
if (preset)
|
||||
{
|
||||
INI::parseIndexList(ini,NULL,&preset->m_cpuType,CPUNames);
|
||||
INI::parseInt(ini,NULL,&preset->m_mhz,NULL);
|
||||
INI::parseIndexList(ini,NULL,&preset->m_videoType,VideoNames);
|
||||
INI::parseInt(ini,NULL,&preset->m_memory,NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GameLODManager::GameLODManager(void)
|
||||
{
|
||||
m_currentStaticLOD = STATIC_GAME_LOD_UNKNOWN;
|
||||
m_currentDynamicLOD = DYNAMIC_GAME_LOD_HIGH;
|
||||
m_numParticleGenerations=0;
|
||||
m_dynamicParticleSkipMask=0;
|
||||
m_numDebrisGenerations=0;
|
||||
m_dynamicDebrisSkipMask=0;
|
||||
m_videoPassed=false;
|
||||
m_cpuPassed=false;
|
||||
m_memPassed=false;
|
||||
m_slowDeathScale=1.0f;
|
||||
m_idealDetailLevel = STATIC_GAME_LOD_UNKNOWN;
|
||||
m_videoChipType = DC_MAX;
|
||||
m_cpuType = XX;
|
||||
m_numRAM=0;
|
||||
m_cpuFreq=0;
|
||||
m_intBenchIndex=0;
|
||||
m_floatBenchIndex=0;
|
||||
m_memBenchIndex=0;
|
||||
m_compositeBenchIndex=0;
|
||||
m_numBenchProfiles=0;
|
||||
m_currentTextureReduction=0;
|
||||
m_reallyLowMHz = 400;
|
||||
|
||||
for (Int i=0; i<STATIC_GAME_LOD_CUSTOM; i++)
|
||||
m_numLevelPresets[i]=0;
|
||||
};
|
||||
|
||||
GameLODManager::~GameLODManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BenchProfile *GameLODManager::newBenchProfile(void)
|
||||
{
|
||||
if (m_numBenchProfiles < MAX_BENCH_PROFILES)
|
||||
{
|
||||
m_numBenchProfiles++;
|
||||
return &m_benchProfiles[m_numBenchProfiles-1];
|
||||
}
|
||||
|
||||
DEBUG_CRASH(( "GameLODManager::newBenchProfile - Too many profiles defined\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LODPresetInfo *GameLODManager::newLODPreset(StaticGameLODLevel index)
|
||||
{
|
||||
if (m_numLevelPresets[index] < MAX_LOD_PRESETS_PER_LEVEL)
|
||||
{
|
||||
m_numLevelPresets[index]++;
|
||||
return &m_lodPresets[index][m_numLevelPresets[index]-1];
|
||||
}
|
||||
|
||||
DEBUG_CRASH(( "GameLODManager::newLODPreset - Too many presets defined for '%s'\n", TheGameLODManager->getStaticGameLODLevelName(index)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void GameLODManager::init(void)
|
||||
{
|
||||
INI ini;
|
||||
//Get Presets for each LOD level.
|
||||
ini.load( AsciiString( "Data\\INI\\GameLOD.ini" ), INI_LOAD_OVERWRITE, NULL );
|
||||
|
||||
//Get presets for each known hardware configuration
|
||||
ini.load( AsciiString( "Data\\INI\\GameLODPresets.ini"), INI_LOAD_OVERWRITE, NULL);
|
||||
|
||||
//Get Presets for custom LOD level by pulling them out of initial globaldata (which should
|
||||
//have all settings already applied).
|
||||
refreshCustomStaticLODLevel();
|
||||
|
||||
//Override with user preferences
|
||||
OptionPreferences optionPref;
|
||||
|
||||
StaticGameLODLevel userSetDetail=(StaticGameLODLevel)optionPref.getStaticGameDetail();
|
||||
|
||||
m_idealDetailLevel=(StaticGameLODLevel)optionPref.getIdealStaticGameDetail();
|
||||
|
||||
//always get this data in case we need it later.
|
||||
testMinimumRequirements(NULL,&m_cpuType,&m_cpuFreq,&m_numRAM,NULL,NULL,NULL);
|
||||
|
||||
if ((Real)(m_numRAM)/(Real)(256*1024*1024) >= PROFILE_ERROR_LIMIT)
|
||||
m_memPassed=TRUE; //check if they have at least 256 MB
|
||||
|
||||
if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN || TheGlobalData->m_forceBenchmark)
|
||||
{
|
||||
if (m_cpuType == XX || TheGlobalData->m_forceBenchmark)
|
||||
{
|
||||
//need to run the benchmark
|
||||
testMinimumRequirements(NULL,NULL,NULL,NULL,&m_intBenchIndex,&m_floatBenchIndex,&m_memBenchIndex);
|
||||
|
||||
if (TheGlobalData->m_forceBenchmark)
|
||||
{ //we want to see the numbers. So dump them to a logfile.
|
||||
FILE *fp=fopen("Benchmark.txt","w");
|
||||
if (fp)
|
||||
{
|
||||
fprintf(fp,"BenchProfile = %s %d %f %f %f", CPUNames[m_cpuType], m_cpuFreq, m_intBenchIndex, m_floatBenchIndex, m_memBenchIndex);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
m_compositeBenchIndex = m_intBenchIndex + m_floatBenchIndex; ///@todo: Need to scale these based on our apps usage of int/float/mem ops.
|
||||
|
||||
StaticGameLODLevel currentLevel=STATIC_GAME_LOD_LOW;
|
||||
BenchProfile *prof=m_benchProfiles;
|
||||
m_cpuType = P3; //assume lowest spec.
|
||||
m_cpuFreq = 1000; //assume lowest spec.
|
||||
for (Int k=0; k<m_numBenchProfiles; k++)
|
||||
{
|
||||
//Check if we're within 5% of the performance of this cpu profile.
|
||||
if (m_intBenchIndex/prof->m_intBenchIndex >= PROFILE_ERROR_LIMIT && m_floatBenchIndex/prof->m_floatBenchIndex >= PROFILE_ERROR_LIMIT && m_memBenchIndex/prof->m_memBenchIndex >= PROFILE_ERROR_LIMIT)
|
||||
{
|
||||
for (Int i=STATIC_GAME_LOD_HIGH; i >= STATIC_GAME_LOD_LOW; i--)
|
||||
{
|
||||
LODPresetInfo *preset=&m_lodPresets[i][0]; //pointer to first preset at this LOD level.
|
||||
for (Int j=0; j<m_numLevelPresets[i]; j++)
|
||||
{
|
||||
if( prof->m_cpuType == preset->m_cpuType && ((Real)prof->m_mhz/(Real)preset->m_mhz >= PROFILE_ERROR_LIMIT))
|
||||
{ currentLevel = (StaticGameLODLevel)i;
|
||||
m_cpuType = prof->m_cpuType;
|
||||
m_cpuFreq = prof->m_mhz;
|
||||
break;
|
||||
}
|
||||
preset++; //skip to next preset
|
||||
}
|
||||
if (currentLevel >= i)
|
||||
break; //we already found a higher level than the remaining presets so no need to keep searching.
|
||||
}
|
||||
}
|
||||
prof++;
|
||||
}
|
||||
} //finding equivalent CPU to unkown cpu.
|
||||
} //find data needed to determine m_idealDetailLevel
|
||||
|
||||
if (userSetDetail == STATIC_GAME_LOD_CUSTOM)
|
||||
{
|
||||
TheWritableGlobalData->m_textureReductionFactor = optionPref.getTextureReduction();
|
||||
TheWritableGlobalData->m_useShadowVolumes = optionPref.get3DShadowsEnabled();
|
||||
TheWritableGlobalData->m_useShadowDecals = optionPref.get2DShadowsEnabled();
|
||||
TheWritableGlobalData->m_enableBehindBuildingMarkers = optionPref.getBuildingOcclusionEnabled();
|
||||
TheWritableGlobalData->m_maxParticleCount = optionPref.getParticleCap();
|
||||
TheWritableGlobalData->m_enableDynamicLOD = optionPref.getDynamicLODEnabled();
|
||||
TheWritableGlobalData->m_useFpsLimit = optionPref.getFPSLimitEnabled();
|
||||
TheWritableGlobalData->m_useLightMap = optionPref.getLightmapEnabled();
|
||||
TheWritableGlobalData->m_useCloudMap = optionPref.getCloudShadowsEnabled();
|
||||
TheWritableGlobalData->m_showSoftWaterEdge = optionPref.getSmoothWaterEnabled();
|
||||
TheWritableGlobalData->m_useDrawModuleLOD = optionPref.getExtraAnimationsDisabled();
|
||||
TheWritableGlobalData->m_useTreeSway = !TheWritableGlobalData->m_useDrawModuleLOD; //borrow same setting.
|
||||
TheWritableGlobalData->m_useTrees = optionPref.getTreesEnabled();
|
||||
}
|
||||
|
||||
setStaticLODLevel(userSetDetail);
|
||||
}
|
||||
|
||||
void GameLODManager::refreshCustomStaticLODLevel(void)
|
||||
{
|
||||
StaticGameLODInfo *lodInfo=&m_staticGameLODInfo[STATIC_GAME_LOD_CUSTOM];
|
||||
|
||||
lodInfo->m_maxParticleCount=TheGlobalData->m_maxParticleCount;
|
||||
lodInfo->m_useShadowVolumes=TheGlobalData->m_useShadowVolumes;
|
||||
lodInfo->m_useShadowDecals=TheGlobalData->m_useShadowDecals;
|
||||
lodInfo->m_useCloudMap=TheGlobalData->m_useCloudMap;
|
||||
lodInfo->m_useLightMap=TheGlobalData->m_useLightMap;
|
||||
lodInfo->m_showSoftWaterEdge=TheGlobalData->m_showSoftWaterEdge;
|
||||
lodInfo->m_maxTankTrackEdges=TheGlobalData->m_maxTankTrackEdges;
|
||||
lodInfo->m_maxTankTrackOpaqueEdges=TheGlobalData->m_maxTankTrackOpaqueEdges;
|
||||
lodInfo->m_maxTankTrackFadeDelay=TheGlobalData->m_maxTankTrackFadeDelay;
|
||||
lodInfo->m_useBuildupScaffolds=!TheGlobalData->m_useDrawModuleLOD;
|
||||
lodInfo->m_useTreeSway=lodInfo->m_useBuildupScaffolds;// Borrow same setting. //TheGlobalData->m_useTreeSway;
|
||||
lodInfo->m_textureReduction=TheGlobalData->m_textureReductionFactor;
|
||||
lodInfo->m_useFpsLimit = TheGlobalData->m_useFpsLimit;
|
||||
lodInfo->m_enableDynamicLOD=TheGlobalData->m_enableDynamicLOD;
|
||||
lodInfo->m_useTrees = TheGlobalData->m_useTrees;
|
||||
|
||||
}
|
||||
|
||||
/**Convert LOD name to an index*/
|
||||
Int GameLODManager::getStaticGameLODIndex(AsciiString name)
|
||||
{
|
||||
for (Int i=0; i<STATIC_GAME_LOD_COUNT; ++i)
|
||||
{
|
||||
if (name.compareNoCase(StaticGameLODNames[i]) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
DEBUG_CRASH(( "GameLODManager::getGameLODIndex - Invalid LOD name '%s'\n", name.str() ));
|
||||
return STATIC_GAME_LOD_UNKNOWN;
|
||||
}
|
||||
|
||||
/**Parse a description of all the LOD settings for a given detail level*/
|
||||
void INI::parseStaticGameLODDefinition( INI* ini )
|
||||
{
|
||||
const char *c;
|
||||
AsciiString name;
|
||||
|
||||
// read the name
|
||||
c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
if( TheGameLODManager )
|
||||
{
|
||||
Int index = TheGameLODManager->getStaticGameLODIndex(name);
|
||||
if (index != STATIC_GAME_LOD_UNKNOWN)
|
||||
{
|
||||
StaticGameLODInfo *lodInfo = &(TheGameLODManager->m_staticGameLODInfo[index]);
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( lodInfo, TheStaticGameLODFieldParseTable );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**Parse an LOD level*/
|
||||
void INI::parseStaticGameLODLevel( INI* ini, void * , void *store, const void*)
|
||||
{
|
||||
const char *tok=ini->getNextToken();
|
||||
for (Int i=0; i<STATIC_GAME_LOD_COUNT; i++)
|
||||
if( stricmp(tok, StaticGameLODNames[i]) == 0 )
|
||||
{ *(StaticGameLODLevel*)store = (StaticGameLODLevel)i;
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_CRASH(("invalid GameLODLevel token %s -- expected LOW/MEDIUM/HIGH\n",tok));
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
|
||||
const char *GameLODManager::getStaticGameLODLevelName(StaticGameLODLevel level)
|
||||
{
|
||||
return StaticGameLODNames[level];
|
||||
}
|
||||
|
||||
/**Function which calculates the recommended LOD level for current hardware
|
||||
configuration.*/
|
||||
StaticGameLODLevel GameLODManager::findStaticLODLevel(void)
|
||||
{
|
||||
//Check if we have never done the test on current system
|
||||
if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN)
|
||||
{
|
||||
//search all our presets for matching hardware
|
||||
m_idealDetailLevel = STATIC_GAME_LOD_LOW;
|
||||
|
||||
//get system configuration - only need vide chip type, got rest in ::init().
|
||||
testMinimumRequirements(&m_videoChipType,NULL,NULL,NULL,NULL,NULL,NULL);
|
||||
if (m_videoChipType == DC_UNKNOWN)
|
||||
m_videoChipType = DC_TNT2; //presume it's at least TNT2 level
|
||||
|
||||
Int numMBRam=m_numRAM/(1024*1024);
|
||||
|
||||
for (Int i=STATIC_GAME_LOD_HIGH; i >= STATIC_GAME_LOD_LOW; i--)
|
||||
{
|
||||
LODPresetInfo *preset=&m_lodPresets[i][0]; //pointer to first preset at this LOD level.
|
||||
for (Int j=0; j<m_numLevelPresets[i]; j++)
|
||||
{
|
||||
|
||||
if( m_cpuType == preset->m_cpuType &&
|
||||
((Real)m_cpuFreq/(Real)preset->m_mhz >= PROFILE_ERROR_LIMIT) &&//make sure we're within 5% or higher
|
||||
m_videoChipType >= preset->m_videoType &&
|
||||
((Real)numMBRam/(Real)preset->m_memory >= PROFILE_ERROR_LIMIT)
|
||||
)
|
||||
{ m_idealDetailLevel = (StaticGameLODLevel)i;
|
||||
break;
|
||||
}
|
||||
|
||||
preset++; //skip to next preset
|
||||
|
||||
}
|
||||
if (m_idealDetailLevel >= i)
|
||||
break; //we already found a higher level than the remaining presets so no need to keep searching.
|
||||
}
|
||||
//Save ideal detail level for future usage
|
||||
OptionPreferences optionPref;
|
||||
optionPref["IdealStaticGameLOD"] = getStaticGameLODLevelName(m_idealDetailLevel);
|
||||
if (getStaticLODLevel() == STATIC_GAME_LOD_UNKNOWN) //save for future usage.
|
||||
optionPref["StaticGameLOD"] = getStaticGameLODLevelName(m_idealDetailLevel);
|
||||
optionPref.write();
|
||||
}
|
||||
|
||||
return m_idealDetailLevel;
|
||||
}
|
||||
|
||||
/**Set all game systems to match the desired LOD level.*/
|
||||
Bool GameLODManager::setStaticLODLevel(StaticGameLODLevel level)
|
||||
{
|
||||
if (!TheGlobalData->m_enableStaticLOD)
|
||||
{ m_currentStaticLOD = STATIC_GAME_LOD_CUSTOM;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (level == STATIC_GAME_LOD_UNKNOWN || (level != STATIC_GAME_LOD_CUSTOM && m_currentStaticLOD == level))
|
||||
return FALSE; //level is already applied. Custom levels are always applied since random options could change.
|
||||
|
||||
applyStaticLODLevel(level);
|
||||
m_currentStaticLOD = level;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void GameLODManager::applyStaticLODLevel(StaticGameLODLevel level)
|
||||
{
|
||||
///@todo: Still need to implement these settings:
|
||||
// m_sampleCount2D=6;
|
||||
// m_sampleCount3D=24;
|
||||
// m_streamCount=2;
|
||||
// m_useEmissiveNightMaterials=TRUE;
|
||||
|
||||
//save previous info for this level since it may be overwritten by refreshCustomStaticLODLevel().
|
||||
StaticGameLODInfo prevLodBackup;
|
||||
if (m_currentStaticLOD != STATIC_GAME_LOD_UNKNOWN)
|
||||
prevLodBackup=m_staticGameLODInfo[m_currentStaticLOD];
|
||||
|
||||
if (level == STATIC_GAME_LOD_CUSTOM)
|
||||
refreshCustomStaticLODLevel(); //store current settings into custom preset
|
||||
|
||||
StaticGameLODInfo *lodInfo=&m_staticGameLODInfo[level];
|
||||
StaticGameLODInfo *prevLodInfo=&prevLodBackup;
|
||||
|
||||
Int requestedTextureReduction = 0;
|
||||
Bool requestedTrees = m_memPassed; //only use trees if memory requirement passed.
|
||||
if (level == STATIC_GAME_LOD_CUSTOM)
|
||||
{ requestedTextureReduction = lodInfo->m_textureReduction;
|
||||
requestedTrees = lodInfo->m_useTrees;
|
||||
}
|
||||
else
|
||||
if (level >= STATIC_GAME_LOD_LOW)
|
||||
{ //normal non-custom level gets texture reduction based on recommendation
|
||||
requestedTextureReduction = getRecommendedTextureReduction();
|
||||
}
|
||||
|
||||
if (TheGlobalData)
|
||||
{
|
||||
TheWritableGlobalData->m_maxParticleCount=lodInfo->m_maxParticleCount;
|
||||
TheWritableGlobalData->m_useShadowVolumes=lodInfo->m_useShadowVolumes;
|
||||
TheWritableGlobalData->m_useShadowDecals=lodInfo->m_useShadowDecals;
|
||||
|
||||
//Check if texture resolution changed. No need to apply when current is unknown because display will do it
|
||||
if (requestedTextureReduction != m_currentTextureReduction)
|
||||
{
|
||||
TheWritableGlobalData->m_textureReductionFactor = requestedTextureReduction;
|
||||
if (TheGameClient)
|
||||
TheGameClient->adjustLOD(0); //apply the new setting stored in globaldata
|
||||
}
|
||||
|
||||
//Check if shadow state changed
|
||||
if (m_currentStaticLOD == STATIC_GAME_LOD_UNKNOWN ||
|
||||
lodInfo->m_useShadowVolumes != prevLodInfo->m_useShadowVolumes ||
|
||||
lodInfo->m_useShadowDecals != prevLodInfo->m_useShadowDecals)
|
||||
{
|
||||
if (TheGameClient)
|
||||
{
|
||||
TheGameClient->releaseShadows(); //free all shadows
|
||||
TheGameClient->allocateShadows(); //allocate those shadows that are enabled.
|
||||
}
|
||||
}
|
||||
|
||||
TheWritableGlobalData->m_useCloudMap=lodInfo->m_useCloudMap;
|
||||
TheWritableGlobalData->m_useLightMap=lodInfo->m_useLightMap;
|
||||
TheWritableGlobalData->m_showSoftWaterEdge=lodInfo->m_showSoftWaterEdge;
|
||||
//Check if shoreline blending mode has changed
|
||||
if (m_currentStaticLOD == STATIC_GAME_LOD_UNKNOWN || lodInfo->m_showSoftWaterEdge != prevLodInfo->m_showSoftWaterEdge)
|
||||
{
|
||||
if (TheTerrainVisual)
|
||||
TheTerrainVisual->setShoreLineDetail();
|
||||
}
|
||||
|
||||
TheWritableGlobalData->m_maxTankTrackEdges=lodInfo->m_maxTankTrackEdges;
|
||||
TheWritableGlobalData->m_maxTankTrackOpaqueEdges=lodInfo->m_maxTankTrackOpaqueEdges;
|
||||
TheWritableGlobalData->m_maxTankTrackFadeDelay=lodInfo->m_maxTankTrackFadeDelay;
|
||||
TheWritableGlobalData->m_useTreeSway=lodInfo->m_useTreeSway;
|
||||
TheWritableGlobalData->m_useDrawModuleLOD=!lodInfo->m_useBuildupScaffolds;
|
||||
TheWritableGlobalData->m_enableDynamicLOD = lodInfo->m_enableDynamicLOD;
|
||||
TheWritableGlobalData->m_useFpsLimit = lodInfo->m_useFpsLimit;
|
||||
TheWritableGlobalData->m_useTrees = requestedTrees;
|
||||
}
|
||||
if (!m_memPassed || isReallyLowMHz()) {
|
||||
TheWritableGlobalData->m_shellMapOn = false;
|
||||
}
|
||||
if (TheTerrainVisual)
|
||||
TheTerrainVisual->setTerrainTracksDetail();
|
||||
|
||||
}
|
||||
|
||||
/**Parse a description of all the LOD settings for a given detail level*/
|
||||
void INI::parseDynamicGameLODDefinition( INI* ini )
|
||||
{
|
||||
const char *c;
|
||||
AsciiString name;
|
||||
|
||||
// read the name
|
||||
c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
if( TheGameLODManager )
|
||||
{
|
||||
Int index = TheGameLODManager->getDynamicGameLODIndex(name);
|
||||
if (index != DYNAMIC_GAME_LOD_UNKNOWN)
|
||||
{
|
||||
DynamicGameLODInfo *lodInfo = &(TheGameLODManager->m_dynamicGameLODInfo[index]);
|
||||
|
||||
// parse the ini weapon definition
|
||||
ini->initFromINI( lodInfo, TheDynamicGameLODFieldParseTable );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**Parse an LOD level*/
|
||||
void INI::parseDynamicGameLODLevel( INI* ini, void * , void *store, const void*)
|
||||
{
|
||||
const char *tok=ini->getNextToken();
|
||||
for (Int i=0; i<DYNAMIC_GAME_LOD_COUNT; i++)
|
||||
if( stricmp(tok, DynamicGameLODNames[i]) == 0 )
|
||||
{ *(DynamicGameLODLevel*)store = (DynamicGameLODLevel)i;
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_CRASH(("invalid GameLODLevel token %s -- expected LOW/MEDIUM/HIGH\n",tok));
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
|
||||
/**Convert LOD name to an index*/
|
||||
Int GameLODManager::getDynamicGameLODIndex(AsciiString name)
|
||||
{
|
||||
for (Int i=0; i<DYNAMIC_GAME_LOD_COUNT; ++i)
|
||||
{
|
||||
if (name.compareNoCase(DynamicGameLODNames[i]) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
DEBUG_CRASH(( "GameLODManager::getGameLODIndex - Invalid LOD name '%s'\n", name.str() ));
|
||||
return STATIC_GAME_LOD_UNKNOWN;
|
||||
}
|
||||
|
||||
const char *GameLODManager::getDynamicGameLODLevelName(DynamicGameLODLevel level)
|
||||
{
|
||||
return DynamicGameLODNames[level];
|
||||
}
|
||||
|
||||
/**Given an average fps, return the optimal dynamic LOD level that matches this fps.*/
|
||||
DynamicGameLODLevel GameLODManager::findDynamicLODLevel(Real averageFPS)
|
||||
{
|
||||
Int ifps=(Int)(averageFPS); //convert to integer.
|
||||
|
||||
for (Int i=DYNAMIC_GAME_LOD_VERY_HIGH; i>=DYNAMIC_GAME_LOD_LOW; i--)
|
||||
{ //check which of the LOD levels matches our fps
|
||||
if (m_dynamicGameLODInfo[i].m_minFPS < ifps)
|
||||
return (DynamicGameLODLevel)i;
|
||||
}
|
||||
return DYNAMIC_GAME_LOD_LOW; //none of the low levels were slow enough so pick the lowest.
|
||||
}
|
||||
|
||||
/**Set all game systems to match the desired LOD level.*/
|
||||
Bool GameLODManager::setDynamicLODLevel(DynamicGameLODLevel level)
|
||||
{
|
||||
if (level == DYNAMIC_GAME_LOD_UNKNOWN || m_currentDynamicLOD == level)
|
||||
return FALSE;
|
||||
|
||||
m_currentDynamicLOD = level;
|
||||
|
||||
applyDynamicLODLevel(level);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void GameLODManager::applyDynamicLODLevel(DynamicGameLODLevel level)
|
||||
{
|
||||
m_numParticleGenerations=0;
|
||||
m_dynamicParticleSkipMask=m_dynamicGameLODInfo[level].m_dynamicParticleSkipMask;
|
||||
|
||||
m_numDebrisGenerations=0;
|
||||
m_dynamicDebrisSkipMask=m_dynamicGameLODInfo[level].m_dynamicDebrisSkipMask;
|
||||
|
||||
m_slowDeathScale=m_dynamicGameLODInfo[level].m_slowDeathScale;
|
||||
m_minDynamicParticlePriority=m_dynamicGameLODInfo[level].m_minDynamicParticlePriority;
|
||||
m_minDynamicParticleSkipPriority=m_dynamicGameLODInfo[level].m_minDynamicParticleSkipPriority;
|
||||
}
|
||||
|
||||
Int GameLODManager::getRecommendedTextureReduction(void)
|
||||
{
|
||||
if (m_idealDetailLevel == STATIC_GAME_LOD_UNKNOWN)
|
||||
findStaticLODLevel(); //it was never tested, so test now.
|
||||
|
||||
if (!m_memPassed) //if they have < 256 MB, force them to low res textures.
|
||||
return m_staticGameLODInfo[STATIC_GAME_LOD_LOW].m_textureReduction;
|
||||
|
||||
return m_staticGameLODInfo[m_idealDetailLevel].m_textureReduction;
|
||||
}
|
||||
|
||||
Int GameLODManager::getLevelTextureReduction(StaticGameLODLevel level)
|
||||
{
|
||||
return m_staticGameLODInfo[level].m_textureReduction;
|
||||
}
|
||||
|
||||
Bool GameLODManager::didMemPass( void )
|
||||
{
|
||||
return m_memPassed;
|
||||
}
|
||||
51
Generals/Code/GameEngine/Source/Common/GameMain.cpp
Normal file
51
Generals/Code/GameEngine/Source/Common/GameMain.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GameMain.cpp
|
||||
// The main entry point for the game
|
||||
// Author: Michael S. Booth, April 2001
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
|
||||
|
||||
#include "Common/GameEngine.h"
|
||||
|
||||
|
||||
/**
|
||||
* This is the entry point for the game system.
|
||||
*/
|
||||
void GameMain( int argc, char *argv[] )
|
||||
{
|
||||
// initialize the game engine using factory function
|
||||
TheGameEngine = CreateGameEngine();
|
||||
TheGameEngine->init(argc, argv);
|
||||
|
||||
// run it
|
||||
TheGameEngine->execute();
|
||||
|
||||
// since execute() returned, we are exiting the game
|
||||
delete TheGameEngine;
|
||||
TheGameEngine = NULL;
|
||||
|
||||
}
|
||||
|
||||
1220
Generals/Code/GameEngine/Source/Common/GlobalData.cpp
Normal file
1220
Generals/Code/GameEngine/Source/Common/GlobalData.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1975
Generals/Code/GameEngine/Source/Common/INI/INI.cpp
Normal file
1975
Generals/Code/GameEngine/Source/Common/INI/INI.cpp
Normal file
File diff suppressed because it is too large
Load Diff
52
Generals/Code/GameEngine/Source/Common/INI/INIAiData.cpp
Normal file
52
Generals/Code/GameEngine/Source/Common/INI/INIAiData.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIAiData.cpp //////////////////////////////////////////////////////////////////////////
|
||||
// Author: John Ahlquist, March 2002
|
||||
// Desc: Parsing AIData INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameLogic/AI.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse GameData entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseAIDataDefinition( INI* ini )
|
||||
{
|
||||
AI::parseAiDataDefinition(ini);
|
||||
}
|
||||
|
||||
|
||||
88
Generals/Code/GameEngine/Source/Common/INI/INIAnimation.cpp
Normal file
88
Generals/Code/GameEngine/Source/Common/INI/INIAnimation.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIAnimation.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, July 2002
|
||||
// Desc: Parsing animation INI entries for 2D image animations
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/Anim2D.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse animation entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseAnim2DDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
Anim2DTemplate *animTemplate;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
//
|
||||
// find existing item if present, note that we do not support overrides
|
||||
// in the animations like we do in systems that are more "design" oriented, images
|
||||
// are assets as they are
|
||||
//
|
||||
if( !TheAnim2DCollection )
|
||||
{
|
||||
|
||||
//We don't need it if we're in the builder... which doesn't have this.
|
||||
return;
|
||||
|
||||
} // end if
|
||||
|
||||
// find existing animation template if present
|
||||
animTemplate = TheAnim2DCollection->findTemplate( name );
|
||||
if( animTemplate == NULL )
|
||||
{
|
||||
|
||||
// item not found, create a new one
|
||||
animTemplate = TheAnim2DCollection->newTemplate( name );
|
||||
DEBUG_ASSERTCRASH( animTemplate, ("INI""parseAnim2DDefinition - unable to allocate animation template for '%s'\n",
|
||||
name.str()) );
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// we're loading over an existing animation template ... something is probably wrong
|
||||
DEBUG_CRASH(( "INI::parseAnim2DDefinition - Animation template '%s' already exists\n",
|
||||
animTemplate->getName().str() ));
|
||||
return;
|
||||
|
||||
} // end else
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( animTemplate, animTemplate->getFieldParse() );
|
||||
|
||||
} // end parseAnim2DDefinition
|
||||
|
||||
|
||||
|
||||
215
Generals/Code/GameEngine/Source/Common/INI/INIAudioEventInfo.cpp
Normal file
215
Generals/Code/GameEngine/Source/Common/INI/INIAudioEventInfo.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIAudioEventInfo.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, July 2002
|
||||
// Desc: Parsing AudioEvent, MusicTrack and DialogEvent INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/AudioEventInfo.h"
|
||||
|
||||
AudioEventInfo::~AudioEventInfo()
|
||||
{
|
||||
|
||||
}
|
||||
// @todo: Get these functions unified.
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseMusicTrackDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
AudioEventInfo *track;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
track = TheAudio->newAudioEventInfo( name );
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
|
||||
AudioEventInfo *defaultInfo = TheAudio->findAudioEventInfo("DefaultMusicTrack");
|
||||
if (defaultInfo) {
|
||||
(*track) = (*defaultInfo);
|
||||
TheAudio->addTrackName( name );
|
||||
}
|
||||
|
||||
track->m_audioName = name;
|
||||
track->m_soundType = AT_Music;
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( track, track->getFieldParse() );
|
||||
} // end parseMusicTrackDefinition
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseAudioEventDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
AudioEventInfo *track;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
track = TheAudio->newAudioEventInfo( name );
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
|
||||
AudioEventInfo *defaultInfo = TheAudio->findAudioEventInfo("DefaultSoundEffect");
|
||||
if (defaultInfo) {
|
||||
(*track) = (*defaultInfo);
|
||||
}
|
||||
|
||||
track->m_audioName = name;
|
||||
track->m_soundType = AT_SoundEffect;
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( track, track->getFieldParse() );
|
||||
} // end parseAudioEventDefinition
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseDialogDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
AudioEventInfo *track;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
track = TheAudio->newAudioEventInfo( name );
|
||||
if (!track) {
|
||||
return;
|
||||
}
|
||||
|
||||
AudioEventInfo *defaultInfo = TheAudio->findAudioEventInfo("DefaultDialog");
|
||||
if (defaultInfo) {
|
||||
(*track) = (*defaultInfo);
|
||||
}
|
||||
|
||||
track->m_audioName = name;
|
||||
track->m_soundType = AT_Streaming;
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( track, track->getFieldParse() );
|
||||
} // end parseAudioEventDefinition
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseDelay( INI* ini, void *instance, void *store, const void* /*userData*/ );
|
||||
static void parsePitchShift( INI* ini, void *instance, void *store, const void* /*userData*/ );
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const FieldParse AudioEventInfo::m_audioEventInfo[] =
|
||||
{
|
||||
{ "Filename", INI::parseAsciiString, NULL, offsetof( AudioEventInfo, m_filename) },
|
||||
{ "Volume", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_volume ) },
|
||||
{ "VolumeShift", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_volumeShift ) },
|
||||
{ "MinVolume", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_minVolume ) },
|
||||
{ "PitchShift", parsePitchShift, NULL, 0 },
|
||||
{ "Delay", parseDelay, NULL, 0 },
|
||||
{ "Limit", INI::parseInt, NULL, offsetof( AudioEventInfo, m_limit ) },
|
||||
{ "LoopCount", INI::parseInt, NULL, offsetof( AudioEventInfo, m_loopCount ) },
|
||||
{ "Priority", INI::parseIndexList, theAudioPriorityNames, offsetof( AudioEventInfo, m_priority ) },
|
||||
{ "Type", INI::parseBitString32, theSoundTypeNames, offsetof( AudioEventInfo, m_type ) },
|
||||
{ "Control", INI::parseBitString32, theAudioControlNames, offsetof( AudioEventInfo, m_control ) },
|
||||
{ "Sounds", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_sounds ) },
|
||||
{ "SoundsNight", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_soundsNight ) },
|
||||
{ "SoundsEvening", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_soundsEvening ) },
|
||||
{ "SoundsMorning", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_soundsMorning ) },
|
||||
{ "Attack", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_attackSounds ) },
|
||||
{ "Decay", INI::parseSoundsList, NULL, offsetof( AudioEventInfo, m_decaySounds ) },
|
||||
{ "MinRange", INI::parseReal, NULL, offsetof( AudioEventInfo, m_minDistance) },
|
||||
{ "MaxRange", INI::parseReal, NULL, offsetof( AudioEventInfo, m_maxDistance) },
|
||||
{ "LowPassCutoff", INI::parsePercentToReal, NULL, offsetof( AudioEventInfo, m_lowPassFreq) },
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parseDelay( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
AudioEventInfo *attribs = (AudioEventInfo*) store;
|
||||
|
||||
Int min = ini->scanInt(ini->getNextToken());
|
||||
Int max = ini->scanInt(ini->getNextToken());
|
||||
|
||||
DEBUG_ASSERTCRASH( min >= 0 && max >= min, ("Bad delay values for audio event %s", attribs->m_audioName.str()));
|
||||
attribs->m_delayMax = max;
|
||||
attribs->m_delayMin = min;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static void parsePitchShift( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
AudioEventInfo *attribs = (AudioEventInfo*) store;
|
||||
|
||||
Real min = ini->scanReal(ini->getNextToken());
|
||||
Real max = ini->scanReal(ini->getNextToken());
|
||||
|
||||
DEBUG_ASSERTCRASH( min > -100 && max >= min, ("Bad pitch shift values for audio event %s", attribs->m_audioName.str()));
|
||||
attribs->m_pitchShiftMin = 1.0f + min/100;
|
||||
attribs->m_pitchShiftMax = 1.0f + max/100;
|
||||
}
|
||||
|
||||
// STATIC DEFINIITIONS ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char *theAudioPriorityNames[] =
|
||||
{
|
||||
"LOWEST",
|
||||
"LOW",
|
||||
"NORMAL",
|
||||
"HIGH",
|
||||
"CRITICAL",
|
||||
NULL
|
||||
};
|
||||
|
||||
char *theSoundTypeNames[] =
|
||||
{
|
||||
"UI",
|
||||
"WORLD",
|
||||
"SHROUDED",
|
||||
"GLOBAL",
|
||||
"VOICE",
|
||||
"PLAYER",
|
||||
"ALLIES",
|
||||
"ENEMIES",
|
||||
"EVERYONE",
|
||||
NULL
|
||||
};
|
||||
|
||||
char *theAudioControlNames[] =
|
||||
{
|
||||
"LOOP",
|
||||
"RANDOM",
|
||||
"ALL",
|
||||
"POSTDELAY",
|
||||
"INTERRUPT",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INICommandButton.cpp /////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, March 2002
|
||||
// Desc: Command buttons are the atomic units we can configure into command sets to then
|
||||
// display in the context sensitive user interface
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/ControlBar.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse a command button */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseCommandButtonDefinition( INI *ini )
|
||||
{
|
||||
ControlBar::parseCommandButtonDefinition(ini);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse a command button */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ControlBar::parseCommandButtonDefinition( INI *ini )
|
||||
{
|
||||
// read the name
|
||||
AsciiString name = ini->getNextToken();
|
||||
|
||||
// find existing item if present
|
||||
CommandButton *button = TheControlBar->findNonConstCommandButton( name );
|
||||
if( button == NULL )
|
||||
{
|
||||
// allocate a new item
|
||||
button = TheControlBar->newCommandButton( name );
|
||||
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
|
||||
{
|
||||
button->markAsOverride();
|
||||
}
|
||||
} // end if
|
||||
else if( ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES )
|
||||
{
|
||||
DEBUG_CRASH(( "[LINE: %d in '%s'] Duplicate commandbutton %s found!", ini->getLineNum(), ini->getFilename().str(), name.str() ));
|
||||
}
|
||||
else
|
||||
{
|
||||
button = TheControlBar->newCommandButtonOverride( button );
|
||||
}
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( button, button->getFieldParse() );
|
||||
|
||||
} // end parseCommandButtonDefinition
|
||||
|
||||
|
||||
43
Generals/Code/GameEngine/Source/Common/INI/INICommandSet.cpp
Normal file
43
Generals/Code/GameEngine/Source/Common/INI/INICommandSet.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INICommandSet.cpp ////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, March 2002
|
||||
// Desc: Command sets are a configurable set of CommandButtons, we will use the sets as
|
||||
// part of the context sensitive user interface
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/ControlBar.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse a command set */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseCommandSetDefinition( INI *ini )
|
||||
{
|
||||
ControlBar::parseCommandSetDefinition(ini);
|
||||
} // end parseCommandSetDefinition
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIControlBarScheme.cpp /////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Electronic Arts Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// created: Apr 2002
|
||||
//
|
||||
// Filename: INIControlBarScheme.cpp
|
||||
//
|
||||
// author: Chris Huybregts
|
||||
//
|
||||
// purpose: Parse a control Bar Scheme
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/ControlBar.h"
|
||||
#include "GameClient/ControlBarScheme.h"
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEFINES ////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse a ControlBarScheme button */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseControlBarSchemeDefinition( INI *ini )
|
||||
{
|
||||
AsciiString name;
|
||||
ControlBarSchemeManager *CBSchemeManager;
|
||||
ControlBarScheme *CBScheme;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
// find existing item if present
|
||||
CBSchemeManager = TheControlBar->getControlBarSchemeManager();
|
||||
DEBUG_ASSERTCRASH( CBSchemeManager, ("parseControlBarSchemeDefinition: Unable to Get CBSchemeManager\n") );
|
||||
if( !CBSchemeManager )
|
||||
return;
|
||||
|
||||
// If we have a previously allocated control bar, this will return a cleared out pointer to it so we
|
||||
// can overwrite it
|
||||
CBScheme = CBSchemeManager->newControlBarScheme( name );
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( CBScheme, ("parseControlBarSchemeDefinition: Unable to allocate Scheme '%s'\n", name.str()) );
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( CBScheme, CBSchemeManager->getFieldParse() );
|
||||
|
||||
} // end parseCommandButtonDefinition
|
||||
|
||||
|
||||
46
Generals/Code/GameEngine/Source/Common/INI/INICrate.cpp
Normal file
46
Generals/Code/GameEngine/Source/Common/INI/INICrate.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INICrate.cpp /////////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Graham Smallwood Feb 2002
|
||||
// Desc: Just passes the parse to the CrateSystem
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameLogic/CrateSystem.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Weapon entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseCrateTemplateDefinition( INI* ini )
|
||||
{
|
||||
CrateSystem::parseCrateTemplateDefinition(ini);
|
||||
}
|
||||
44
Generals/Code/GameEngine/Source/Common/INI/INIDamageFX.cpp
Normal file
44
Generals/Code/GameEngine/Source/Common/INI/INIDamageFX.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIDamageFX.cpp ////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, November 2001
|
||||
// Desc: Parsing DamageFX INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/DamageFX.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*static */ void INI::parseDamageFXDefinition(INI* ini)
|
||||
{
|
||||
DamageFXStore::parseDamageFXDefinition(ini);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIDrawGroupInfo.cpp /////////////////////////////////////////////////////////////////////
|
||||
// Author: John McDonald, October 2002
|
||||
// Desc: Parsing DrawGroupInfo INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/DrawGroupInfo.h"
|
||||
|
||||
void parseInt( INI* ini, void * /*instance*/, void *store, const void* userData )
|
||||
{
|
||||
DrawGroupInfo *dgi = (DrawGroupInfo*) store;
|
||||
if (userData == 0) {
|
||||
store = &dgi->m_pixelOffsetX;
|
||||
dgi->m_usingPixelOffsetX = TRUE;
|
||||
} else {
|
||||
store = &dgi->m_pixelOffsetY;
|
||||
dgi->m_usingPixelOffsetY = TRUE;
|
||||
}
|
||||
|
||||
INI::parseInt(ini, NULL, store, NULL);
|
||||
}
|
||||
|
||||
void parsePercentToReal( INI* ini, void * /*instance*/, void *store, const void* userData )
|
||||
{
|
||||
DrawGroupInfo *dgi = (DrawGroupInfo*) store;
|
||||
if (userData == 0) {
|
||||
store = &dgi->m_pixelOffsetX;
|
||||
dgi->m_usingPixelOffsetX = FALSE;
|
||||
} else {
|
||||
store = &dgi->m_pixelOffsetY;
|
||||
dgi->m_usingPixelOffsetY = FALSE;
|
||||
}
|
||||
|
||||
INI::parsePercentToReal(ini, NULL, store, NULL);
|
||||
}
|
||||
|
||||
const FieldParse DrawGroupInfo::s_fieldParseTable[] =
|
||||
{
|
||||
{ "UsePlayerColor", INI::parseBool, NULL, offsetof( DrawGroupInfo, m_usePlayerColor) },
|
||||
{ "ColorForText", INI::parseColorInt, NULL, offsetof( DrawGroupInfo, m_colorForText ) },
|
||||
{ "ColorForTextDropShadow", INI::parseColorInt, NULL, offsetof( DrawGroupInfo, m_colorForTextDropShadow ) },
|
||||
|
||||
{ "FontName", INI::parseQuotedAsciiString, NULL, offsetof( DrawGroupInfo, m_fontName ) },
|
||||
{ "FontSize", INI::parseInt, NULL, offsetof( DrawGroupInfo, m_fontSize ) },
|
||||
{ "FontIsBold", INI::parseBool, NULL, offsetof( DrawGroupInfo, m_fontIsBold ) },
|
||||
{ "DropShadowOffsetX", INI::parseInt, NULL, offsetof( DrawGroupInfo, m_dropShadowOffsetX) },
|
||||
{ "DropShadowOffsetY", INI::parseInt, NULL, offsetof( DrawGroupInfo, m_dropShadowOffsetY) },
|
||||
{ "DrawPositionXPixel", parseInt, (void*)0, 0 },
|
||||
{ "DrawPositionXPercent", parsePercentToReal, (void*)0, 0 },
|
||||
{ "DrawPositionYPixel", parseInt, (void*)1, 0 },
|
||||
{ "DrawPositionYPercent", parsePercentToReal, (void*)1, 0 },
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/*static */ void INI::parseDrawGroupNumberDefinition(INI* ini)
|
||||
{
|
||||
if (!TheDrawGroupInfo) {
|
||||
throw INI_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
ini->initFromINI(TheDrawGroupInfo, TheDrawGroupInfo->getFieldParse());
|
||||
}
|
||||
|
||||
52
Generals/Code/GameEngine/Source/Common/INI/INIGameData.cpp
Normal file
52
Generals/Code/GameEngine/Source/Common/INI/INIGameData.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIGameData.cpp //////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Parsing GameData INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/GlobalData.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse GameData entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseGameDataDefinition( INI* ini )
|
||||
{
|
||||
GlobalData::parseGameDataDefinition(ini);
|
||||
}
|
||||
|
||||
|
||||
170
Generals/Code/GameEngine/Source/Common/INI/INIMapCache.cpp
Normal file
170
Generals/Code/GameEngine/Source/Common/INI/INIMapCache.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIMapCache.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell, February 2002
|
||||
// Desc: Parsing MapCache INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Lib/BaseType.h"
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameNetwork/NetworkDefs.h"
|
||||
#include "Common/NameKeyGenerator.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/QuotedPrintable.h"
|
||||
|
||||
class MapMetaDataReader
|
||||
{
|
||||
public:
|
||||
Region3D m_extent;
|
||||
Int m_numPlayers;
|
||||
Bool m_isMultiplayer;
|
||||
AsciiString m_asciiDisplayName;
|
||||
|
||||
Bool m_isOfficial;
|
||||
WinTimeStamp m_timestamp;
|
||||
UnsignedInt m_filesize;
|
||||
UnsignedInt m_CRC;
|
||||
|
||||
Coord3D m_waypoints[MAX_SLOTS];
|
||||
Coord3D m_initialCameraPosition;
|
||||
Coord3DList m_supplyPositions;
|
||||
Coord3DList m_techPositions;
|
||||
static const FieldParse m_mapFieldParseTable[]; ///< the parse table for INI definition
|
||||
const FieldParse *getFieldParse( void ) const { return m_mapFieldParseTable; }
|
||||
};
|
||||
|
||||
|
||||
void parseSupplyPositionCoord3D( INI* ini, void * instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
MapMetaDataReader *mmdr = (MapMetaDataReader *)instance;
|
||||
Coord3D coord3d;
|
||||
INI::parseCoord3D(ini, NULL, &coord3d,NULL );
|
||||
mmdr->m_supplyPositions.push_front(coord3d);
|
||||
|
||||
}
|
||||
|
||||
void parseTechPositionsCoord3D( INI* ini, void * instance, void * /*store*/, const void* /*userData*/ )
|
||||
{
|
||||
MapMetaDataReader *mmdr = (MapMetaDataReader *)instance;
|
||||
Coord3D coord3d;
|
||||
INI::parseCoord3D(ini, NULL, &coord3d,NULL );
|
||||
mmdr->m_techPositions.push_front(coord3d);
|
||||
|
||||
}
|
||||
|
||||
const FieldParse MapMetaDataReader::m_mapFieldParseTable[] =
|
||||
{
|
||||
|
||||
{ "isOfficial", INI::parseBool, NULL, offsetof( MapMetaDataReader, m_isOfficial ) },
|
||||
{ "isMultiplayer", INI::parseBool, NULL, offsetof( MapMetaDataReader, m_isMultiplayer ) },
|
||||
{ "extentMin", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_extent.lo ) },
|
||||
{ "extentMax", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_extent.hi ) },
|
||||
{ "numPlayers", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_numPlayers ) },
|
||||
{ "fileSize", INI::parseUnsignedInt, NULL, offsetof( MapMetaDataReader, m_filesize ) },
|
||||
{ "fileCRC", INI::parseUnsignedInt, NULL, offsetof( MapMetaDataReader, m_CRC ) },
|
||||
{ "timestampLo", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_timestamp.m_lowTimeStamp ) },
|
||||
{ "timestampHi", INI::parseInt, NULL, offsetof( MapMetaDataReader, m_timestamp.m_highTimeStamp ) },
|
||||
{ "displayName", INI::parseAsciiString, NULL, offsetof( MapMetaDataReader, m_asciiDisplayName ) },
|
||||
|
||||
{ "supplyPosition", parseSupplyPositionCoord3D, NULL, NULL },
|
||||
{ "techPosition", parseTechPositionsCoord3D, NULL, NULL },
|
||||
|
||||
{ "Player_1_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) },
|
||||
{ "Player_2_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 1 },
|
||||
{ "Player_3_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 2 },
|
||||
{ "Player_4_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 3 },
|
||||
{ "Player_5_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 4 },
|
||||
{ "Player_6_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 5 },
|
||||
{ "Player_7_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 6 },
|
||||
{ "Player_8_Start", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_waypoints ) + sizeof(Coord3D) * 7 },
|
||||
|
||||
{ "InitialCameraPosition", INI::parseCoord3D, NULL, offsetof( MapMetaDataReader, m_initialCameraPosition ) },
|
||||
|
||||
{ NULL, NULL, NULL, 0 } // keep this last
|
||||
|
||||
};
|
||||
|
||||
void INI::parseMapCacheDefinition( INI* ini )
|
||||
{
|
||||
const char *c;
|
||||
AsciiString name;
|
||||
MapMetaDataReader mdr;
|
||||
MapMetaData md;
|
||||
|
||||
// read the name
|
||||
c = ini->getNextToken(" \n\r\t");
|
||||
name.set( c );
|
||||
name = QuotedPrintableToAsciiString(name);
|
||||
md.m_waypoints.clear();
|
||||
|
||||
ini->initFromINI( &mdr, mdr.getFieldParse() );
|
||||
|
||||
md.m_extent = mdr.m_extent;
|
||||
md.m_isOfficial = mdr.m_isOfficial != 0;
|
||||
md.m_isMultiplayer = mdr.m_isMultiplayer != 0;
|
||||
md.m_numPlayers = mdr.m_numPlayers;
|
||||
md.m_filesize = mdr.m_filesize;
|
||||
md.m_CRC = mdr.m_CRC;
|
||||
md.m_timestamp = mdr.m_timestamp;
|
||||
|
||||
md.m_waypoints[TheNameKeyGenerator->keyToName(TheKey_InitialCameraPosition)] = mdr.m_initialCameraPosition;
|
||||
|
||||
md.m_displayName = QuotedPrintableToUnicodeString(mdr.m_asciiDisplayName);
|
||||
|
||||
AsciiString startingCamName;
|
||||
for (Int i=0; i<md.m_numPlayers; ++i)
|
||||
{
|
||||
startingCamName.format("Player_%d_Start", i+1); // start pos waypoints are 1-based
|
||||
md.m_waypoints[startingCamName] = mdr.m_waypoints[i];
|
||||
}
|
||||
|
||||
Coord3DList::iterator it = mdr.m_supplyPositions.begin();
|
||||
while( it != mdr.m_supplyPositions.end())
|
||||
{
|
||||
md.m_supplyPositions.push_front(*it);
|
||||
it++;
|
||||
}
|
||||
|
||||
it = mdr.m_techPositions.begin();
|
||||
while( it != mdr.m_techPositions.end())
|
||||
{
|
||||
md.m_techPositions.push_front(*it);
|
||||
it++;
|
||||
}
|
||||
|
||||
if(TheMapCache && !md.m_displayName.isEmpty())
|
||||
{
|
||||
AsciiString lowerName = name;
|
||||
lowerName.toLower();
|
||||
md.m_fileName = lowerName;
|
||||
// DEBUG_LOG(("INI::parseMapCacheDefinition - adding %s to map cache\n", lowerName.str()));
|
||||
(*TheMapCache)[lowerName] = md;
|
||||
}
|
||||
}
|
||||
|
||||
50
Generals/Code/GameEngine/Source/Common/INI/INIMapData.cpp
Normal file
50
Generals/Code/GameEngine/Source/Common/INI/INIMapData.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIMapData.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Parsing MapData INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse MapData entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseMapDataDefinition( INI* ini )
|
||||
{
|
||||
} // end parseMapData
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIMappedImage.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, December 2001
|
||||
// Desc: Mapped image INI parsing
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/Image.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse mapped image entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseMappedImageDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
//
|
||||
// find existing item if present, note that we do not support overrides
|
||||
// in the images like we do in systems that are more "design" oriented, images
|
||||
// are assets as they are
|
||||
//
|
||||
if( !TheMappedImageCollection )
|
||||
{
|
||||
//We don't need it if we're in the builder... which doesn't have this.
|
||||
return;
|
||||
}
|
||||
Image *image = const_cast<Image*>(TheMappedImageCollection->findImageByName( name ));
|
||||
if(image)
|
||||
DEBUG_ASSERTCRASH(!image->getRawTextureData(), ("We are trying to parse over an existing image that contains a non-null rawTextureData, you should fix that"));
|
||||
|
||||
if( image == NULL )
|
||||
{
|
||||
|
||||
// image not found, create a new one
|
||||
image = TheMappedImageCollection->newImage();
|
||||
image->setName( name );
|
||||
DEBUG_ASSERTCRASH( image, ("parseMappedImage: unable to allocate image for '%s'\n",
|
||||
name.str()) );
|
||||
|
||||
} // end if
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( image, image->getFieldParse());
|
||||
|
||||
} // end parseMappedImage
|
||||
76
Generals/Code/GameEngine/Source/Common/INI/INIMiscAudio.cpp
Normal file
76
Generals/Code/GameEngine/Source/Common/INI/INIMiscAudio.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/INI.h"
|
||||
|
||||
const FieldParse MiscAudio::m_fieldParseTable[] =
|
||||
{
|
||||
{ "RadarNotifyUnitUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarUnitUnderAttackSound ) },
|
||||
{ "RadarNotifyHarvesterUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarHarvesterUnderAttackSound ) },
|
||||
{ "RadarNotifyStructureUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarStructureUnderAttackSound ) },
|
||||
{ "RadarNotifyUnderAttackSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarUnderAttackSound ) },
|
||||
{ "RadarNotifyInfiltrationSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarInfiltrationSound ) },
|
||||
{ "RadarNotifyOnlineSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarOnlineSound ) },
|
||||
{ "RadarNotifyOfflineSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_radarOfflineSound ) },
|
||||
{ "DefectorTimerTickSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_defectorTimerTickSound ) },
|
||||
{ "DefectorTimerDingSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_defectorTimerDingSound ) },
|
||||
{ "LockonTickSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_lockonTickSound ) },
|
||||
{ "AllCheerSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_allCheerSound ) },
|
||||
{ "BattleCrySound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_battleCrySound ) },
|
||||
{ "GUIClickSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_guiClickSound ) },
|
||||
{ "NoCanDoSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_noCanDoSound ) },
|
||||
{ "StealthDiscoveredSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_stealthDiscoveredSound ) },
|
||||
{ "StealthNeutralizedSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_stealthNeutralizedSound ) },
|
||||
{ "MoneyDepositSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_moneyDepositSound ) },
|
||||
{ "MoneyWithdrawSound", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_moneyWithdrawSound ) },
|
||||
{ "BuildingDisabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_buildingDisabled ) },
|
||||
{ "BuildingReenabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_buildingReenabled ) },
|
||||
{ "VehicleDisabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_vehicleDisabled ) },
|
||||
{ "VehicleReenabled", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_vehicleReenabled ) },
|
||||
{ "SplatterVehiclePilotsBrain", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_splatterVehiclePilotsBrain ) },
|
||||
{ "TerroristInCarMoveVoice", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_terroristInCarMoveVoice ) },
|
||||
{ "TerroristInCarAttackVoice", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_terroristInCarAttackVoice ) },
|
||||
{ "TerroristInCarSelectVoice", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_terroristInCarSelectVoice ) },
|
||||
{ "CrateHeal", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateHeal ) },
|
||||
{ "CrateShroud", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateShroud ) },
|
||||
{ "CrateSalvage", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateSalvage ) },
|
||||
{ "CrateFreeUnit", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateFreeUnit ) },
|
||||
{ "CrateMoney", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_crateMoney ) },
|
||||
{ "UnitPromoted", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_unitPromoted ) },
|
||||
{ "RepairSparks", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_repairSparks ) },
|
||||
{ "AircraftWheelScreech", INI::parseAudioEventRTS, NULL, offsetof( MiscAudio, m_aircraftWheelScreech ) },
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseMiscAudio( INI *ini )
|
||||
{
|
||||
ini->initFromINI(TheAudio->friend_getMiscAudio(), MiscAudio::m_fieldParseTable);
|
||||
}
|
||||
42
Generals/Code/GameEngine/Source/Common/INI/INIModel.cpp
Normal file
42
Generals/Code/GameEngine/Source/Common/INI/INIModel.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIModel.cpp /////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Parsing Model INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIMultiplayer.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell, January 2002
|
||||
// Desc: Parsing MultiplayerSettings and MultiplayerColor INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
|
||||
void INI::parseMultiplayerSettingsDefinition( INI* ini )
|
||||
{
|
||||
if( TheMultiplayerSettings )
|
||||
{
|
||||
//
|
||||
// if the type of loading we're doing creates override data, we need to
|
||||
// be loading into a new override item
|
||||
//
|
||||
if( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(false, ("Creating an override of MultiplayerSettings!"));
|
||||
}
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
// we don't have any multiplayer settings instance at all yet, create one
|
||||
TheMultiplayerSettings = NEW MultiplayerSettings;
|
||||
} // end else
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( TheMultiplayerSettings, TheMultiplayerSettings->getFieldParse() );
|
||||
}
|
||||
|
||||
void INI::parseMultiplayerColorDefinition( INI* ini )
|
||||
{
|
||||
const char *c;
|
||||
AsciiString name;
|
||||
MultiplayerColorDefinition *multiplayerColorDefinition;
|
||||
|
||||
// read the name
|
||||
c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
// find existing item if present, but this type does not allow overrides,
|
||||
//so if it exists just overwrite it.
|
||||
multiplayerColorDefinition = TheMultiplayerSettings->findMultiplayerColorDefinitionByName( name );
|
||||
if( multiplayerColorDefinition == NULL )
|
||||
multiplayerColorDefinition = TheMultiplayerSettings->newMultiplayerColorDefinition( name );
|
||||
|
||||
ini->initFromINI( multiplayerColorDefinition, multiplayerColorDefinition->getFieldParse() );
|
||||
|
||||
multiplayerColorDefinition->setColor(multiplayerColorDefinition->getRGBValue());
|
||||
multiplayerColorDefinition->setNightColor(multiplayerColorDefinition->getRGBNightValue());
|
||||
}
|
||||
|
||||
61
Generals/Code/GameEngine/Source/Common/INI/INIObject.cpp
Normal file
61
Generals/Code/GameEngine/Source/Common/INI/INIObject.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIObject.cpp ////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Parsing Object INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "GameLogic/Module/OpenContain.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Object entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseObjectDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name = ini->getNextToken();
|
||||
ThingFactory::parseObjectDefinition(ini, name, AsciiString::TheEmptyString);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Object entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseObjectReskinDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name = ini->getNextToken();
|
||||
AsciiString reskinFrom = ini->getNextToken();
|
||||
ThingFactory::parseObjectDefinition(ini, name, reskinFrom);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIParticleSys.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Michael S. Booth, November 2001
|
||||
// Desc: Parsing Particle System INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/ParticleSys.h"
|
||||
|
||||
|
||||
/**
|
||||
* Parse entry
|
||||
*/
|
||||
void INI::parseParticleSystemDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
// find existing item if present
|
||||
ParticleSystemTemplate *sysTemplate = const_cast<ParticleSystemTemplate*>(TheParticleSystemManager->findTemplate( name ));
|
||||
if (sysTemplate == NULL)
|
||||
{
|
||||
// no item is present, create a new one
|
||||
sysTemplate = TheParticleSystemManager->newTemplate( name );
|
||||
}
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( sysTemplate, sysTemplate->getFieldParse() );
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INISpecialPower.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, April 2002
|
||||
// Desc: Special Power INI database
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/SpecialPower.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseSpecialPowerDefinition( INI *ini )
|
||||
{
|
||||
SpecialPowerStore::parseSpecialPowerDefinition(ini);
|
||||
}
|
||||
|
||||
|
||||
|
||||
62
Generals/Code/GameEngine/Source/Common/INI/INITerrain.cpp
Normal file
62
Generals/Code/GameEngine/Source/Common/INI/INITerrain.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INITerrain.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, December 2001
|
||||
// Desc: Terrain type INI loading
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/TerrainTypes.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Terrain type entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseTerrainDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
TerrainType *terrainType;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
// find existing item if present
|
||||
terrainType = TheTerrainTypes->findTerrain( name );
|
||||
if( terrainType == NULL )
|
||||
terrainType = TheTerrainTypes->newTerrain( name );
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( terrainType, ("Unable to allocate terrain type '%s'\n", name.str()) );
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( terrainType, terrainType->getFieldParse() );
|
||||
|
||||
} // end parseTerrain
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INITerrainBridge.cpp /////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, December 2001
|
||||
// Desc: Terrain bridge INI loading
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/TerrainRoads.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Terrain Bridge entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseTerrainBridgeDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
TerrainRoadType *bridge;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
// find existing item if present or allocate new one
|
||||
bridge = TheTerrainRoads->findBridge( name );
|
||||
|
||||
// if item is found it better already be a bridge
|
||||
if( bridge )
|
||||
{
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( bridge->isBridge(), ("Redefining road '%s' as a bridge!\n",
|
||||
bridge->getName().str()) );
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
if( bridge == NULL )
|
||||
bridge = TheTerrainRoads->newBridge( name );
|
||||
|
||||
DEBUG_ASSERTCRASH( bridge, ("Unable to allcoate bridge '%s'\n", name.str()) );
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( bridge, bridge->getBridgeFieldParse() );
|
||||
|
||||
} // end parseTerrainBridge
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INITerrainRoad.cpp ///////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, December 2001
|
||||
// Desc: Terrain road INI loading
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/TerrainRoads.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Terrain Road entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseTerrainRoadDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
TerrainRoadType *road;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
name.set( c );
|
||||
|
||||
// find existing item if present or allocate new one
|
||||
road = TheTerrainRoads->findRoad( name );
|
||||
|
||||
// if item is found it better not already be a bridge
|
||||
if( road )
|
||||
{
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( road->isBridge() == FALSE, ("Redefining bridge '%s' as a road!\n",
|
||||
road->getName().str()) );
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
if( road == NULL )
|
||||
road = TheTerrainRoads->newRoad( name );
|
||||
|
||||
DEBUG_ASSERTCRASH( road, ("Unable to allocate road '%s'\n", name.str()) );
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( road, road->getRoadFieldParse() );
|
||||
|
||||
} // end parseTerrainRoad
|
||||
|
||||
|
||||
|
||||
|
||||
45
Generals/Code/GameEngine/Source/Common/INI/INIUpgrade.cpp
Normal file
45
Generals/Code/GameEngine/Source/Common/INI/INIUpgrade.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIUpgrade.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, March 2002
|
||||
// Desc: Upgrade database
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/Upgrade.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse an upgrade definition */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseUpgradeDefinition( INI *ini )
|
||||
{
|
||||
UpgradeCenter::parseUpgradeDefinition(ini);
|
||||
}
|
||||
|
||||
|
||||
|
||||
61
Generals/Code/GameEngine/Source/Common/INI/INIVideo.cpp
Normal file
61
Generals/Code/GameEngine/Source/Common/INI/INIVideo.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIVideo.cpp /////////////////////////////////////////////////////////////////////////////
|
||||
// Author: John McDonald, February 2002
|
||||
// Desc: Parsing Video INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameClient/VideoPlayer.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Music entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseVideoDefinition( INI* ini )
|
||||
{
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
|
||||
Video video;
|
||||
video.m_internalName.set( c );
|
||||
|
||||
ini->initFromINI(&video, TheVideoPlayer->getFieldParse() );
|
||||
TheVideoPlayer->addVideo(&video);
|
||||
|
||||
|
||||
} // end parseVideoDefinition
|
||||
|
||||
|
||||
144
Generals/Code/GameEngine/Source/Common/INI/INIWater.cpp
Normal file
144
Generals/Code/GameEngine/Source/Common/INI/INIWater.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIWater.cpp /////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, December 2001
|
||||
// Desc: Water settings
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_TIME_OF_DAY_NAMES
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/GameType.h"
|
||||
|
||||
#include "GameClient/TerrainVisual.h"
|
||||
#include "GameClient/Water.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Water setting, note that this does not support override situations. As the water
|
||||
* system becomes more complex we may want to change this */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseWaterSettingDefinition( INI* ini )
|
||||
{
|
||||
AsciiString name;
|
||||
WaterSetting *waterSetting = NULL;
|
||||
|
||||
// read the name
|
||||
const char* token = ini->getNextToken();
|
||||
|
||||
name.set( token );
|
||||
|
||||
// get the water setting we want to load based on name
|
||||
char **timeOfDayName = TimeOfDayNames;
|
||||
Int timeOfDayIndex = 0; // TIME_OF_DAY_INVALID
|
||||
while( timeOfDayName && *timeOfDayName )
|
||||
{
|
||||
|
||||
if( stricmp( *timeOfDayName, name.str() ) == 0 )
|
||||
{
|
||||
|
||||
waterSetting = &WaterSettings[ timeOfDayIndex ];
|
||||
break;
|
||||
|
||||
} // end if
|
||||
|
||||
// next name
|
||||
timeOfDayName++;
|
||||
timeOfDayIndex++;
|
||||
|
||||
} // end while
|
||||
|
||||
// check for no time of day match
|
||||
if( waterSetting == NULL )
|
||||
throw INI_INVALID_DATA;
|
||||
|
||||
// parse the data
|
||||
ini->initFromINI( waterSetting, waterSetting->getFieldParse() );
|
||||
|
||||
} // end parseWaterSetting
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseWaterTransparencyDefinition( INI *ini )
|
||||
{
|
||||
if (TheWaterTransparency == NULL) {
|
||||
TheWaterTransparency = newInstance(WaterTransparencySetting);
|
||||
} else if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) {
|
||||
WaterTransparencySetting* wt = (WaterTransparencySetting*) (TheWaterTransparency.getNonOverloadedPointer());
|
||||
WaterTransparencySetting* wtOverride = newInstance(WaterTransparencySetting);
|
||||
*wtOverride = *wt;
|
||||
|
||||
// Mark that it is an override.
|
||||
wtOverride->markAsOverride();
|
||||
|
||||
wt->friend_getFinalOverride()->setNextOverride(wtOverride);
|
||||
} else {
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
|
||||
WaterTransparencySetting* waterTrans = (WaterTransparencySetting*) (TheWaterTransparency.getNonOverloadedPointer());
|
||||
waterTrans = (WaterTransparencySetting*) (waterTrans->friend_getFinalOverride());
|
||||
// parse the data
|
||||
ini->initFromINI( waterTrans, TheWaterTransparency->getFieldParse() );
|
||||
|
||||
// If we overrode any skybox textures, then call the W3D Water stuff.
|
||||
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) {
|
||||
// Check to see if we overrode any skybox textures.
|
||||
// If we did, then we need to replace them in the model.
|
||||
// Copy/Paste monkeys PLEASE TAKE NOTE. This technique only works for the skybox because we
|
||||
// know that there will never be more than one sky box. If you were to use this technique for
|
||||
// technicals, for instance, it would make all technicals in the level have the same new
|
||||
// texture.
|
||||
|
||||
const WaterTransparencySetting* wtOriginal = TheWaterTransparency.getNonOverloadedPointer();
|
||||
OVERRIDE<WaterTransparencySetting> wtOverride = TheWaterTransparency;
|
||||
|
||||
if (wtOriginal == wtOverride)
|
||||
return;
|
||||
|
||||
const AsciiString *oldTextures[5],*newTextures[5];
|
||||
|
||||
//Copy current texture names into arrays
|
||||
oldTextures[0]=&wtOriginal->m_skyboxTextureN;
|
||||
newTextures[0]=&wtOverride->m_skyboxTextureN;
|
||||
oldTextures[1]=&wtOriginal->m_skyboxTextureE;
|
||||
newTextures[1]=&wtOverride->m_skyboxTextureE;
|
||||
oldTextures[2]=&wtOriginal->m_skyboxTextureS;
|
||||
newTextures[2]=&wtOverride->m_skyboxTextureS;
|
||||
oldTextures[3]=&wtOriginal->m_skyboxTextureW;
|
||||
newTextures[3]=&wtOverride->m_skyboxTextureW;
|
||||
oldTextures[4]=&wtOriginal->m_skyboxTextureT;
|
||||
newTextures[4]=&wtOverride->m_skyboxTextureT;
|
||||
|
||||
TheTerrainVisual->replaceSkyboxTextures(oldTextures, newTextures);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
Generals/Code/GameEngine/Source/Common/INI/INIWeapon.cpp
Normal file
48
Generals/Code/GameEngine/Source/Common/INI/INIWeapon.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIWeapon.cpp ////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, November 2001
|
||||
// Desc: Parsing Weapon INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "GameLogic/Weapon.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Weapon entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseWeaponTemplateDefinition( INI* ini )
|
||||
{
|
||||
WeaponStore::parseWeaponTemplateDefinition(ini);
|
||||
}
|
||||
|
||||
|
||||
128
Generals/Code/GameEngine/Source/Common/INI/INIWebpageURL.cpp
Normal file
128
Generals/Code/GameEngine/Source/Common/INI/INIWebpageURL.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: INIWebpageURL.cpp /////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Bryan Cleveland, November 2001
|
||||
// Desc: Parsing Webpage URL INI entries
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/Registry.h"
|
||||
#include "GameNetwork/WOLBrowser/WebBrowser.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AsciiString encodeURL(AsciiString source)
|
||||
{
|
||||
if (source.isEmpty())
|
||||
{
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
AsciiString target;
|
||||
AsciiString allowedChars = "$-_.+!*'(),\\";
|
||||
const char *ptr = source.str();
|
||||
while (*ptr)
|
||||
{
|
||||
if (isalnum(*ptr) || allowedChars.find(*ptr))
|
||||
{
|
||||
target.concat(*ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
AsciiString tmp;
|
||||
target.concat('%');
|
||||
tmp.format("%2.2x", ((int)*ptr));
|
||||
target.concat(tmp);
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse Music entry */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parseWebpageURLDefinition( INI* ini )
|
||||
{
|
||||
AsciiString tag;
|
||||
WebBrowserURL *url;
|
||||
|
||||
// read the name
|
||||
const char* c = ini->getNextToken();
|
||||
tag.set( c );
|
||||
|
||||
if (TheWebBrowser != NULL)
|
||||
{
|
||||
url = TheWebBrowser->findURL(tag);
|
||||
|
||||
if (url == NULL)
|
||||
{
|
||||
url = TheWebBrowser->makeNewURL(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// find existing item if present
|
||||
// track = TheAudio->Music->getTrack( name );
|
||||
// if( track == NULL )
|
||||
// {
|
||||
|
||||
// allocate a new track
|
||||
// track = TheAudio->Music->newMusicTrack( name );
|
||||
|
||||
// } // end if
|
||||
|
||||
// DEBUG_ASSERTCRASH( track, ("parseMusicTrackDefinition: Unable to allocate track '%s'\n",
|
||||
// name.str()) );
|
||||
|
||||
// parse the ini definition
|
||||
ini->initFromINI( url, url->getFieldParse() );
|
||||
|
||||
if (url->m_url.startsWith("file://"))
|
||||
{
|
||||
char cwd[_MAX_PATH] = "\\";
|
||||
getcwd(cwd, _MAX_PATH);
|
||||
|
||||
url->m_url.format("file://%s\\Data\\%s\\%s", encodeURL(cwd).str(), GetRegistryLanguage().str(), url->m_url.str()+7);
|
||||
DEBUG_LOG(("INI::parseWebpageURLDefinition() - converted URL to [%s]\n", url->m_url.str()));
|
||||
}
|
||||
} // end parseMusicTrackDefinition
|
||||
|
||||
|
||||
69
Generals/Code/GameEngine/Source/Common/Language.cpp
Normal file
69
Generals/Code/GameEngine/Source/Common/Language.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Language.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: Language.cpp
|
||||
//
|
||||
// Created: Colin Day, June 2001
|
||||
//
|
||||
// Desc: For dealing with multiple languages
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Language.h"
|
||||
|
||||
// DEFINES ////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE TYPES //////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
LanguageID OurLanguage = LANGUAGE_ID_US;
|
||||
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////
|
||||
|
||||
// PUBLIC DATA ////////////////////////////////////////////////////////////////
|
||||
|
||||
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
|
||||
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
|
||||
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
|
||||
1192
Generals/Code/GameEngine/Source/Common/MessageStream.cpp
Normal file
1192
Generals/Code/GameEngine/Source/Common/MessageStream.cpp
Normal file
File diff suppressed because it is too large
Load Diff
93
Generals/Code/GameEngine/Source/Common/MiniLog.cpp
Normal file
93
Generals/Code/GameEngine/Source/Common/MiniLog.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MiniLog.cpp ///////////////////////////////////////////////////////////
|
||||
// Alternative logging
|
||||
// Author: Matthew D. Campbell, January 2003
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/MiniLog.h"
|
||||
|
||||
#ifdef DEBUG_LOGGING
|
||||
|
||||
LogClass::LogClass(const char *fname)
|
||||
{
|
||||
char buffer[ _MAX_PATH ];
|
||||
GetModuleFileName( NULL, buffer, sizeof( buffer ) );
|
||||
char *pEnd = buffer + strlen( buffer );
|
||||
while( pEnd != buffer )
|
||||
{
|
||||
if( *pEnd == '\\' )
|
||||
{
|
||||
*pEnd = 0;
|
||||
break;
|
||||
}
|
||||
pEnd--;
|
||||
}
|
||||
AsciiString fullPath;
|
||||
fullPath.format("%s\\%s", buffer, fname);
|
||||
m_fp = fopen(fullPath.str(), "wt");
|
||||
}
|
||||
|
||||
LogClass::~LogClass()
|
||||
{
|
||||
if (m_fp)
|
||||
{
|
||||
fclose(m_fp);
|
||||
}
|
||||
}
|
||||
|
||||
void LogClass::log(const char *fmt, ...)
|
||||
{
|
||||
static char buf[1024];
|
||||
static Int lastFrame = 0;
|
||||
static Int lastIndex = 0;
|
||||
if (lastFrame != TheGameLogic->getFrame())
|
||||
{
|
||||
lastFrame = TheGameLogic->getFrame();
|
||||
lastIndex = 0;
|
||||
}
|
||||
|
||||
va_list va;
|
||||
va_start( va, fmt );
|
||||
_vsnprintf(buf, 1024, fmt, va );
|
||||
buf[1023] = 0;
|
||||
va_end( va );
|
||||
|
||||
char *tmp = buf;
|
||||
while (tmp && *tmp)
|
||||
{
|
||||
if (*tmp == '\r' || *tmp == '\n')
|
||||
{
|
||||
*tmp = ' ';
|
||||
}
|
||||
++tmp;
|
||||
}
|
||||
|
||||
fprintf(m_fp, "%d:%d %s\n", lastFrame, lastIndex++, buf);
|
||||
fflush(m_fp);
|
||||
}
|
||||
|
||||
#endif // DEBUG_LOGGING
|
||||
168
Generals/Code/GameEngine/Source/Common/MultiplayerSettings.cpp
Normal file
168
Generals/Code/GameEngine/Source/Common/MultiplayerSettings.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MultiplayerSettings.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// The MultiplayerSettings object
|
||||
// Author: Matthew D. Campbell, January 2002
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_TERRAIN_LOD_NAMES
|
||||
#define DEFINE_TIME_OF_DAY_NAMES
|
||||
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "Common/INI.h"
|
||||
#include "GameNetwork/GameInfo.h" // for PLAYERTEMPLATE_*
|
||||
|
||||
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
|
||||
MultiplayerSettings *TheMultiplayerSettings = NULL; ///< The MultiplayerSettings singleton
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const FieldParse MultiplayerColorDefinition::m_colorFieldParseTable[] =
|
||||
{
|
||||
|
||||
{ "TooltipName", INI::parseAsciiString, NULL, offsetof( MultiplayerColorDefinition, m_tooltipName ) },
|
||||
{ "RGBColor", INI::parseRGBColor, NULL, offsetof( MultiplayerColorDefinition, m_rgbValue ) },
|
||||
{ "RGBNightColor", INI::parseRGBColor, NULL, offsetof( MultiplayerColorDefinition, m_rgbValueNight ) },
|
||||
{ NULL, NULL, NULL, 0 } // keep this last
|
||||
|
||||
};
|
||||
|
||||
const FieldParse MultiplayerSettings::m_multiplayerSettingsFieldParseTable[] =
|
||||
{
|
||||
|
||||
{ "InitialCreditsMin", INI::parseInt, NULL, offsetof( MultiplayerSettings, m_initialCreditsMin ) },
|
||||
{ "InitialCreditsMax", INI::parseInt, NULL, offsetof( MultiplayerSettings, m_initialCreditsMax ) },
|
||||
{ "StartCountdownTimer", INI::parseInt, NULL, offsetof( MultiplayerSettings, m_startCountdownTimerSeconds ) },
|
||||
{ "MaxBeaconsPerPlayer", INI::parseInt, NULL, offsetof( MultiplayerSettings, m_maxBeaconsPerPlayer ) },
|
||||
{ "UseShroud", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_isShroudInMultiplayer ) },
|
||||
{ "ShowRandomPlayerTemplate", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_showRandomPlayerTemplate ) },
|
||||
{ "ShowRandomStartPos", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_showRandomStartPos ) },
|
||||
{ "ShowRandomColor", INI::parseBool, NULL, offsetof( MultiplayerSettings, m_showRandomColor ) },
|
||||
|
||||
{ NULL, NULL, NULL, 0 } // keep this last
|
||||
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
MultiplayerSettings::MultiplayerSettings()
|
||||
{
|
||||
m_initialCreditsMin = 5000;
|
||||
|
||||
//Fixed And Added Code By Sadullah Nader
|
||||
//DID U MEAN m_initialCreditsMax = 10000;?
|
||||
//Initializations inserted
|
||||
m_initialCreditsMax = 10000;
|
||||
m_maxBeaconsPerPlayer = 3;
|
||||
//
|
||||
|
||||
m_startCountdownTimerSeconds = 0;
|
||||
m_numColors = 0;
|
||||
m_isShroudInMultiplayer = TRUE;
|
||||
m_showRandomPlayerTemplate = TRUE;
|
||||
m_showRandomStartPos = TRUE;
|
||||
m_showRandomColor = TRUE;
|
||||
|
||||
m_observerColor;
|
||||
m_randomColor;
|
||||
} // end MultiplayerSettings
|
||||
|
||||
MultiplayerColorDefinition::MultiplayerColorDefinition()
|
||||
{
|
||||
m_tooltipName.clear();
|
||||
m_rgbValue.setFromInt(0xFFFFFFFF);
|
||||
m_rgbValueNight=m_rgbValue;
|
||||
m_color = 0xFFFFFFFF;
|
||||
m_colorNight = m_color;
|
||||
}
|
||||
|
||||
MultiplayerColorDefinition * MultiplayerSettings::getColor(Int which)
|
||||
{
|
||||
if (which == PLAYERTEMPLATE_RANDOM)
|
||||
{
|
||||
return &m_randomColor;
|
||||
}
|
||||
else if (which == PLAYERTEMPLATE_OBSERVER)
|
||||
{
|
||||
return &m_observerColor;
|
||||
}
|
||||
else if (which < 0 || which >= getNumColors())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &m_colorList[which];
|
||||
}
|
||||
|
||||
MultiplayerColorDefinition * MultiplayerSettings::findMultiplayerColorDefinitionByName(AsciiString name)
|
||||
{
|
||||
MultiplayerColorIter iter = m_colorList.begin();
|
||||
|
||||
while (iter != m_colorList.end())
|
||||
{
|
||||
if (iter->second.getTooltipName() == name)
|
||||
return &(iter->second);
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MultiplayerColorDefinition * MultiplayerSettings::newMultiplayerColorDefinition(AsciiString name)
|
||||
{
|
||||
MultiplayerColorDefinition tmp;
|
||||
Int numColors = getNumColors();
|
||||
|
||||
m_colorList[numColors] = tmp;
|
||||
m_numColors = m_colorList.size();
|
||||
|
||||
return &m_colorList[numColors];
|
||||
}
|
||||
|
||||
MultiplayerColorDefinition * MultiplayerColorDefinition::operator =(const MultiplayerColorDefinition& other)
|
||||
{
|
||||
m_tooltipName = other.getTooltipName();
|
||||
m_rgbValue = other.getRGBValue();
|
||||
m_color = other.getColor();
|
||||
m_rgbValueNight = other.getRGBNightValue();
|
||||
m_colorNight = other.getNightColor();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void MultiplayerColorDefinition::setColor( RGBColor rgb )
|
||||
{
|
||||
m_color = rgb.getAsInt() | 0xFF << 24;
|
||||
}
|
||||
|
||||
void MultiplayerColorDefinition::setNightColor( RGBColor rgb )
|
||||
{
|
||||
m_colorNight = rgb.getAsInt() | 0xFF << 24;
|
||||
}
|
||||
173
Generals/Code/GameEngine/Source/Common/NameKeyGenerator.cpp
Normal file
173
Generals/Code/GameEngine/Source/Common/NameKeyGenerator.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: NameKeyGenerator.cpp /////////////////////////////////////////////////////////////////////
|
||||
// Created: Michael Booth, May 2001
|
||||
// Colin Day, May 2001
|
||||
// Desc: Name key system to translate between names and unique key ids
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
// Public Data ////////////////////////////////////////////////////////////////////////////////////
|
||||
NameKeyGenerator *TheNameKeyGenerator = NULL; ///< name key gen. singleton
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
NameKeyGenerator::NameKeyGenerator()
|
||||
{
|
||||
|
||||
m_nextID = (UnsignedInt)NAMEKEY_INVALID; // uninitialized system
|
||||
|
||||
for (Int i = 0; i < SOCKET_COUNT; ++i)
|
||||
m_sockets[i] = NULL;
|
||||
|
||||
} // end NameKeyGenerator
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
NameKeyGenerator::~NameKeyGenerator()
|
||||
{
|
||||
|
||||
// free all system data
|
||||
freeSockets();
|
||||
|
||||
} // end ~NameKeyGenerator
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void NameKeyGenerator::init()
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_nextID == (UnsignedInt)NAMEKEY_INVALID, ("NameKeyGen already inited"));
|
||||
|
||||
// start keys at the beginning again
|
||||
freeSockets();
|
||||
m_nextID = 1;
|
||||
|
||||
} // end init
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void NameKeyGenerator::reset()
|
||||
{
|
||||
freeSockets();
|
||||
m_nextID = 1;
|
||||
|
||||
} // end reset
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void NameKeyGenerator::freeSockets()
|
||||
{
|
||||
for (Int i = 0; i < SOCKET_COUNT; ++i)
|
||||
{
|
||||
Bucket *next;
|
||||
for (Bucket *b = m_sockets[i]; b; b = next)
|
||||
{
|
||||
next = b->m_nextInSocket;
|
||||
b->deleteInstance();
|
||||
}
|
||||
m_sockets[i] = NULL;
|
||||
}
|
||||
|
||||
} // end freeSockets
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
inline UnsignedInt calcHashForString(const char* p)
|
||||
{
|
||||
UnsignedInt result = 0;
|
||||
Byte *pp = (Byte*)p;
|
||||
while (*pp)
|
||||
result = (result << 5) + result + *pp++;
|
||||
return result;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
AsciiString NameKeyGenerator::keyToName(NameKeyType key)
|
||||
{
|
||||
for (Int i = 0; i < SOCKET_COUNT; ++i)
|
||||
{
|
||||
for (Bucket *b = m_sockets[i]; b; b = b->m_nextInSocket)
|
||||
{
|
||||
if (key == b->m_key)
|
||||
return b->m_nameString;
|
||||
}
|
||||
}
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
NameKeyType NameKeyGenerator::nameToKey(const char* nameString)
|
||||
{
|
||||
Bucket *b;
|
||||
|
||||
UnsignedInt hash = calcHashForString(nameString) % SOCKET_COUNT;
|
||||
|
||||
// hmm, do we have it already?
|
||||
for (b = m_sockets[hash]; b; b = b->m_nextInSocket)
|
||||
{
|
||||
if (strcmp(nameString, b->m_nameString.str()) == 0)
|
||||
return b->m_key;
|
||||
}
|
||||
|
||||
// nope, guess not. let's allocate it.
|
||||
b = newInstance(Bucket);
|
||||
b->m_key = (NameKeyType)m_nextID++;
|
||||
b->m_nameString = nameString;
|
||||
b->m_nextInSocket = m_sockets[hash];
|
||||
m_sockets[hash] = b;
|
||||
|
||||
NameKeyType result = b->m_key;
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
// reality-check to be sure our hasher isn't going bad.
|
||||
const Int maxThresh = 3;
|
||||
Int numOverThresh = 0;
|
||||
for (Int i = 0; i < SOCKET_COUNT; ++i)
|
||||
{
|
||||
Int numInThisSocket = 0;
|
||||
for (b = m_sockets[i]; b; b = b->m_nextInSocket)
|
||||
++numInThisSocket;
|
||||
|
||||
if (numInThisSocket > maxThresh)
|
||||
++numOverThresh;
|
||||
}
|
||||
|
||||
// if more than a small percent of the sockets are getting deep, probably want to increase the socket count.
|
||||
if (numOverThresh > SOCKET_COUNT/20)
|
||||
{
|
||||
DEBUG_CRASH(("hmm, might need to increase the number of bucket-sockets for NameKeyGenerator (numOverThresh %d = %f%%)\n",numOverThresh,(Real)numOverThresh/(Real)(SOCKET_COUNT/20)));
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
|
||||
} // end nameToKey
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
NameKeyType StaticNameKey::key() const
|
||||
{
|
||||
if (m_key == NAMEKEY_INVALID)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(TheNameKeyGenerator, ("no TheNameKeyGenerator yet"));
|
||||
if (TheNameKeyGenerator)
|
||||
m_key = TheNameKeyGenerator->nameToKey(m_name);
|
||||
}
|
||||
return m_key;
|
||||
}
|
||||
126
Generals/Code/GameEngine/Source/Common/PartitionSolver.cpp
Normal file
126
Generals/Code/GameEngine/Source/Common/PartitionSolver.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PartitionSolver.cpp //////////////////////////////////////////////////////////////////////
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* EA Pacific */
|
||||
/* Confidential Information */
|
||||
/* Copyright (C) 2001 - All Rights Reserved */
|
||||
/* DO NOT DISTRIBUTE */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Project: RTS3 */
|
||||
/* File name: PartitionSolver.cpp */
|
||||
/* Created: John K. McDonald, Jr., 4/2/2002 */
|
||||
/* Desc: This contains a general-purpose Partition solver */
|
||||
/* Revision History: */
|
||||
/* 4/12/2002 : Initial creation */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/**************************************************************************************************
|
||||
Some info about partioning problems:
|
||||
|
||||
This problem is contained in a very interesting class of problems known as NP complete. The
|
||||
basic problem is that there is no way to tell whether you have an optimal solution or not.
|
||||
Worst case, you try out every possible solution and still don't find the optimal solution:
|
||||
this takes 2^n time to find, where N is the number of elements you are attempting to place.
|
||||
For this reason, a value near PREFER_FAST_SOLUTION should almost always be chosen. We will use
|
||||
a flat multiply to determine how many solutions to attempt before giving up and returning our
|
||||
best attempt. If you want more info, this site contains info on the problem:
|
||||
http://odysseus.nat.uni-magdeburg.de/~mertens/npp/index.shtml
|
||||
**************************************************************************************************/
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/PartitionSolver.h"
|
||||
|
||||
static Bool greater_than(PairObjectIDAndUInt a, PairObjectIDAndUInt b)
|
||||
{
|
||||
return a.second > b.second;
|
||||
}
|
||||
|
||||
PartitionSolver::PartitionSolver(const EntriesVec& elements, const SpacesVec& spaces, SolutionType solveHow)
|
||||
{
|
||||
m_data = elements;
|
||||
m_spacesForData = spaces;
|
||||
m_howToSolve = solveHow;
|
||||
//Added By Sadullah Nader
|
||||
//Initializations inserted
|
||||
m_currentSolutionLeftovers = 0;
|
||||
//
|
||||
}
|
||||
|
||||
void PartitionSolver::solve(void)
|
||||
{
|
||||
m_bestSolution.clear();
|
||||
m_currentSolution.clear();
|
||||
m_currentSolutionLeftovers = 0x7fffffff;
|
||||
|
||||
Int minSizeForAllData = 0;
|
||||
Int slotsAllotted = 0;
|
||||
Int i, j;
|
||||
|
||||
// first, determine whether there is an actual solution, or we're going to have to fudge it.
|
||||
for (i = 0; i < m_data.size(); ++i) {
|
||||
minSizeForAllData += m_data[i].second;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_spacesForData.size(); ++i) {
|
||||
slotsAllotted += m_spacesForData[i].second;
|
||||
}
|
||||
|
||||
// we want to attempt to place the largest things first. This allows us to throw
|
||||
// out whole classes of solutions
|
||||
|
||||
std::sort(m_data.begin(), m_data.end(), greater_than);
|
||||
|
||||
// Also make the largest partition first.
|
||||
std::sort(m_spacesForData.begin(), m_spacesForData.end(), greater_than);
|
||||
|
||||
// work in our temporary vector.
|
||||
SpacesVec spacesStillAvailable = m_spacesForData;
|
||||
|
||||
if (m_howToSolve == PREFER_FAST_SOLUTION)
|
||||
{
|
||||
// we prefer the fast, but not necessarily correct solution
|
||||
// simply start placing the stuff. Skip things you can't place.
|
||||
for (i = 0; i < m_data.size(); ++i)
|
||||
{
|
||||
for (j = 0; j < spacesStillAvailable.size(); ++j)
|
||||
{
|
||||
if (m_data[i].second <= spacesStillAvailable[j].second)
|
||||
{
|
||||
spacesStillAvailable[j].second -= m_data[i].second;
|
||||
m_bestSolution.push_back(std::make_pair(m_data[i].first, spacesStillAvailable[j].first));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_CRASH(("PREFER_CORRECT_SOLUTION @todo impl"));
|
||||
}
|
||||
}
|
||||
|
||||
const SolutionVec& PartitionSolver::getSolution( void ) const
|
||||
{
|
||||
return m_bestSolution;
|
||||
}
|
||||
592
Generals/Code/GameEngine/Source/Common/PerfTimer.cpp
Normal file
592
Generals/Code/GameEngine/Source/Common/PerfTimer.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PerfTimer.cpp ///////////////////////////////////////////////////////////////////////////
|
||||
// Author:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/PerfTimer.h"
|
||||
|
||||
#include "Common/GlobalData.h"
|
||||
#include "GameClient/DebugDisplay.h"
|
||||
#include "GameClient/Display.h"
|
||||
#include "GameClient/GraphDraw.h"
|
||||
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#if defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static Int64 s_ticksPerSec = 0;
|
||||
static double s_ticksPerMSec = 0;
|
||||
static double s_ticksPerUSec = 0;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void GetPrecisionTimerTicksPerSec(Int64* t)
|
||||
{
|
||||
*t = s_ticksPerSec;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void InitPrecisionTimer()
|
||||
{
|
||||
#ifdef USE_QPF
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&s_ticksPerSec);
|
||||
#else
|
||||
// Init the precision timers
|
||||
Int64 totalTime = 0;
|
||||
Int64 TotalTicks = 0;
|
||||
static int TESTS = 5;
|
||||
|
||||
for (int i = 0; i < TESTS; ++i)
|
||||
{
|
||||
int TimeStart;
|
||||
int TimeStop;
|
||||
Int64 StartTicks;
|
||||
Int64 EndTicks;
|
||||
|
||||
TimeStart = timeGetTime();
|
||||
GetPrecisionTimer(&StartTicks);
|
||||
for(;;)
|
||||
{
|
||||
TimeStop = timeGetTime();
|
||||
if ((TimeStop - TimeStart) > 1000)
|
||||
{
|
||||
GetPrecisionTimer(&EndTicks);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TotalTicks += (EndTicks - StartTicks);
|
||||
|
||||
totalTime += (TimeStop - TimeStart);
|
||||
}
|
||||
|
||||
s_ticksPerMSec = 1.0 * TotalTicks / totalTime;
|
||||
s_ticksPerSec = s_ticksPerMSec * 1000.0f;
|
||||
#endif
|
||||
s_ticksPerMSec = s_ticksPerSec / 1000.0f;
|
||||
s_ticksPerUSec = s_ticksPerSec / 1000000.0f;
|
||||
|
||||
#ifdef NOT_IN_USE
|
||||
Int64 bogus[8];
|
||||
GetPrecisionTimer(&start);
|
||||
for (Int ii = 0; ii < ITERS; ++ii)
|
||||
{
|
||||
GetPrecisionTimer(&bogus[0]);
|
||||
GetPrecisionTimer(&bogus[1]);
|
||||
GetPrecisionTimer(&bogus[2]);
|
||||
GetPrecisionTimer(&bogus[3]);
|
||||
GetPrecisionTimer(&bogus[4]);
|
||||
GetPrecisionTimer(&bogus[5]);
|
||||
GetPrecisionTimer(&bogus[6]);
|
||||
GetPrecisionTimer(&bogus[7]);
|
||||
}
|
||||
TheTicksToGetTicks = (bogus[7] - start) / (ITERS*8);
|
||||
DEBUG_LOG(("TheTicksToGetTicks is %d (%f usec)\n",(int)TheTicksToGetTicks,TheTicksToGetTicks/s_ticksPerUSec));
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif // defined(PERF_TIMERS) || defined(DUMP_PERF_STATS)
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifdef PERF_TIMERS
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
/*static*/ Bool AutoPerfGatherIgnore::s_ignoring = false;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
typedef std::vector< std::pair< AsciiString, AsciiString > > StringPairVec;
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// PerfMetrics class. Basically, request a handle with your name and it will return. We use a vector
|
||||
// of pairs of asciistrings for this
|
||||
|
||||
class PerfMetricsOutput
|
||||
{
|
||||
private:
|
||||
StringPairVec m_outputStats;
|
||||
|
||||
public:
|
||||
|
||||
AsciiString& getStatsString(const AsciiString& id)
|
||||
{
|
||||
for (int i = 0; i < m_outputStats.size(); ++i)
|
||||
{
|
||||
if (m_outputStats[i].first == id)
|
||||
return m_outputStats[i].second;
|
||||
}
|
||||
std::pair<AsciiString, AsciiString> newPair;
|
||||
newPair.first = id;
|
||||
m_outputStats.push_back(newPair);
|
||||
return m_outputStats.back().second;
|
||||
}
|
||||
|
||||
void clearStatsString(const AsciiString& id)
|
||||
{
|
||||
for (int i = 0; i < m_outputStats.size(); ++i)
|
||||
{
|
||||
if (m_outputStats[i].first == id)
|
||||
{
|
||||
m_outputStats.erase(&m_outputStats[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringPairVec& friend_getAllStatsStrings() { return m_outputStats; }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
static PerfMetricsOutput s_output;
|
||||
static FILE* s_perfStatsFile = NULL;
|
||||
static Int s_perfDumpOptions = 0;
|
||||
static UnsignedInt s_lastDumpedFrame = 0;
|
||||
static char s_buf[256] = "";
|
||||
|
||||
PerfGather* PerfGather::m_active[MAX_ACTIVE_STACK] = { 0 };
|
||||
PerfGather** PerfGather::m_activeHead = &PerfGather::m_active[0];
|
||||
Int64 PerfGather::s_stopStartOverhead = -1;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ PerfGather*& PerfGather::getHeadPtr()
|
||||
{
|
||||
// funky technique for order-of-init problem. trust me. (srj)
|
||||
static PerfGather* s_head = NULL;
|
||||
return s_head;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PerfGather::addToList()
|
||||
{
|
||||
PerfGather*& head = getHeadPtr();
|
||||
|
||||
this->m_next = head;
|
||||
if (head)
|
||||
head->m_prev = this;
|
||||
head = this;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PerfGather::removeFromList()
|
||||
{
|
||||
PerfGather*& head = getHeadPtr();
|
||||
|
||||
if (this->m_next)
|
||||
this->m_next->m_prev = this->m_prev;
|
||||
|
||||
if (this->m_prev)
|
||||
this->m_prev->m_next = this->m_next;
|
||||
else
|
||||
head = this->m_next;
|
||||
|
||||
this->m_prev = 0;
|
||||
this->m_next = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PerfGather::PerfGather(const char *identifier) :
|
||||
m_identifier(identifier),
|
||||
m_startTime(0),
|
||||
m_runningTimeGross(0),
|
||||
m_runningTimeNet(0),
|
||||
m_callCount(0),
|
||||
m_next(0),
|
||||
m_prev(0)
|
||||
{
|
||||
//Added By Sadullah Nader
|
||||
//Initializations inserted
|
||||
m_ignore = FALSE;
|
||||
//
|
||||
DEBUG_ASSERTCRASH(strchr(m_identifier, ',') == NULL, ("PerfGather names must not contain commas"));
|
||||
addToList();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PerfGather::~PerfGather()
|
||||
{
|
||||
removeFromList();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PerfGather::reset()
|
||||
{
|
||||
m_startTime = 0;
|
||||
m_runningTimeGross = 0;
|
||||
m_runningTimeNet = 0;
|
||||
m_callCount = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PerfGather::resetAll()
|
||||
{
|
||||
for (PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
head->reset();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PerfGather::initPerfDump(const char* fname, Int options)
|
||||
{
|
||||
PerfGather::termPerfDump();
|
||||
|
||||
strcpy(s_buf, fname);
|
||||
|
||||
char tmp[256];
|
||||
strcpy(tmp, s_buf);
|
||||
strcat(tmp, ".csv");
|
||||
|
||||
s_perfStatsFile = fopen(tmp, "w");
|
||||
s_perfDumpOptions = options;
|
||||
|
||||
if (s_perfStatsFile == NULL)
|
||||
{
|
||||
DEBUG_CRASH(("could not open/create perf file %s -- is it open in another app?",s_buf));
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_stopStartOverhead == -1)
|
||||
{
|
||||
const Int ITERS = 100000;
|
||||
Int64 start, end;
|
||||
PerfGather pf("timer");
|
||||
GetPrecisionTimer(&start);
|
||||
for (Int ii = 0; ii < ITERS; ++ii)
|
||||
{
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
pf.startTimer(); pf.stopTimer();
|
||||
}
|
||||
GetPrecisionTimer(&end);
|
||||
s_stopStartOverhead = (end - start) / (ITERS*8);
|
||||
DEBUG_LOG(("s_stopStartOverhead is %d (%f usec)\n",(int)s_stopStartOverhead,s_stopStartOverhead/s_ticksPerUSec));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PerfGather::dumpAll(UnsignedInt frame)
|
||||
{
|
||||
if (frame < s_lastDumpedFrame)
|
||||
{
|
||||
// must have reset or started a new game.
|
||||
termPerfDump();
|
||||
initPerfDump(s_buf, s_perfDumpOptions);
|
||||
}
|
||||
|
||||
if (!s_perfStatsFile)
|
||||
{
|
||||
DEBUG_CRASH(("not inited"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame >= 1 && frame <= 30)
|
||||
{
|
||||
// always skip the first second or so, since it loads everything and skews the results horribly
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_lastDumpedFrame == 0)
|
||||
{
|
||||
fprintf(s_perfStatsFile, "Frame");
|
||||
if (s_perfDumpOptions & PERF_GROSSTIME)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
fprintf(s_perfStatsFile, ",Gross:%s", head->m_identifier);
|
||||
}
|
||||
}
|
||||
if (s_perfDumpOptions & PERF_NETTIME)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
fprintf(s_perfStatsFile, ",Net:%s", head->m_identifier);
|
||||
}
|
||||
}
|
||||
if (s_perfDumpOptions & PERF_CALLCOUNT)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
fprintf(s_perfStatsFile, ",Count:%s", head->m_identifier);
|
||||
}
|
||||
}
|
||||
fprintf(s_perfStatsFile, "\n");
|
||||
}
|
||||
|
||||
// a strange value so we can find it in the dump, if necessary.
|
||||
// there's nothing magic about this value, it's purely determined from sample dumps...
|
||||
// const Real CLIP_BIG_SPIKES = 1e10f;
|
||||
const Real CLIP_BIG_SPIKES = 100000.0f;
|
||||
|
||||
// make this a nonnumeric thing so Excel won't try to graph it...
|
||||
fprintf(s_perfStatsFile, "Frame%08d", frame);
|
||||
if (s_perfDumpOptions & PERF_GROSSTIME)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
double t = head->m_runningTimeGross;
|
||||
t /= s_ticksPerUSec;
|
||||
if (t > CLIP_BIG_SPIKES)
|
||||
t = CLIP_BIG_SPIKES;
|
||||
fprintf(s_perfStatsFile, ",%f", t);
|
||||
}
|
||||
}
|
||||
if (s_perfDumpOptions & PERF_NETTIME)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
double t = head->m_runningTimeNet;
|
||||
t /= s_ticksPerUSec;
|
||||
if (t > CLIP_BIG_SPIKES)
|
||||
t = CLIP_BIG_SPIKES;
|
||||
fprintf(s_perfStatsFile, ",%f", t);
|
||||
}
|
||||
}
|
||||
if (s_perfDumpOptions & PERF_CALLCOUNT)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
fprintf(s_perfStatsFile, ",%d", head->m_callCount);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(s_perfStatsFile, "\n");
|
||||
fflush(s_perfStatsFile);
|
||||
|
||||
s_lastDumpedFrame = frame;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// This function will queue up stuff to draw on the next frame. We also need to adjust the
|
||||
// perf timers to not include time spent paused by the script engine.
|
||||
/*static*/ void PerfGather::displayGraph(UnsignedInt frame)
|
||||
{
|
||||
if (!TheGraphDraw) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame >= 1 && frame <= 30)
|
||||
{
|
||||
// always skip the first second or so, since it loads everything and skews the results horribly
|
||||
}
|
||||
else
|
||||
{
|
||||
const Real CLIP_BIG_SPIKES = 100000.0f;
|
||||
|
||||
if (s_perfDumpOptions & PERF_GROSSTIME)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
Real t = head->m_runningTimeGross;
|
||||
t /= s_ticksPerUSec;
|
||||
if (t > CLIP_BIG_SPIKES)
|
||||
t = CLIP_BIG_SPIKES;
|
||||
|
||||
TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
|
||||
}
|
||||
}
|
||||
if (s_perfDumpOptions & PERF_NETTIME)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
Real t = head->m_runningTimeNet;
|
||||
t /= s_ticksPerUSec;
|
||||
if (t > CLIP_BIG_SPIKES)
|
||||
t = CLIP_BIG_SPIKES;
|
||||
|
||||
TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
|
||||
}
|
||||
}
|
||||
if (s_perfDumpOptions & PERF_CALLCOUNT)
|
||||
{
|
||||
for (const PerfGather* head = getHeadPtr(); head != NULL; head = head->m_next)
|
||||
{
|
||||
Real t = head->m_callCount;
|
||||
TheGraphDraw->addEntry(head->m_identifier, REAL_TO_INT(t));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PerfGather::termPerfDump()
|
||||
{
|
||||
if (s_perfStatsFile)
|
||||
{
|
||||
fflush(s_perfStatsFile);
|
||||
fclose(s_perfStatsFile);
|
||||
s_perfStatsFile = NULL;
|
||||
}
|
||||
s_lastDumpedFrame = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PerfTimer::PerfTimer( const char *identifier, Bool crashWithInfo, Int startFrame, Int endFrame) :
|
||||
m_identifier(identifier),
|
||||
m_crashWithInfo(crashWithInfo),
|
||||
m_startFrame(startFrame),
|
||||
m_endFrame(endFrame),
|
||||
m_callCount(0),
|
||||
m_runningTime(0),
|
||||
m_outputInfo(true),
|
||||
//Added By Sadullah Nader
|
||||
//Intializations inserted
|
||||
m_lastFrame(-1)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PerfTimer::~PerfTimer( )
|
||||
{
|
||||
if (m_endFrame == -1) {
|
||||
outputInfo();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PerfTimer::outputInfo( void )
|
||||
{
|
||||
if (TheGlobalData->m_showMetrics) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_outputInfo && !TheGlobalData->m_showMetrics) {
|
||||
m_outputInfo = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_ticksPerSec) {
|
||||
// DEBUG_CRASH here
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
double totalTimeInMS = 1000.0 * m_runningTime / s_ticksPerSec;
|
||||
double avgTimePerFrame = totalTimeInMS / (m_lastFrame - m_startFrame + 1);
|
||||
double avgTimePerCall = totalTimeInMS / m_callCount;
|
||||
#endif
|
||||
|
||||
if (m_crashWithInfo) {
|
||||
DEBUG_CRASH(("%s\n"
|
||||
"Average Time (per call): %.4f ms\n"
|
||||
"Average Time (per frame): %.4f ms\n"
|
||||
"Average calls per frame: %.2f\n"
|
||||
"Number of calls: %d\n"
|
||||
"Max possible FPS: %.4f\n",
|
||||
m_identifier,
|
||||
avgTimePerCall,
|
||||
avgTimePerFrame,
|
||||
1.0f * m_callCount / (m_lastFrame - m_startFrame + 1),
|
||||
m_callCount,
|
||||
1000.0f / avgTimePerFrame));
|
||||
} else {
|
||||
DEBUG_LOG(("%s\n"
|
||||
"Average Time (per call): %.4f ms\n"
|
||||
"Average Time (per frame): %.4f ms\n"
|
||||
"Average calls per frame: %.2f\n"
|
||||
"Number of calls: %d\n"
|
||||
"Max possible FPS: %.4f\n",
|
||||
m_identifier,
|
||||
avgTimePerCall,
|
||||
avgTimePerFrame,
|
||||
1.0f * m_callCount / (m_lastFrame - m_startFrame + 1),
|
||||
m_callCount,
|
||||
1000.0f / avgTimePerFrame));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void PerfTimer::showMetrics( void )
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
double totalTimeInMS = 1000.0 * m_runningTime / s_ticksPerSec;
|
||||
double avgTimePerFrame = totalTimeInMS / (m_lastFrame - m_startFrame + 1);
|
||||
double avgTimePerCall = totalTimeInMS / m_callCount;
|
||||
#endif
|
||||
|
||||
// we want to work on the thing in the array, so just store a reference.
|
||||
AsciiString &outputStats = s_output.getStatsString(m_identifier);
|
||||
|
||||
outputStats.format("%s: %.2fms / call, %.2fms / frame \n",
|
||||
m_identifier,
|
||||
avgTimePerCall,
|
||||
avgTimePerFrame);
|
||||
m_callCount = 0;
|
||||
m_runningTime = 0;
|
||||
|
||||
UnsignedInt frm = (TheGameLogic ? TheGameLogic->getFrame() : m_startFrame);
|
||||
m_startFrame = frm + 1;
|
||||
m_endFrame = m_startFrame + PERFMETRICS_BETWEEN_METRICS;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------StatMetricsDisplay
|
||||
void StatMetricsDisplay( DebugDisplayInterface *dd, void *, FILE *fp )
|
||||
{
|
||||
dd->printf("Performance Metrics: \n");
|
||||
// no copies will take place because we are storing a reference to the thing
|
||||
StringPairVec &stats = s_output.friend_getAllStatsStrings();
|
||||
|
||||
for (int i = 0; i < stats.size(); ++i) {
|
||||
dd->printf("%s", stats[i].second.str());
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PERF_TIMERS
|
||||
|
||||
1875
Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp
Normal file
1875
Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
259
Generals/Code/GameEngine/Source/Common/RTS/Energy.cpp
Normal file
259
Generals/Code/GameEngine/Source/Common/RTS/Energy.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Energy.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: Energy.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: @todo
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Energy.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Real Energy::getEnergySupplyRatio() const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_energyProduction >= 0 && m_energyConsumption >= 0, ("neg Energy numbers\n"));
|
||||
|
||||
if (m_energyConsumption == 0)
|
||||
return (Real)m_energyProduction;
|
||||
|
||||
return (Real)m_energyProduction / (Real)m_energyConsumption;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool Energy::hasSufficientPower(void) const
|
||||
{
|
||||
return m_energyProduction >= m_energyConsumption;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void Energy::adjustPower(Int powerDelta, Bool adding)
|
||||
{
|
||||
if (powerDelta == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (powerDelta > 0) {
|
||||
if (adding) {
|
||||
addProduction(powerDelta);
|
||||
} else {
|
||||
addProduction(-powerDelta);
|
||||
}
|
||||
} else {
|
||||
// Seems a little odd, however, consumption is reversed. Negative power is positive consumption.
|
||||
if (adding) {
|
||||
addConsumption(-powerDelta);
|
||||
} else {
|
||||
addConsumption(powerDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** new 'obj' will now add/subtract from this energy construct */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void Energy::objectEnteringInfluence( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
// get the amount of energy this object produces or consumes
|
||||
Int energy = obj->getTemplate()->getEnergyProduction();
|
||||
|
||||
// adjust energy
|
||||
if( energy < 0 )
|
||||
addConsumption( -energy );
|
||||
else if( energy > 0 )
|
||||
addProduction( energy );
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
|
||||
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
|
||||
m_energyProduction, m_energyConsumption) );
|
||||
|
||||
} // end objectEnteringInfluence
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** 'obj' will now no longer add/subtrack from this energy construct */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void Energy::objectLeavingInfluence( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
// get the amount of energy this object produces or consumes
|
||||
Int energy = obj->getTemplate()->getEnergyProduction();
|
||||
|
||||
// adjust energy
|
||||
if( energy < 0 )
|
||||
addConsumption( energy );
|
||||
else if( energy > 0 )
|
||||
addProduction( -energy );
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
|
||||
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
|
||||
m_energyProduction, m_energyConsumption) );
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Adds an energy bonus to the player's pool of energy when the "Control Rods" upgrade
|
||||
is made to the American Cold Fusion Plant */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void Energy::addPowerBonus( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
addProduction(obj->getTemplate()->getEnergyBonus());
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
|
||||
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
|
||||
m_energyProduction, m_energyConsumption) );
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Removed an energy bonus */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Energy::removePowerBonus( Object *obj )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL )
|
||||
return;
|
||||
|
||||
addProduction( -obj->getTemplate()->getEnergyBonus() );
|
||||
|
||||
// sanity
|
||||
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
|
||||
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
|
||||
m_energyProduction, m_energyConsumption) );
|
||||
|
||||
} // end removePowerBonus
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Private functions
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Energy::addProduction(Int amt)
|
||||
{
|
||||
m_energyProduction += amt;
|
||||
|
||||
if( m_owner == NULL )
|
||||
return;
|
||||
|
||||
// A repeated Brownout signal does nothing bad, and we need to handle more than just edge cases.
|
||||
// Like low power, now even more low power, refresh disable.
|
||||
m_owner->onPowerBrownOutChange( !hasSufficientPower() );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Energy::addConsumption(Int amt)
|
||||
{
|
||||
m_energyConsumption += amt;
|
||||
|
||||
if( m_owner == NULL )
|
||||
return;
|
||||
|
||||
m_owner->onPowerBrownOutChange( !hasSufficientPower() );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Energy::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Energy::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 2;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// It is actually incorrect to save these, as they are reconstructed when the buildings are loaded
|
||||
// I need to version though so old games will load wrong rather than crashing
|
||||
|
||||
// production
|
||||
if( version < 2 )
|
||||
xfer->xferInt( &m_energyProduction );
|
||||
|
||||
// consumption
|
||||
if( version < 2 )
|
||||
xfer->xferInt( &m_energyConsumption );
|
||||
|
||||
// owning player index
|
||||
Int owningPlayerIndex;
|
||||
if( xfer->getXferMode() == XFER_SAVE )
|
||||
owningPlayerIndex = m_owner->getPlayerIndex();
|
||||
xfer->xferInt( &owningPlayerIndex );
|
||||
m_owner = ThePlayerList->getNthPlayer( owningPlayerIndex );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Energy::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
126
Generals/Code/GameEngine/Source/Common/RTS/Handicap.cpp
Normal file
126
Generals/Code/GameEngine/Source/Common/RTS/Handicap.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Handicap.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: Handicap.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: @todo
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Handicap.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Dict.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Handicap::Handicap()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Handicap::init()
|
||||
{
|
||||
for (Int i = 0; i < HANDICAP_TYPE_COUNT; ++i)
|
||||
for (Int j = 0; j < THING_TYPE_COUNT; ++j)
|
||||
m_handicaps[i][j] = 1.0f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Handicap::readFromDict(const Dict* d)
|
||||
{
|
||||
// this isn't very efficient, but is only called at load times,
|
||||
// so it probably doesn't really matter.
|
||||
|
||||
const char* htNames[HANDICAP_TYPE_COUNT] =
|
||||
{
|
||||
"BUILDCOST",
|
||||
"BUILDTIME",
|
||||
// "FIREPOWER",
|
||||
// "ARMOR",
|
||||
// "GROUNDSPEED",
|
||||
// "AIRSPEED",
|
||||
// "INCOME"
|
||||
};
|
||||
|
||||
const char* ttNames[THING_TYPE_COUNT] =
|
||||
{
|
||||
"GENERIC",
|
||||
"BUILDINGS",
|
||||
};
|
||||
|
||||
// no, you should NOT call init() here.
|
||||
//init();
|
||||
|
||||
AsciiString c;
|
||||
for (Int i = 0; i < HANDICAP_TYPE_COUNT; ++i)
|
||||
{
|
||||
for (Int j = 0; j < THING_TYPE_COUNT; ++j)
|
||||
{
|
||||
c.clear();
|
||||
c.set("HANDICAP_");
|
||||
c.concat(htNames[i]);
|
||||
c.concat("_");
|
||||
c.concat(ttNames[j]);
|
||||
NameKeyType k = TheNameKeyGenerator->nameToKey(c);
|
||||
Bool exists;
|
||||
Real r = d->getReal(k, &exists);
|
||||
if (exists)
|
||||
m_handicaps[i][j] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/*static*/ Handicap::ThingType Handicap::getBestThingType(const ThingTemplate *tmpl)
|
||||
{
|
||||
/// if this ends up being too slow, cache the information in the object
|
||||
if (tmpl->isKindOf(KINDOF_STRUCTURE))
|
||||
return BUILDINGS;
|
||||
|
||||
return GENERIC;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Real Handicap::getHandicap(HandicapType ht, const ThingTemplate *tmpl) const
|
||||
{
|
||||
ThingType tt = getBestThingType(tmpl);
|
||||
return m_handicaps[ht][tt];
|
||||
}
|
||||
113
Generals/Code/GameEngine/Source/Common/RTS/MissionStats.cpp
Normal file
113
Generals/Code/GameEngine/Source/Common/RTS/MissionStats.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MissionStats.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: MissionStats.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: @todo
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/MissionStats.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
MissionStats::MissionStats()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void MissionStats::init()
|
||||
{
|
||||
Int i;
|
||||
|
||||
for (i = 0; i < MAX_PLAYER_COUNT; ++i)
|
||||
{
|
||||
m_unitsKilled[i] = 0;
|
||||
m_buildingsKilled[i] = 0;
|
||||
}
|
||||
m_unitsLost = 0;
|
||||
m_buildingsLost = 0;
|
||||
//m_whoLastHurtMe = PLAYER_NONE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MissionStats::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info;
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MissionStats::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// units killed
|
||||
xfer->xferUser( m_unitsKilled, sizeof( Int ) * MAX_PLAYER_COUNT );
|
||||
|
||||
// units lost
|
||||
xfer->xferInt( &m_unitsLost );
|
||||
|
||||
// buidings killed
|
||||
xfer->xferUser( m_buildingsKilled, sizeof( Int ) * MAX_PLAYER_COUNT );
|
||||
|
||||
// buildings lost
|
||||
xfer->xferInt( &m_buildingsLost );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void MissionStats::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
125
Generals/Code/GameEngine/Source/Common/RTS/Money.cpp
Normal file
125
Generals/Code/GameEngine/Source/Common/RTS/Money.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Money.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: Money.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: @todo
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/Money.h"
|
||||
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/MiscAudio.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
UnsignedInt Money::withdraw(UnsignedInt amountToWithdraw, Bool playSound)
|
||||
{
|
||||
if (amountToWithdraw > m_money)
|
||||
amountToWithdraw = m_money;
|
||||
|
||||
if (amountToWithdraw == 0)
|
||||
return amountToWithdraw;
|
||||
|
||||
//@todo: Do we do this frequently enough that it is a performance hit?
|
||||
AudioEventRTS event = TheAudio->getMiscAudio()->m_moneyWithdrawSound;
|
||||
event.setPlayerIndex(m_playerIndex);
|
||||
|
||||
// Play a sound
|
||||
if (playSound)
|
||||
TheAudio->addAudioEvent(&event);
|
||||
|
||||
m_money -= amountToWithdraw;
|
||||
|
||||
return amountToWithdraw;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Money::deposit(UnsignedInt amountToDeposit, Bool playSound)
|
||||
{
|
||||
if (amountToDeposit == 0)
|
||||
return;
|
||||
|
||||
//@todo: Do we do this frequently enough that it is a performance hit?
|
||||
AudioEventRTS event = TheAudio->getMiscAudio()->m_moneyDepositSound;
|
||||
event.setPlayerIndex(m_playerIndex);
|
||||
|
||||
// Play a sound
|
||||
if (playSound)
|
||||
TheAudio->addAudioEvent(&event);
|
||||
|
||||
m_money += amountToDeposit;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Money::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Money::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// money value
|
||||
xfer->xferUnsignedInt( &m_money );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Money::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
4135
Generals/Code/GameEngine/Source/Common/RTS/Player.cpp
Normal file
4135
Generals/Code/GameEngine/Source/Common/RTS/Player.cpp
Normal file
File diff suppressed because it is too large
Load Diff
493
Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp
Normal file
493
Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PlayerList.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: PlayerList.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: @todo
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Errors.h"
|
||||
#include "Common/DataChunk.h"
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/Team.h"
|
||||
#include "Common/WellKnownKeys.h"
|
||||
#include "Common/Xfer.h"
|
||||
#ifdef _DEBUG
|
||||
#include "GameLogic/Object.h"
|
||||
#endif
|
||||
#include "GameLogic/SidesList.h"
|
||||
#include "GameNetwork/NetworkDefs.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/*extern*/ PlayerList *ThePlayerList = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlayerList::PlayerList() :
|
||||
m_local(NULL),
|
||||
m_playerCount(0)
|
||||
{
|
||||
// we only allocate a few of these, so don't bother pooling 'em
|
||||
for (Int i = 0; i < MAX_PLAYER_COUNT; i++)
|
||||
m_players[ i ] = NEW Player( i );
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlayerList::~PlayerList()
|
||||
{
|
||||
try {
|
||||
// the world is happier if we reinit things before destroying them,
|
||||
// to avoid debug warnings
|
||||
init();
|
||||
} catch (...) {
|
||||
// nothing
|
||||
}
|
||||
for( Int i = 0; i < MAX_PLAYER_COUNT; ++i )
|
||||
delete m_players[ i ];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Player *PlayerList::getNthPlayer(Int i)
|
||||
{
|
||||
if( i < 0 || i >= MAX_PLAYER_COUNT )
|
||||
{
|
||||
// DEBUG_CRASH( ("Illegal player index\n") );
|
||||
return NULL;
|
||||
}
|
||||
return m_players[i];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Player *PlayerList::findPlayerWithNameKey(NameKeyType key)
|
||||
{
|
||||
for (Int i = 0; i < m_playerCount; i++)
|
||||
{
|
||||
if (m_players[i]->getPlayerNameKey() == key)
|
||||
{
|
||||
return m_players[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerList::reset()
|
||||
{
|
||||
TheTeamFactory->clear(); // cleans up energy, among other things
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerList::newGame()
|
||||
{
|
||||
Int i;
|
||||
|
||||
DEBUG_ASSERTCRASH(this != NULL, ("null this"));
|
||||
|
||||
TheTeamFactory->clear(); // cleans up energy, among other things
|
||||
|
||||
// first, re-init ourselves.
|
||||
init();
|
||||
|
||||
// ok, now create the rest of players we need.
|
||||
Bool setLocal = false;
|
||||
for( i = 0; i < TheSidesList->getNumSides(); i++)
|
||||
{
|
||||
Dict *d = TheSidesList->getSideInfo(i)->getDict();
|
||||
AsciiString pname = d->getAsciiString(TheKey_playerName);
|
||||
if (pname.isEmpty())
|
||||
continue; // it's neutral, which we've already done, so skip it.
|
||||
|
||||
/// @todo The Player class should have a reset() method, instead of directly calling initFromDict() (MSB)
|
||||
Player* p = m_players[m_playerCount++];
|
||||
p->initFromDict(d);
|
||||
|
||||
// Multiplayer override
|
||||
Bool exists; // throwaway, since we don't care if it exists
|
||||
if (d->getBool(TheKey_multiplayerIsLocal, &exists))
|
||||
{
|
||||
DEBUG_LOG(("Player %s is multiplayer local\n", pname.str()));
|
||||
setLocalPlayer(p);
|
||||
setLocal = true;
|
||||
}
|
||||
|
||||
if (!setLocal && !TheNetwork && d->getBool(TheKey_playerIsHuman))
|
||||
{
|
||||
setLocalPlayer(p);
|
||||
setLocal = true;
|
||||
}
|
||||
|
||||
// Set the build list.
|
||||
p->setBuildList(TheSidesList->getSideInfo(i)->getBuildList());
|
||||
// Build list is attached to player now, so release it from the side info.
|
||||
TheSidesList->getSideInfo(i)->releaseBuildList();
|
||||
}
|
||||
|
||||
if (!setLocal)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(TheNetwork, ("*** Map has no human player... picking first nonneutral player for control\n"));
|
||||
for( i = 0; i < TheSidesList->getNumSides(); i++)
|
||||
{
|
||||
Player* p = getNthPlayer(i);
|
||||
if (p != getNeutralPlayer())
|
||||
{
|
||||
p->setPlayerType(PLAYER_HUMAN, false);
|
||||
setLocalPlayer(p);
|
||||
setLocal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must reset teams *after* creating players.
|
||||
TheTeamFactory->initFromSides(TheSidesList);
|
||||
|
||||
for( i = 0; i < TheSidesList->getNumSides(); i++)
|
||||
{
|
||||
Dict *d = TheSidesList->getSideInfo(i)->getDict();
|
||||
Player* p = findPlayerWithNameKey(NAMEKEY(d->getAsciiString(TheKey_playerName)));
|
||||
|
||||
AsciiString tok;
|
||||
|
||||
AsciiString enemies = d->getAsciiString(TheKey_playerEnemies);
|
||||
while (enemies.nextToken(&tok))
|
||||
{
|
||||
Player *p2 = findPlayerWithNameKey(NAMEKEY(tok));
|
||||
if (p2)
|
||||
{
|
||||
p->setPlayerRelationship(p2, ENEMIES);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("unknown enemy %s\n",tok.str()));
|
||||
}
|
||||
}
|
||||
|
||||
AsciiString allies = d->getAsciiString(TheKey_playerAllies);
|
||||
while (allies.nextToken(&tok))
|
||||
{
|
||||
Player *p2 = findPlayerWithNameKey(NAMEKEY(tok));
|
||||
if (p2)
|
||||
{
|
||||
p->setPlayerRelationship(p2, ALLIES);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("unknown ally %s\n",tok.str()));
|
||||
}
|
||||
}
|
||||
|
||||
// finally, make sure self & neutral are correct.
|
||||
p->setPlayerRelationship(p, ALLIES);
|
||||
if (p != getNeutralPlayer())
|
||||
p->setPlayerRelationship(getNeutralPlayer(), NEUTRAL);
|
||||
|
||||
p->setDefaultTeam();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerList::init()
|
||||
{
|
||||
m_playerCount = 1;
|
||||
m_players[0]->init(NULL);
|
||||
|
||||
for (int i = 1; i < MAX_PLAYER_COUNT; i++)
|
||||
m_players[i]->init(NULL);
|
||||
|
||||
// call setLocalPlayer so that becomingLocalPlayer() gets called appropriately
|
||||
setLocalPlayer(m_players[0]);
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerList::update()
|
||||
{
|
||||
// update all players
|
||||
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
|
||||
{
|
||||
m_players[i]->update();
|
||||
} // end for i
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerList::newMap()
|
||||
{
|
||||
// update all players
|
||||
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
|
||||
{
|
||||
m_players[i]->newMap();
|
||||
} // end for i
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void PlayerList::teamAboutToBeDeleted(Team* team)
|
||||
{
|
||||
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
|
||||
{
|
||||
m_players[i]->removeTeamRelationship(team);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void PlayerList::updateTeamStates(void)
|
||||
{
|
||||
// Clear team flags for all players.
|
||||
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
|
||||
{
|
||||
m_players[i]->updateTeamStates();
|
||||
} // end for i
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Team *PlayerList::validateTeam( AsciiString owner )
|
||||
{
|
||||
// owner could be a player or team. first, check team names.
|
||||
Team *t = TheTeamFactory->findTeam(owner);
|
||||
if (t)
|
||||
{
|
||||
//DEBUG_LOG(("assigned obj %08lx to team %s\n",obj,owner.str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH(("no team or player named %s could be found!\n", owner.str()));
|
||||
t = getNeutralPlayer()->getDefaultTeam();
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerList::setLocalPlayer(Player *player)
|
||||
{
|
||||
// can't set local player to null -- if you try, you get neutral.
|
||||
if (player == NULL)
|
||||
{
|
||||
DEBUG_CRASH(("local player may not be null"));
|
||||
player = getNeutralPlayer();
|
||||
}
|
||||
|
||||
if (player != m_local)
|
||||
{
|
||||
// m_local can be null the very first time we call this.
|
||||
if (m_local)
|
||||
m_local->becomingLocalPlayer(false);
|
||||
m_local = player;
|
||||
player->becomingLocalPlayer(true);
|
||||
}
|
||||
|
||||
#ifdef INTENSE_DEBUG
|
||||
if (player)
|
||||
{
|
||||
DEBUG_LOG(("\n----------\n"));
|
||||
// did you know? you can use "%ls" to print a doublebyte string, even in a single-byte printf...
|
||||
DEBUG_LOG(("Switching local players. The new player is named '%ls' (%s) and owns the following objects:\n",
|
||||
player->getPlayerDisplayName().str(),
|
||||
TheNameKeyGenerator->keyToName(player->getPlayerNameKey()).str()
|
||||
));
|
||||
for (Object *obj = player->getFirstOwnedObject(); obj; obj = obj->getNextOwnedObject())
|
||||
{
|
||||
DEBUG_LOG(("Obj %08lx is of type %s",obj,obj->getTemplate()->getName().str()));
|
||||
if (!player->canBuild(obj->getTemplate()))
|
||||
{
|
||||
DEBUG_LOG((" (NOT BUILDABLE)"));
|
||||
}
|
||||
DEBUG_LOG(("\n"));
|
||||
}
|
||||
DEBUG_LOG(("\n----------\n"));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Player *PlayerList::getPlayerFromMask( PlayerMaskType mask )
|
||||
{
|
||||
Player *player = NULL;
|
||||
Int i;
|
||||
|
||||
for( i = 0; i < MAX_PLAYER_COUNT; i++ )
|
||||
{
|
||||
|
||||
player = getNthPlayer( i );
|
||||
if( player && player->getPlayerMask() == mask )
|
||||
return player;
|
||||
|
||||
} // end for i
|
||||
|
||||
DEBUG_CRASH( ("Player does not exist for mask\n") );
|
||||
return NULL; // mask not found
|
||||
|
||||
} // end getPlayerFromMask
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Player *PlayerList::getEachPlayerFromMask( PlayerMaskType& maskToAdjust )
|
||||
{
|
||||
Player *player = NULL;
|
||||
Int i;
|
||||
|
||||
for( i = 0; i < MAX_PLAYER_COUNT; i++ )
|
||||
{
|
||||
|
||||
player = getNthPlayer( i );
|
||||
if ( player && BitTest(player->getPlayerMask(), maskToAdjust ))
|
||||
{
|
||||
maskToAdjust &= (~player->getPlayerMask());
|
||||
return player;
|
||||
}
|
||||
} // end for i
|
||||
|
||||
DEBUG_CRASH( ("No players found that contain any matching masks.\n") );
|
||||
maskToAdjust = 0;
|
||||
return NULL; // mask not found
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
PlayerMaskType PlayerList::getPlayersWithRelationship( Int srcPlayerIndex, UnsignedInt allowedRelationships )
|
||||
{
|
||||
PlayerMaskType retVal = 0;
|
||||
|
||||
if (allowedRelationships == 0)
|
||||
return retVal;
|
||||
|
||||
Player *srcPlayer = getNthPlayer(srcPlayerIndex);
|
||||
if (!srcPlayer)
|
||||
return retVal;
|
||||
|
||||
if (BitTest(allowedRelationships, ALLOW_SAME_PLAYER))
|
||||
BitSet(retVal, srcPlayer->getPlayerMask());
|
||||
|
||||
for ( Int i = 0; i < getPlayerCount(); ++i )
|
||||
{
|
||||
Player *player = getNthPlayer(i);
|
||||
if (!player)
|
||||
continue;
|
||||
|
||||
if (player == srcPlayer)
|
||||
continue;
|
||||
|
||||
switch (srcPlayer->getRelationship(player->getDefaultTeam()))
|
||||
{
|
||||
case ENEMIES:
|
||||
if (BitTest(allowedRelationships, ALLOW_ENEMIES))
|
||||
BitSet(retVal, player->getPlayerMask());
|
||||
break;
|
||||
case ALLIES:
|
||||
if (BitTest(allowedRelationships, ALLOW_ALLIES))
|
||||
BitSet(retVal, player->getPlayerMask());
|
||||
break;
|
||||
case NEUTRAL:
|
||||
if (BitTest(allowedRelationships, ALLOW_NEUTRAL))
|
||||
BitSet(retVal, player->getPlayerMask());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PlayerList::crc( Xfer *xfer )
|
||||
{
|
||||
xfer->xferInt( &m_playerCount );
|
||||
|
||||
for( Int i = 0; i < m_playerCount; ++i )
|
||||
xfer->xferSnapshot( m_players[ i ] );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PlayerList::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// xfer the player count
|
||||
Int playerCount = m_playerCount;
|
||||
xfer->xferInt( &playerCount );
|
||||
|
||||
//
|
||||
// sanity, the player count read from the file should match our player count that
|
||||
// was setup from the bare bones map load since that data can't change during run time
|
||||
//
|
||||
if( playerCount != m_playerCount )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "Invalid player count '%d', should be '%d'\n", playerCount, m_playerCount ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// xfer each of the player data
|
||||
for( Int i = 0; i < playerCount; ++i )
|
||||
xfer->xferSnapshot( m_players[ i ] );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void PlayerList::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end postProcessLoad
|
||||
|
||||
389
Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp
Normal file
389
Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: PlayerTemplate.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: PlayerTemplate.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: @todo
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_VETERANCY_NAMES // for TheVeterancyNames[]
|
||||
|
||||
#include "Common/GameCommon.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/Science.h"
|
||||
#include "GameClient/Image.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ const FieldParse* PlayerTemplate::getFieldParse()
|
||||
{
|
||||
static const FieldParse TheFieldParseTable[] =
|
||||
{
|
||||
{ "Side", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_side ) },
|
||||
{ "PlayableSide", INI::parseBool, NULL, offsetof( PlayerTemplate, m_playableSide ) },
|
||||
{ "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( PlayerTemplate, m_displayName) },
|
||||
{ "StartMoney", PlayerTemplate::parseStartMoney, NULL, offsetof( PlayerTemplate, m_money ) },
|
||||
{ "PreferredColor", INI::parseRGBColor, NULL, offsetof( PlayerTemplate, m_preferredColor ) },
|
||||
{ "StartingBuilding", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingBuilding ) },
|
||||
{ "StartingUnit0", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[0] ) },
|
||||
{ "StartingUnit1", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[1] ) },
|
||||
{ "StartingUnit2", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[2] ) },
|
||||
{ "StartingUnit3", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[3] ) },
|
||||
{ "StartingUnit4", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[4] ) },
|
||||
{ "StartingUnit5", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[5] ) },
|
||||
{ "StartingUnit6", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[6] ) },
|
||||
{ "StartingUnit7", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[7] ) },
|
||||
{ "StartingUnit8", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[8] ) },
|
||||
{ "StartingUnit9", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[9] ) },
|
||||
{ "ProductionCostChange", PlayerTemplate::parseProductionCostChange, NULL, 0 },
|
||||
{ "ProductionTimeChange", PlayerTemplate::parseProductionTimeChange, NULL, 0 },
|
||||
{ "ProductionVeterancyLevel", PlayerTemplate::parseProductionVeterancyLevel, NULL, 0 },
|
||||
{ "IntrinsicSciences", INI::parseScienceVector, NULL, offsetof( PlayerTemplate, m_intrinsicSciences ) },
|
||||
{ "PurchaseScienceCommandSetRank1",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank1 ) },
|
||||
{ "PurchaseScienceCommandSetRank3",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank3 ) },
|
||||
{ "PurchaseScienceCommandSetRank8",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank8 ) },
|
||||
{ "SpecialPowerShortcutCommandSet",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutCommandSet ) },
|
||||
{ "SpecialPowerShortcutWinName" ,INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutWinName) },
|
||||
{ "SpecialPowerShortcutButtonCount",INI::parseInt, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutButtonCount ) },
|
||||
{ "IsObserver", INI::parseBool, NULL, offsetof( PlayerTemplate, m_observer ) },
|
||||
{ "IntrinsicSciencePurchasePoints", INI::parseInt, NULL, offsetof( PlayerTemplate, m_intrinsicSPP ) },
|
||||
{ "ScoreScreenImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_scoreScreenImage ) },
|
||||
{ "LoadScreenImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_loadScreenImage ) },
|
||||
{ "LoadScreenMusic", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_loadScreenMusic ) },
|
||||
|
||||
{ "HeadWaterMark", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_headWaterMark ) },
|
||||
{ "FlagWaterMark", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_flagWaterMark ) },
|
||||
{ "EnabledImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_enabledImage ) },
|
||||
//{ "DisabledImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_disabledImage ) },
|
||||
//{ "HiliteImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_hiliteImage ) },
|
||||
//{ "PushedImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_pushedImage ) },
|
||||
{ "SideIconImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_sideIconImage ) },
|
||||
|
||||
{ "BeaconName", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_beaconTemplate ) },
|
||||
{ NULL, NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
return TheFieldParseTable;
|
||||
}
|
||||
|
||||
AsciiString PlayerTemplate::getStartingUnit( Int i ) const
|
||||
{
|
||||
if (i<0 || i>= MAX_MP_STARTING_UNITS)
|
||||
return AsciiString::TheEmptyString;
|
||||
|
||||
return m_startingUnits[i];
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// This is is a Template, and a percent change to the cost of producing it.
|
||||
/*static*/ void PlayerTemplate::parseProductionCostChange( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
PlayerTemplate* self = (PlayerTemplate*)instance;
|
||||
|
||||
NameKeyType buildTemplateKey = NAMEKEY(ini->getNextToken());
|
||||
Real percentChange = INI::scanPercentToReal(ini->getNextToken());
|
||||
|
||||
self->m_productionCostChanges[buildTemplateKey] = percentChange;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PlayerTemplate::parseProductionTimeChange( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
PlayerTemplate* self = (PlayerTemplate*)instance;
|
||||
|
||||
NameKeyType buildTemplateKey = NAMEKEY(ini->getNextToken());
|
||||
Real percentChange = INI::scanPercentToReal(ini->getNextToken());
|
||||
|
||||
self->m_productionTimeChanges[buildTemplateKey] = percentChange;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PlayerTemplate::parseProductionVeterancyLevel( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
PlayerTemplate* self = (PlayerTemplate*)instance;
|
||||
|
||||
// Format is ThingTemplatename VeterancyName
|
||||
AsciiString HACK = AsciiString(ini->getNextToken());
|
||||
NameKeyType buildTemplateKey = NAMEKEY(HACK.str());
|
||||
|
||||
VeterancyLevel startLevel = (VeterancyLevel)INI::scanIndexList(ini->getNextToken(), TheVeterancyNames);
|
||||
self->m_productionVeterancyLevels[buildTemplateKey] = startLevel;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Parse integer money and deposit in the m_money */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PlayerTemplate::parseStartMoney( INI* ini, void *instance, void *store, const void* /*userData*/ )
|
||||
{
|
||||
Int money = 0;
|
||||
|
||||
// parse the money as a regular "FIELD = <integer>"
|
||||
INI::parseInt( ini, instance, &money, NULL );
|
||||
|
||||
// assign the money into the 'Money' (m_money) pointed to at 'store'
|
||||
Money *theMoney = (Money *)store;
|
||||
theMoney->init();
|
||||
theMoney->deposit( money );
|
||||
|
||||
} // end parseStartMoney
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
PlayerTemplate::PlayerTemplate() :
|
||||
m_nameKey(NAMEKEY_INVALID),
|
||||
m_observer(false),
|
||||
m_playableSide(false),
|
||||
m_intrinsicSPP(0),
|
||||
m_specialPowerShortcutButtonCount(0)
|
||||
{
|
||||
m_preferredColor.red = m_preferredColor.green = m_preferredColor.blue = 0.0f;
|
||||
m_beaconTemplate.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Image *PlayerTemplate::getHeadWaterMarkImage( void ) const
|
||||
{
|
||||
return TheMappedImageCollection->findImageByName(m_headWaterMark);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Image *PlayerTemplate::getFlagWaterMarkImage( void ) const
|
||||
{
|
||||
return TheMappedImageCollection->findImageByName(m_flagWaterMark);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Image *PlayerTemplate::getSideIconImage( void ) const
|
||||
{
|
||||
return TheMappedImageCollection->findImageByName(m_sideIconImage);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Image *PlayerTemplate::getEnabledImage( void ) const
|
||||
{
|
||||
return TheMappedImageCollection->findImageByName(m_enabledImage);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//const Image *PlayerTemplate::getDisabledImage( void ) const
|
||||
//{
|
||||
// return TheMappedImageCollection->findImageByName(m_disabledImage);
|
||||
//}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//const Image *PlayerTemplate::getHiliteImage( void ) const
|
||||
//{
|
||||
// return TheMappedImageCollection->findImageByName(m_hiliteImage);
|
||||
//}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//const Image *PlayerTemplate::getPushedImage( void ) const
|
||||
//{
|
||||
// return TheMappedImageCollection->findImageByName(m_pushedImage);
|
||||
//}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*extern*/ PlayerTemplateStore *ThePlayerTemplateStore = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlayerTemplateStore::PlayerTemplateStore()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
PlayerTemplateStore::~PlayerTemplateStore()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerTemplateStore::init()
|
||||
{
|
||||
m_playerTemplates.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerTemplateStore::reset()
|
||||
{
|
||||
// don't reset this list here; we want to retain this info.
|
||||
// m_playerTemplates.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void PlayerTemplateStore::update()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const PlayerTemplate* PlayerTemplateStore::findPlayerTemplate(NameKeyType namekey) const
|
||||
{
|
||||
// begin ugly, hokey code to quietly load old maps...
|
||||
static NameKeyType a0 = NAMEKEY("FactionAmerica");
|
||||
static NameKeyType a1 = NAMEKEY("FactionAmericaChooseAGeneral");
|
||||
static NameKeyType a2 = NAMEKEY("FactionAmericaTankCommand");
|
||||
static NameKeyType a3 = NAMEKEY("FactionAmericaSpecialForces");
|
||||
static NameKeyType a4 = NAMEKEY("FactionAmericaAirForce");
|
||||
|
||||
static NameKeyType c0 = NAMEKEY("FactionChina");
|
||||
static NameKeyType c1 = NAMEKEY("FactionChinaChooseAGeneral");
|
||||
static NameKeyType c2 = NAMEKEY("FactionChinaRedArmy");
|
||||
static NameKeyType c3 = NAMEKEY("FactionChinaSpecialWeapons");
|
||||
static NameKeyType c4 = NAMEKEY("FactionChinaSecretPolice");
|
||||
|
||||
static NameKeyType g0 = NAMEKEY("FactionGLA");
|
||||
static NameKeyType g1 = NAMEKEY("FactionGLAChooseAGeneral");
|
||||
static NameKeyType g2 = NAMEKEY("FactionGLATerrorCell");
|
||||
static NameKeyType g3 = NAMEKEY("FactionGLABiowarCommand");
|
||||
static NameKeyType g4 = NAMEKEY("FactionGLAWarlordCommand");
|
||||
|
||||
if (namekey == a1 || namekey == a2 || namekey == a3 || namekey == a4)
|
||||
namekey = a0;
|
||||
else if (namekey == c1 || namekey == c2 || namekey == c3 || namekey == c4)
|
||||
namekey = c0;
|
||||
else if (namekey == g1 || namekey == g2 || namekey == g3 || namekey == g4)
|
||||
namekey = g0;
|
||||
// end ugly, hokey code to quietly load old maps...
|
||||
|
||||
#ifdef _DEBUG
|
||||
AsciiString nn = KEYNAME(namekey);
|
||||
#endif
|
||||
for (PlayerTemplateVector::const_iterator it = m_playerTemplates.begin(); it != m_playerTemplates.end(); ++it)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
AsciiString n = KEYNAME((*it).getNameKey());
|
||||
#endif
|
||||
if ((*it).getNameKey() == namekey)
|
||||
return &(*it);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const PlayerTemplate* PlayerTemplateStore::getNthPlayerTemplate(Int i) const
|
||||
{
|
||||
if (i >= 0 && i < m_playerTemplates.size())
|
||||
return &m_playerTemplates[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// @todo: PERF_EVALUATE Get a perf timer on this.
|
||||
// If this function is called frequently, there are some relatively trivial changes we could make to
|
||||
// have it run a lot faster.
|
||||
void PlayerTemplateStore::getAllSideStrings(AsciiStringList *outStringList)
|
||||
{
|
||||
if (!outStringList)
|
||||
return;
|
||||
|
||||
// should outStringList be cleared first? If so, that would go here
|
||||
AsciiStringList tmpList;
|
||||
|
||||
Int numTemplates = getPlayerTemplateCount();
|
||||
for ( Int i = 0; i < numTemplates; ++i )
|
||||
{
|
||||
const PlayerTemplate *pt = getNthPlayerTemplate(i);
|
||||
// Sanity
|
||||
if (!pt)
|
||||
continue;
|
||||
|
||||
if (std::find(tmpList.begin(), tmpList.end(), pt->getSide()) == tmpList.end())
|
||||
tmpList.push_back(pt->getSide());
|
||||
}
|
||||
// tmpList is now filled with all unique sides found in the player templates.
|
||||
|
||||
// splice is a constant-time function which takes all elements from tmpList and
|
||||
// inserts them before outStringList->end(), and also removes them from tmpList
|
||||
outStringList->splice(outStringList->end(), tmpList);
|
||||
|
||||
// all done
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/*static*/ void PlayerTemplateStore::parsePlayerTemplateDefinition( INI* ini )
|
||||
{
|
||||
const char* c = ini->getNextToken();
|
||||
NameKeyType namekey = NAMEKEY(c);
|
||||
|
||||
PlayerTemplate* pt = const_cast<PlayerTemplate*>(ThePlayerTemplateStore->findPlayerTemplate(namekey));
|
||||
if (pt)
|
||||
{
|
||||
ini->initFromINI(pt, pt->getFieldParse() );
|
||||
pt->setNameKey(namekey);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerTemplate npt;
|
||||
ini->initFromINI( &npt, npt.getFieldParse() );
|
||||
npt.setNameKey(namekey);
|
||||
ThePlayerTemplateStore->m_playerTemplates.push_back(npt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void INI::parsePlayerTemplateDefinition( INI* ini )
|
||||
{
|
||||
PlayerTemplateStore::parsePlayerTemplateDefinition(ini);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ProductionPrerequisite.cpp /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: ProductionPrerequisite.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: @todo
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/ProductionPrerequisite.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
#include "GameClient/GameText.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
ProductionPrerequisite::ProductionPrerequisite()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
ProductionPrerequisite::~ProductionPrerequisite()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ProductionPrerequisite::init()
|
||||
{
|
||||
m_prereqUnits.clear();
|
||||
m_prereqSciences.clear();
|
||||
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void ProductionPrerequisite::resolveNames()
|
||||
{
|
||||
for (Int i = 0; i < m_prereqUnits.size(); i++)
|
||||
{
|
||||
|
||||
//
|
||||
// note that this will find the template at the "top most" level (not override
|
||||
// sub-temlates), which is what we want ... we conceptually only have one
|
||||
// template for any given thing, it's only the *data* that is overridden
|
||||
//
|
||||
m_prereqUnits[i].unit = TheThingFactory->findTemplate(m_prereqUnits[i].name); // might be null
|
||||
|
||||
/** @todo for now removing this assert until we can completely remove
|
||||
the GDF stuff, the problem is that some INI files refer to GDF names, and they
|
||||
aren't yet loaded in the world builder but will all go away later anyway etc */
|
||||
DEBUG_ASSERTCRASH(m_prereqUnits[i].unit,("could not find prereq %s\n",m_prereqUnits[i].name.str()));
|
||||
|
||||
m_prereqUnits[i].name.clear(); // we're done with it
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Int ProductionPrerequisite::calcNumPrereqUnitsOwned(const Player *player, Int counts[MAX_PREREQ]) const
|
||||
{
|
||||
const ThingTemplate *tmpls[MAX_PREREQ];
|
||||
Int cnt = m_prereqUnits.size();
|
||||
if (cnt > MAX_PREREQ)
|
||||
cnt = MAX_PREREQ;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
tmpls[i] = m_prereqUnits[i].unit;
|
||||
player->countObjectsByThingTemplate(cnt, tmpls, false, counts);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Int ProductionPrerequisite::getAllPossibleBuildFacilityTemplates(const ThingTemplate* tmpls[], Int maxtmpls) const
|
||||
{
|
||||
Int count = 0;
|
||||
for (int i = 0; i < m_prereqUnits.size(); i++)
|
||||
{
|
||||
if (i > 0 && !(m_prereqUnits[i].flags & UNIT_OR_WITH_PREV))
|
||||
break;
|
||||
if (count >= maxtmpls)
|
||||
break;
|
||||
tmpls[count++] = m_prereqUnits[i].unit;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const ThingTemplate *ProductionPrerequisite::getExistingBuildFacilityTemplate( const Player *player ) const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(player, ("player may not be null"));
|
||||
if (m_prereqUnits.size())
|
||||
{
|
||||
Int ownCount[MAX_PREREQ];
|
||||
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (i > 0 && !(m_prereqUnits[i].flags & UNIT_OR_WITH_PREV))
|
||||
break;
|
||||
if (ownCount[i])
|
||||
return m_prereqUnits[i].unit;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool ProductionPrerequisite::isSatisfied(const Player *player) const
|
||||
{
|
||||
Int i;
|
||||
|
||||
if (!player)
|
||||
return false;
|
||||
|
||||
// gotta have all the prereq sciences.
|
||||
for (i = 0; i < m_prereqSciences.size(); i++)
|
||||
{
|
||||
if (!player->hasScience(m_prereqSciences[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
// the player must have at least one instance of each prereq unit.
|
||||
Int ownCount[MAX_PREREQ];
|
||||
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
|
||||
|
||||
// fix up the "or" cases. (start at 1!)
|
||||
for (i = 1; i < cnt; i++)
|
||||
{
|
||||
if (m_prereqUnits[i].flags & UNIT_OR_WITH_PREV)
|
||||
{
|
||||
ownCount[i] += ownCount[i-1]; // lump 'em together for prereq purposes
|
||||
ownCount[i-1] = -1; // flag for "ignore me"
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
if (ownCount[i] == -1) // the magic "ignore me" flag
|
||||
continue;
|
||||
if (ownCount[i] == 0) // everything not ignored, is required
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Add a unit prerequisite, if 'orWithPrevious' is set then this unit is said
|
||||
* to be an alternate prereq to the previously added unit, otherwise this becomes
|
||||
* a new 'block' and is required in ADDDITION to other entries.
|
||||
* Return FALSE if no space left to add unit */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ProductionPrerequisite::addUnitPrereq( AsciiString unit, Bool orUnitWithPrevious )
|
||||
{
|
||||
PrereqUnitRec info;
|
||||
info.name = unit;
|
||||
info.flags = orUnitWithPrevious ? UNIT_OR_WITH_PREV : 0;
|
||||
info.unit = NULL;
|
||||
m_prereqUnits.push_back(info);
|
||||
|
||||
} // end addUnitPrereq
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Add a unit prerequisite, if 'orWithPrevious' is set then this unit is said
|
||||
* to be an alternate prereq to the previously added unit, otherwise this becomes
|
||||
* a new 'block' and is required in ADDDITION to other entries.
|
||||
* Return FALSE if no space left to add unit */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void ProductionPrerequisite::addUnitPrereq( const std::vector<AsciiString>& units )
|
||||
{
|
||||
Bool orWithPrevious = false;
|
||||
for (int i = 0; i < units.size(); ++i)
|
||||
{
|
||||
addUnitPrereq(units[i], orWithPrevious);
|
||||
orWithPrevious = true;
|
||||
}
|
||||
|
||||
} // end addUnitPrereq
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// returns an asciistring which is a list of all the prerequisites
|
||||
// not satisfied yet
|
||||
UnicodeString ProductionPrerequisite::getRequiresList(const Player *player) const
|
||||
{
|
||||
|
||||
// if player is invalid, return empty string
|
||||
if (!player)
|
||||
return UnicodeString::TheEmptyString;
|
||||
|
||||
UnicodeString requiresList = UnicodeString::TheEmptyString;
|
||||
|
||||
// check the prerequired units
|
||||
Int ownCount[MAX_PREREQ];
|
||||
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
|
||||
Int i;
|
||||
|
||||
Bool orRequirements[MAX_PREREQ];
|
||||
//Added for fix below in getRequiresList
|
||||
//By Sadullah Nader
|
||||
//Initializes the OR_WITH_PREV structures
|
||||
for (i = 0; i < MAX_PREREQ; i++)
|
||||
{
|
||||
orRequirements[i] = FALSE;
|
||||
}
|
||||
//
|
||||
// account for the "or" unit cases, start for loop at 1
|
||||
for (i = 1; i < cnt; i++)
|
||||
{
|
||||
if (m_prereqUnits[i].flags & UNIT_OR_WITH_PREV)
|
||||
{
|
||||
orRequirements[i] = TRUE; // set the flag for this unit to be "ored" with previous
|
||||
ownCount[i] += ownCount[i-1]; // lump 'em together for prereq purposes
|
||||
ownCount[i-1] = -1; // flag for "ignore me"
|
||||
}
|
||||
}
|
||||
|
||||
// check to see if anything is required
|
||||
const ThingTemplate *unit;
|
||||
UnicodeString unitName;
|
||||
Bool firstRequirement = true;
|
||||
for (i = 0; i < cnt; i++)
|
||||
{
|
||||
// we have an unfulfilled requirement
|
||||
if (ownCount[i] == 0) {
|
||||
|
||||
if(orRequirements[i])
|
||||
{
|
||||
unit = m_prereqUnits[i-1].unit;
|
||||
unitName = unit->getDisplayName();
|
||||
unitName.concat( L" " );
|
||||
unitName.concat(TheGameText->fetch("CONTROLBAR:OrRequirement", NULL));
|
||||
unitName.concat( L" " );
|
||||
requiresList.concat(unitName);
|
||||
}
|
||||
|
||||
// get the requirement and then its name
|
||||
unit = m_prereqUnits[i].unit;
|
||||
unitName = unit->getDisplayName();
|
||||
|
||||
// gets command button, and then modifies unitName
|
||||
//CommandButton *cmdButton = TheControlBar->findCommandButton(unit->getName());
|
||||
//if (cmdButton)
|
||||
//unitName.translate(TheGameText->fetch(cmdButton->m_textLabel.str()));
|
||||
|
||||
// format name appropriately with 'returns' if necessary
|
||||
if (firstRequirement)
|
||||
firstRequirement = false;
|
||||
else
|
||||
unitName.concat(L"\n");
|
||||
|
||||
// add it to the list
|
||||
requiresList.concat(unitName);
|
||||
}
|
||||
}
|
||||
|
||||
Bool hasSciences = TRUE;
|
||||
// gotta have all the prereq sciences.
|
||||
for (i = 0; i < m_prereqSciences.size(); i++)
|
||||
{
|
||||
if (!player->hasScience(m_prereqSciences[i]))
|
||||
hasSciences = FALSE;
|
||||
}
|
||||
|
||||
if (hasSciences == FALSE) {
|
||||
if (firstRequirement) {
|
||||
firstRequirement = false;
|
||||
} else {
|
||||
unitName.concat(L"\n");
|
||||
}
|
||||
requiresList.concat(TheGameText->fetch("CONTROLBAR:GeneralsPromotion", NULL));
|
||||
}
|
||||
|
||||
// return final list
|
||||
return requiresList;
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ResourceGatheringManager.cpp ///////////////////////////////////////////////////////////
|
||||
// The part of a Player's brain that keeps track of all Resource type Objects and makes
|
||||
// gathering type decisions based on them.
|
||||
// Author: Graham Smallwood, January, 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/ResourceGatheringManager.h"
|
||||
|
||||
#include "Common/ActionManager.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
#include "GameLogic/Module/SupplyTruckAIUpdate.h"
|
||||
#include "GameLogic/Module/SupplyCenterDockUpdate.h"
|
||||
#include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
|
||||
#include "GameLogic/Module/UpdateModule.h"
|
||||
|
||||
ResourceGatheringManager::ResourceGatheringManager()
|
||||
{
|
||||
}
|
||||
|
||||
ResourceGatheringManager::~ResourceGatheringManager()
|
||||
{
|
||||
m_supplyWarehouses.erase( m_supplyWarehouses.begin(), m_supplyWarehouses.end() );
|
||||
m_supplyCenters.erase( m_supplyCenters.begin(), m_supplyCenters.end() );
|
||||
}
|
||||
|
||||
void ResourceGatheringManager::addSupplyCenter( Object *newCenter )
|
||||
{
|
||||
if( newCenter == NULL )
|
||||
return;
|
||||
|
||||
m_supplyCenters.push_back( newCenter->getID() );
|
||||
}
|
||||
|
||||
void ResourceGatheringManager::removeSupplyCenter( Object *oldCenter )
|
||||
{
|
||||
if( oldCenter == NULL )
|
||||
return;
|
||||
|
||||
ObjectID targetID = oldCenter->getID();
|
||||
|
||||
objectIDListIterator iterator = m_supplyCenters.begin();
|
||||
while( iterator != m_supplyCenters.end() )
|
||||
{
|
||||
if( targetID == *iterator )
|
||||
{
|
||||
iterator = m_supplyCenters.erase( iterator );
|
||||
}
|
||||
else
|
||||
iterator++;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceGatheringManager::addSupplyWarehouse( Object *newWarehouse )
|
||||
{
|
||||
if( newWarehouse == NULL )
|
||||
return;
|
||||
|
||||
m_supplyWarehouses.push_back( newWarehouse->getID() );
|
||||
}
|
||||
|
||||
void ResourceGatheringManager::removeSupplyWarehouse( Object *oldWarehouse )
|
||||
{
|
||||
if( oldWarehouse == NULL )
|
||||
return;
|
||||
|
||||
ObjectID targetID = oldWarehouse->getID();
|
||||
|
||||
objectIDListIterator iterator = m_supplyWarehouses.begin();
|
||||
while( iterator != m_supplyWarehouses.end() )
|
||||
{
|
||||
if( targetID == *iterator )
|
||||
{
|
||||
iterator = m_supplyWarehouses.erase( iterator );
|
||||
}
|
||||
else
|
||||
iterator++;
|
||||
}
|
||||
}
|
||||
|
||||
static Real computeRelativeCost( Object *queryObject, Object *destObject, Real *pureDistanceSquared )
|
||||
{
|
||||
/** @todo This gets filled with Pathfinding computations, analysis of Boxes remaining,
|
||||
Threat calculations, paths of other trucks, and other fancy stuff.
|
||||
*/
|
||||
|
||||
//A good score is a very small number.
|
||||
|
||||
if( queryObject == NULL || destObject == NULL )
|
||||
return FLT_MAX;
|
||||
|
||||
if( !TheActionManager->canTransferSuppliesAt(queryObject, destObject) )
|
||||
return FLT_MAX;// Handles emptyness and alliances
|
||||
|
||||
DockUpdateInterface *dockInterface = destObject->getDockUpdateInterface();
|
||||
if( !dockInterface->isClearToApproach( queryObject ) )
|
||||
return FLT_MAX;
|
||||
|
||||
// since we don't care about the distance as a distance per se, but rather as
|
||||
// a goodness-factor, save some time by getting the dist-sqr (srj)
|
||||
Real distSquared = ThePartitionManager->getDistanceSquared(queryObject, destObject, FROM_CENTER_3D);
|
||||
|
||||
// I need the distance, but I don't want to count on the coincidence that
|
||||
// the abstract 'cost' this function returns happens to be just the distance, since it could
|
||||
// become more complicated
|
||||
if( pureDistanceSquared )
|
||||
*pureDistanceSquared = distSquared;
|
||||
|
||||
return distSquared;
|
||||
}
|
||||
|
||||
Object *ResourceGatheringManager::findBestSupplyWarehouse( Object *queryObject )
|
||||
{
|
||||
Object *bestWarehouse = NULL;
|
||||
Real maxDistanceSquared = 100000;
|
||||
|
||||
if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) )
|
||||
return NULL;
|
||||
|
||||
SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface();
|
||||
if( supplyTruckAI )
|
||||
{
|
||||
// Check for a dock override being set.
|
||||
ObjectID dockID = supplyTruckAI->getPreferredDockID();
|
||||
Object *dock = TheGameLogic->findObjectByID(dockID);
|
||||
if( dock )
|
||||
{
|
||||
static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
|
||||
SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_warehouseUpdate );
|
||||
//If remotely okay, let User win.
|
||||
if( warehouseModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX )
|
||||
return dock;
|
||||
}
|
||||
// Please note, there is not a separate Warehouse and Center memory by Design. Because
|
||||
// we lack a UI way to click Warehouse and drag to center to set up a specific path, the
|
||||
// practical realization has been made that you do not want separate memory.
|
||||
|
||||
// Design wants a harvester to give up and return to base if it is "too far" to the warehouse.
|
||||
// Note, the "PreferedDock" will override this, and there is no distance max on Centers.
|
||||
maxDistanceSquared = supplyTruckAI->getWarehouseScanDistance() * supplyTruckAI->getWarehouseScanDistance();
|
||||
}
|
||||
|
||||
//Otherwise, search for a good one.
|
||||
Real bestCost = FLT_MAX;
|
||||
|
||||
objectIDListIterator iterator = m_supplyWarehouses.begin();
|
||||
while( iterator != m_supplyWarehouses.end() )
|
||||
{
|
||||
ObjectID currentID = *iterator;
|
||||
Object *currentWarehouse =TheGameLogic->findObjectByID(currentID);
|
||||
|
||||
if( currentWarehouse == NULL )
|
||||
{
|
||||
iterator = m_supplyWarehouses.erase( iterator );
|
||||
}
|
||||
else
|
||||
{
|
||||
Real distanceSquared;
|
||||
Real currentCost = computeRelativeCost( queryObject, currentWarehouse, &distanceSquared );
|
||||
if( (currentCost < bestCost) && (distanceSquared < maxDistanceSquared) )
|
||||
{
|
||||
bestWarehouse = currentWarehouse;
|
||||
bestCost = currentCost;
|
||||
}
|
||||
|
||||
iterator++;
|
||||
}
|
||||
}
|
||||
|
||||
return bestWarehouse;
|
||||
}
|
||||
|
||||
Object *ResourceGatheringManager::findBestSupplyCenter( Object *queryObject )
|
||||
{
|
||||
Object *bestCenter = NULL;
|
||||
|
||||
if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) )
|
||||
return NULL;
|
||||
|
||||
SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface();
|
||||
if( supplyTruckAI )
|
||||
{
|
||||
// Check for a dock override being set.
|
||||
ObjectID dockID = supplyTruckAI->getPreferredDockID();
|
||||
Object *dock = TheGameLogic->findObjectByID(dockID);
|
||||
if( dock )
|
||||
{
|
||||
static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate");
|
||||
SupplyWarehouseDockUpdate *centerModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_centerUpdate );
|
||||
//If remotely okay, let User win.
|
||||
if( centerModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX )
|
||||
return dock;
|
||||
}
|
||||
// Please note, there is not a separate Warehouse and Center memory by Design. Because
|
||||
// we lack a UI way to click Warehouse and drag to center to set up a specific path, the
|
||||
// practical realization has been made that you do not want separate memory.
|
||||
}
|
||||
|
||||
//Otherwise, search for a good one.
|
||||
Real bestCost = FLT_MAX;
|
||||
|
||||
objectIDListIterator iterator = m_supplyCenters.begin();
|
||||
while( iterator != m_supplyCenters.end() )
|
||||
{
|
||||
ObjectID currentID = *iterator;
|
||||
Object *currentCenter =TheGameLogic->findObjectByID(currentID);
|
||||
|
||||
if( currentCenter == NULL )
|
||||
{
|
||||
iterator = m_supplyWarehouses.erase( iterator );
|
||||
}
|
||||
else
|
||||
{
|
||||
Real currentCost = computeRelativeCost( queryObject, currentCenter, NULL );
|
||||
if( currentCost < bestCost )
|
||||
{
|
||||
bestCenter = currentCenter;
|
||||
bestCost = currentCost;
|
||||
}
|
||||
|
||||
iterator++;
|
||||
}
|
||||
}
|
||||
|
||||
return bestCenter;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ResourceGatheringManager::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ResourceGatheringManager::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// supply warehouses
|
||||
xfer->xferSTLObjectIDList( &m_supplyWarehouses );
|
||||
|
||||
// supply centers
|
||||
xfer->xferSTLObjectIDList( &m_supplyCenters );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ResourceGatheringManager::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
366
Generals/Code/GameEngine/Source/Common/RTS/Science.cpp
Normal file
366
Generals/Code/GameEngine/Source/Common/RTS/Science.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Science.cpp /////////////////////////////////////////////////////////
|
||||
// Created: Steven Johnson, October 2001
|
||||
// Desc: @todo
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/INI.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Science.h"
|
||||
|
||||
ScienceStore* TheScienceStore = NULL;
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ScienceStore::init()
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_sciences.empty(), ("Hmm"));
|
||||
m_sciences.clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ScienceStore::reset()
|
||||
{
|
||||
// nope.
|
||||
//m_sciences.clear();
|
||||
|
||||
// go through all sciences and delete any overrides
|
||||
for (ScienceInfoVec::iterator it = m_sciences.begin(); it != m_sciences.end(); /*++it*/)
|
||||
{
|
||||
ScienceInfo* si = *it;
|
||||
Overridable* temp = si->deleteOverrides();
|
||||
if (!temp)
|
||||
{
|
||||
it = m_sciences.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
ScienceType ScienceStore::getScienceFromInternalName(const AsciiString& name) const
|
||||
{
|
||||
if (name.isEmpty())
|
||||
return SCIENCE_INVALID;
|
||||
NameKeyType nkt = TheNameKeyGenerator->nameToKey(name);
|
||||
ScienceType st = (ScienceType)nkt;
|
||||
return st;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
AsciiString ScienceStore::getInternalNameForScience(ScienceType science) const
|
||||
{
|
||||
if (science == SCIENCE_INVALID)
|
||||
return AsciiString::TheEmptyString;
|
||||
NameKeyType nk = (NameKeyType)(science);
|
||||
return TheNameKeyGenerator->keyToName(nk);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// return a vector of all the currently-known science names
|
||||
// NOTE: this is really only for use by WorldBuilder! Please
|
||||
// do not use it in RTS!
|
||||
std::vector<AsciiString> ScienceStore::friend_getScienceNames() const
|
||||
{
|
||||
std::vector<AsciiString> v;
|
||||
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
|
||||
{
|
||||
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
|
||||
NameKeyType nk = (NameKeyType)(si->m_science);
|
||||
v.push_back(TheNameKeyGenerator->keyToName(nk));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ScienceInfo::addRootSciences(ScienceVec& v) const
|
||||
{
|
||||
if (m_prereqSciences.empty())
|
||||
{
|
||||
// we're a root. add ourselves.
|
||||
if (std::find(v.begin(), v.end(), m_science) == v.end())
|
||||
v.push_back(m_science);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're not a root. add the roots of all our prereqs.
|
||||
for (ScienceVec::const_iterator it = m_prereqSciences.begin(); it != m_prereqSciences.end(); ++it)
|
||||
{
|
||||
const ScienceInfo* si = TheScienceStore->findScienceInfo(*it);
|
||||
if (si)
|
||||
si->addRootSciences(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const ScienceInfo* ScienceStore::findScienceInfo(ScienceType st) const
|
||||
{
|
||||
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
|
||||
{
|
||||
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
|
||||
if (si->m_science == st)
|
||||
{
|
||||
return si;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/*static*/ void ScienceStore::friend_parseScienceDefinition( INI* ini )
|
||||
{
|
||||
const char* c = ini->getNextToken();
|
||||
NameKeyType nkt = NAMEKEY(c);
|
||||
ScienceType st = (ScienceType)nkt;
|
||||
|
||||
if (TheScienceStore)
|
||||
{
|
||||
|
||||
static const FieldParse myFieldParse[] =
|
||||
{
|
||||
{ "PrerequisiteSciences", INI::parseScienceVector, NULL, offsetof( ScienceInfo, m_prereqSciences ) },
|
||||
{ "SciencePurchasePointCost", INI::parseInt, NULL, offsetof( ScienceInfo, m_sciencePurchasePointCost ) },
|
||||
{ "IsGrantable", INI::parseBool, NULL, offsetof( ScienceInfo, m_grantable ) },
|
||||
{ "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_name) },
|
||||
{ "Description", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_description) },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
ScienceInfo* info = NULL;
|
||||
|
||||
// see if the science already exists. (can't use findScienceInfo() since it is const and should remain so.)
|
||||
for (ScienceInfoVec::iterator it = TheScienceStore->m_sciences.begin(); it != TheScienceStore->m_sciences.end(); ++it)
|
||||
{
|
||||
// note that we don't use getFinalOverride here. this is correct and as-desired.
|
||||
if ((*it)->m_science == st)
|
||||
{
|
||||
info = *it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
|
||||
{
|
||||
ScienceInfo* newInfo = newInstance(ScienceInfo);
|
||||
|
||||
if (info == NULL)
|
||||
{
|
||||
// only add if it's not overriding an existing one.
|
||||
info = newInfo;
|
||||
info->markAsOverride(); // yep, so we will get cleared on reset()
|
||||
TheScienceStore->m_sciences.push_back(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy data from final override to 'newInfo' as a set of initial default values
|
||||
info = (ScienceInfo*)(info->friend_getFinalOverride());
|
||||
|
||||
*newInfo = *info;
|
||||
info->setNextOverride(newInfo);
|
||||
newInfo->markAsOverride(); // must do AFTER the copy
|
||||
|
||||
// use the newly created override for us to set values with etc
|
||||
info = newInfo;
|
||||
//TheScienceStore->m_sciences.push_back(info); // NO, BAD, WRONG -- don't add in this case.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info != NULL)
|
||||
{
|
||||
DEBUG_CRASH(("duplicate science %s!\n",c));
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
info = newInstance(ScienceInfo);
|
||||
TheScienceStore->m_sciences.push_back(info);
|
||||
}
|
||||
|
||||
ini->initFromINI(info, myFieldParse);
|
||||
info->m_science = st;
|
||||
info->addRootSciences(info->m_rootSciences);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Int ScienceStore::getSciencePurchaseCost(ScienceType st) const
|
||||
{
|
||||
const ScienceInfo* si = findScienceInfo(st);
|
||||
if (si)
|
||||
{
|
||||
return si->m_sciencePurchasePointCost;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool ScienceStore::isScienceGrantable(ScienceType st) const
|
||||
{
|
||||
const ScienceInfo* si = findScienceInfo(st);
|
||||
if (si)
|
||||
{
|
||||
return si->m_grantable;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool ScienceStore::getNameAndDescription(ScienceType st, UnicodeString& name, UnicodeString& description) const
|
||||
{
|
||||
const ScienceInfo* si = findScienceInfo(st);
|
||||
if (si)
|
||||
{
|
||||
name = si->m_name;
|
||||
description = si->m_description;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool ScienceStore::playerHasPrereqsForScience(const Player* player, ScienceType st) const
|
||||
{
|
||||
const ScienceInfo* si = findScienceInfo(st);
|
||||
if (si)
|
||||
{
|
||||
for (ScienceVec::const_iterator it2 = si->m_prereqSciences.begin(); it2 != si->m_prereqSciences.end(); ++it2)
|
||||
{
|
||||
if (!player->hasScience(*it2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool ScienceStore::playerHasRootPrereqsForScience(const Player* player, ScienceType st) const
|
||||
{
|
||||
const ScienceInfo* si = findScienceInfo(st);
|
||||
if (si)
|
||||
{
|
||||
for (ScienceVec::const_iterator it2 = si->m_rootSciences.begin(); it2 != si->m_rootSciences.end(); ++it2)
|
||||
{
|
||||
if (!player->hasScience(*it2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** return a list of the sciences the given player can purchase now, and a list he might be able to purchase in the future,
|
||||
but currently lacks prereqs or points for. (either might be an empty list) */
|
||||
void ScienceStore::getPurchasableSciences(const Player* player, ScienceVec& purchasable, ScienceVec& potentiallyPurchasable) const
|
||||
{
|
||||
purchasable.clear();
|
||||
potentiallyPurchasable.clear();
|
||||
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
|
||||
{
|
||||
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
|
||||
|
||||
if (si->m_sciencePurchasePointCost == 0)
|
||||
{
|
||||
// 0 means "cannot be purchased"
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->hasScience(si->m_science))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (playerHasPrereqsForScience(player, si->m_science))
|
||||
{
|
||||
purchasable.push_back(si->m_science);
|
||||
}
|
||||
else if (playerHasRootPrereqsForScience(player, si->m_science))
|
||||
{
|
||||
potentiallyPurchasable.push_back(si->m_science);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// this is intended ONLY for use by INI::scanScience.
|
||||
// Don't use it anywhere else. In particular, never, ever, ever
|
||||
// call this with a hardcoded science name. (srj)
|
||||
ScienceType ScienceStore::friend_lookupScience(const char* scienceName) const
|
||||
{
|
||||
NameKeyType nkt = NAMEKEY(scienceName);
|
||||
ScienceType st = (ScienceType)nkt;
|
||||
if (!isValidScience(st))
|
||||
{
|
||||
DEBUG_CRASH(("Science name %s not known! (Did you define it in Science.ini?)",scienceName));
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool ScienceStore::isValidScience(ScienceType st) const
|
||||
{
|
||||
const ScienceInfo* si = findScienceInfo(st);
|
||||
return si != NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void INI::parseScienceDefinition( INI* ini )
|
||||
{
|
||||
ScienceStore::friend_parseScienceDefinition(ini);
|
||||
}
|
||||
|
||||
568
Generals/Code/GameEngine/Source/Common/RTS/ScoreKeeper.cpp
Normal file
568
Generals/Code/GameEngine/Source/Common/RTS/ScoreKeeper.cpp
Normal file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: ScoreKeeper.cpp /////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Electronic Arts Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// created: Jun 2002
|
||||
//
|
||||
// Filename: ScoreKeeper.cpp
|
||||
//
|
||||
// author: Chris Huybregts
|
||||
//
|
||||
// purpose: Score Keeper class will be an object attached to each player
|
||||
// that will maintain accurate counts for the various stats we
|
||||
// want to show on the score screen. The information in here
|
||||
// could also be used for the observer screen
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/KindOf.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/ScoreKeeper.h"
|
||||
#include "Common/ThingFactory.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEFINES ////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ScoreKeeper::ScoreKeeper( void )
|
||||
{
|
||||
reset(0);
|
||||
}
|
||||
|
||||
ScoreKeeper::~ScoreKeeper( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static KindOfMaskType scoringBuildingMask;
|
||||
static KindOfMaskType scoringBuildingDestroyMask;
|
||||
static KindOfMaskType scoringBuildingCreateMask;
|
||||
|
||||
void ScoreKeeper::reset( Int playerIdx )
|
||||
{
|
||||
scoringBuildingMask.set(KINDOF_STRUCTURE);
|
||||
scoringBuildingMask.set(KINDOF_SCORE);
|
||||
|
||||
scoringBuildingCreateMask.set(KINDOF_STRUCTURE);
|
||||
scoringBuildingCreateMask.set(KINDOF_SCORE_CREATE);
|
||||
|
||||
scoringBuildingDestroyMask.set(KINDOF_STRUCTURE);
|
||||
scoringBuildingDestroyMask.set(KINDOF_SCORE_DESTROY);
|
||||
|
||||
m_totalMoneyEarned = m_totalMoneySpent = 0;
|
||||
m_totalUnitsLost = m_totalUnitsBuilt = 0;
|
||||
m_totalBuildingsLost = m_totalBuildingsBuilt = 0;
|
||||
//Added By Sadullah Nader
|
||||
//Initializtion(s) inserted
|
||||
m_totalFactionBuildingsCaptured = m_totalTechBuildingsCaptured = 0;
|
||||
//
|
||||
m_currentScore = 0;
|
||||
m_objectsBuilt.clear();
|
||||
m_objectsCaptured.clear();
|
||||
m_objectsLost.clear();
|
||||
for(int i = 0; i < MAX_PLAYER_COUNT; ++i)
|
||||
{
|
||||
m_objectsDestroyed[i].clear();
|
||||
m_totalBuildingsDestroyed[i] = m_totalUnitsDestroyed[i] = 0;
|
||||
}
|
||||
m_myPlayerIdx = playerIdx;
|
||||
}
|
||||
|
||||
void ScoreKeeper::addObjectBuilt( const Object *o)
|
||||
{
|
||||
Bool addToCount = FALSE;
|
||||
|
||||
if (TheGameLogic->isScoringEnabled() == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
|
||||
{
|
||||
++m_totalBuildingsBuilt;
|
||||
addToCount = TRUE;
|
||||
}
|
||||
else if (o->getTemplate()->isKindOfMulti(scoringBuildingCreateMask, KINDOFMASK_NONE))
|
||||
{
|
||||
++m_totalBuildingsBuilt;
|
||||
addToCount = TRUE;
|
||||
}
|
||||
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
|
||||
{
|
||||
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_CREATE))
|
||||
{
|
||||
++m_totalUnitsBuilt;
|
||||
addToCount = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(addToCount)
|
||||
{
|
||||
Int existingCount = 0;
|
||||
ObjectCountMapIt it = m_objectsBuilt.find(o->getTemplate());
|
||||
if (it != m_objectsBuilt.end())
|
||||
existingCount = it->second;
|
||||
m_objectsBuilt[o->getTemplate()] = existingCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Int ScoreKeeper::getTotalUnitsBuilt( KindOfMaskType validMask, KindOfMaskType invalidMask )
|
||||
{
|
||||
Int count = 0;
|
||||
for (ObjectCountMapIt it = m_objectsBuilt.begin(); it != m_objectsBuilt.end(); ++it)
|
||||
{
|
||||
const ThingTemplate *theTemplate = it->first;
|
||||
Int numBuilt = it->second;
|
||||
if (theTemplate && theTemplate->isKindOfMulti(validMask, invalidMask))
|
||||
count += numBuilt;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
Int ScoreKeeper::getTotalObjectsBuilt( const ThingTemplate *pTemplate )
|
||||
{
|
||||
Int count = 0;
|
||||
for (ObjectCountMapIt it = m_objectsBuilt.begin(); it != m_objectsBuilt.end(); ++it)
|
||||
{
|
||||
const ThingTemplate *theTemplate = it->first;
|
||||
if (theTemplate->isEquivalentTo(pTemplate))
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
void ScoreKeeper::removeObjectBuilt( const Object *o)
|
||||
{
|
||||
if (TheGameLogic->isScoringEnabled() == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bool removeFromCount = FALSE;
|
||||
if (o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
|
||||
{
|
||||
--m_totalBuildingsBuilt;
|
||||
removeFromCount = TRUE;
|
||||
}
|
||||
else if (o->getTemplate()->isKindOfMulti(scoringBuildingCreateMask, KINDOFMASK_NONE))
|
||||
{
|
||||
--m_totalBuildingsBuilt;
|
||||
removeFromCount = TRUE;
|
||||
}
|
||||
else if (o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
|
||||
{
|
||||
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_CREATE))
|
||||
{
|
||||
--m_totalUnitsBuilt;
|
||||
removeFromCount = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeFromCount)
|
||||
{
|
||||
Int existingCount = 0;
|
||||
ObjectCountMapIt it = m_objectsBuilt.find(o->getTemplate());
|
||||
if (it != m_objectsBuilt.end())
|
||||
existingCount = it->second;
|
||||
m_objectsBuilt[o->getTemplate()] = existingCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ScoreKeeper::addObjectCaptured( const Object *o )
|
||||
{
|
||||
if (TheGameLogic->isScoringEnabled() == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bool addToCount = FALSE;
|
||||
if(o->getTemplate()->isKindOf(KINDOF_STRUCTURE))
|
||||
{
|
||||
if (o->getTemplate()->isKindOf(KINDOF_SCORE))
|
||||
{
|
||||
++m_totalFactionBuildingsCaptured;
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_totalTechBuildingsCaptured;
|
||||
}
|
||||
addToCount = TRUE;
|
||||
}
|
||||
|
||||
if(addToCount)
|
||||
{
|
||||
Int existingCount = 0;
|
||||
ObjectCountMapIt it = m_objectsCaptured.find(o->getTemplate());
|
||||
if (it != m_objectsCaptured.end())
|
||||
existingCount = it->second;
|
||||
m_objectsCaptured[o->getTemplate()] = existingCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ScoreKeeper::addObjectDestroyed( const Object *o)
|
||||
{
|
||||
|
||||
if (TheGameLogic->isScoringEnabled() == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Int playerIdx = o->getControllingPlayer()->getPlayerIndex();
|
||||
|
||||
Bool addToCount = FALSE;
|
||||
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
|
||||
{
|
||||
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
|
||||
++m_totalBuildingsDestroyed[playerIdx];
|
||||
addToCount = TRUE;
|
||||
}
|
||||
}
|
||||
else if (o->getTemplate()->isKindOfMulti(scoringBuildingDestroyMask, KINDOFMASK_NONE))
|
||||
{
|
||||
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
|
||||
++m_totalBuildingsDestroyed[playerIdx];
|
||||
addToCount = TRUE;
|
||||
}
|
||||
}
|
||||
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
|
||||
{
|
||||
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_DESTROY))
|
||||
{
|
||||
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
|
||||
m_totalUnitsDestroyed[playerIdx]++;
|
||||
addToCount = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(addToCount)
|
||||
{
|
||||
Int existingCount = 0;
|
||||
ObjectCountMapIt it = m_objectsDestroyed[playerIdx].find(o->getTemplate());
|
||||
if (it != m_objectsDestroyed[playerIdx].end())
|
||||
existingCount = it->second;
|
||||
m_objectsDestroyed[playerIdx][o->getTemplate()] = existingCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ScoreKeeper::addObjectLost( const Object *o )
|
||||
{
|
||||
if (TheGameLogic->isScoringEnabled() == FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bool addToCount = FALSE;
|
||||
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
|
||||
{
|
||||
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
|
||||
++m_totalBuildingsLost;
|
||||
addToCount = TRUE;
|
||||
}
|
||||
}
|
||||
else if (o->getTemplate()->isKindOfMulti(scoringBuildingDestroyMask, KINDOFMASK_NONE))
|
||||
{
|
||||
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
|
||||
++m_totalBuildingsLost;
|
||||
addToCount = TRUE;
|
||||
}
|
||||
}
|
||||
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
|
||||
{
|
||||
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_DESTROY))
|
||||
{
|
||||
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
|
||||
++m_totalUnitsLost;
|
||||
addToCount = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(addToCount)
|
||||
{
|
||||
Int existingCount = 0;
|
||||
ObjectCountMapIt it = m_objectsLost.find(o->getTemplate());
|
||||
if (it != m_objectsLost.end())
|
||||
existingCount = it->second;
|
||||
m_objectsLost[o->getTemplate()] = existingCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Int ScoreKeeper::calculateScore( void )
|
||||
{
|
||||
Int score = 0;
|
||||
score += m_totalUnitsBuilt * 100;
|
||||
score += m_totalMoneyEarned;
|
||||
score += m_totalBuildingsBuilt * 100;
|
||||
for (Int i = 0; i < MAX_PLAYER_COUNT; ++i)
|
||||
{
|
||||
if(i == m_myPlayerIdx)
|
||||
continue;
|
||||
score += m_totalUnitsDestroyed[i] * 100;
|
||||
score += m_totalBuildingsDestroyed[i] * 100;
|
||||
}
|
||||
|
||||
m_currentScore = score;
|
||||
return m_currentScore;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Int ScoreKeeper::getTotalBuildingsDestroyed( void )
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i< MAX_PLAYER_COUNT; ++i)
|
||||
{
|
||||
// Design change, display even if we killed our own
|
||||
// if(i == m_myPlayerIdx)
|
||||
// continue;
|
||||
count += m_totalBuildingsDestroyed[i];
|
||||
//for (ObjectCountMapIt it = m_objectsDestroyed[i].begin(); it != m_objectsDestroyed[i].end(); ++it)
|
||||
// {
|
||||
//
|
||||
// count += it->second;
|
||||
// }
|
||||
|
||||
}
|
||||
return count;
|
||||
}
|
||||
Int ScoreKeeper::getTotalUnitsDestroyed( void )
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i< MAX_PLAYER_COUNT; ++i)
|
||||
{
|
||||
// Design change, display even if we killed our own
|
||||
// if(i == m_myPlayerIdx)
|
||||
// continue;
|
||||
count += m_totalUnitsDestroyed[i];
|
||||
// for (ObjectCountMapIt it = m_objectsDestroyed[i].begin(); it != m_objectsDestroyed[i].end(); ++it)
|
||||
// {
|
||||
// }
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ScoreKeeper::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end ScoreKeeper
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer of an object count map
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ScoreKeeper::xferObjectCountMap( Xfer *xfer, ObjectCountMap *map )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( map == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "xferObjectCountMap - Invalid map parameter\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// version info
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// size of the map
|
||||
UnsignedShort mapSize = map->size();
|
||||
xfer->xferUnsignedShort( &mapSize );
|
||||
|
||||
// map data
|
||||
Int count;
|
||||
const ThingTemplate *thingTemplate;
|
||||
AsciiString thingTemplateName;
|
||||
if( xfer->getXferMode() == XFER_SAVE )
|
||||
{
|
||||
ObjectCountMapIt it;
|
||||
|
||||
// save all entries
|
||||
for( it = map->begin(); it != map->end(); ++it )
|
||||
{
|
||||
|
||||
// thing template
|
||||
thingTemplate = it->first;
|
||||
thingTemplateName = thingTemplate->getName();
|
||||
xfer->xferAsciiString( &thingTemplateName );
|
||||
|
||||
// the count
|
||||
count = it->second;
|
||||
xfer->xferInt( &count );
|
||||
|
||||
} // end for, it
|
||||
|
||||
} // end if, save
|
||||
else
|
||||
{
|
||||
|
||||
// read all entries
|
||||
for( UnsignedShort i = 0; i < mapSize; ++i )
|
||||
{
|
||||
|
||||
// read thing template name
|
||||
xfer->xferAsciiString( &thingTemplateName );
|
||||
thingTemplate = TheThingFactory->findTemplate( thingTemplateName );
|
||||
if( thingTemplate == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "xferObjectCountMap - Unknown thing template '%s'\n", thingTemplateName.str() ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// read count
|
||||
xfer->xferInt( &count );
|
||||
|
||||
// add to map
|
||||
(*map)[ thingTemplate ] = count;
|
||||
|
||||
} // end for, i
|
||||
|
||||
} // end else
|
||||
|
||||
} // end xferObjectCountMap
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ScoreKeeper::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// money earned
|
||||
xfer->xferInt( &m_totalMoneyEarned );
|
||||
|
||||
// money spent
|
||||
xfer->xferInt( &m_totalMoneySpent );
|
||||
|
||||
// units destroyed
|
||||
xfer->xferUser( m_totalUnitsDestroyed, sizeof( Int ) * MAX_PLAYER_COUNT );
|
||||
|
||||
// units built
|
||||
xfer->xferInt( &m_totalUnitsBuilt );
|
||||
|
||||
// units lost
|
||||
xfer->xferInt( &m_totalUnitsLost );
|
||||
|
||||
// buildings destroyed
|
||||
xfer->xferUser( m_totalBuildingsDestroyed, sizeof( Int ) * MAX_PLAYER_COUNT );
|
||||
|
||||
// buildings built
|
||||
xfer->xferInt( &m_totalBuildingsBuilt );
|
||||
|
||||
// buildings lost
|
||||
xfer->xferInt( &m_totalBuildingsLost );
|
||||
|
||||
// tech buildings captured
|
||||
xfer->xferInt( &m_totalTechBuildingsCaptured );
|
||||
|
||||
// faction buildings captured
|
||||
xfer->xferInt( &m_totalFactionBuildingsCaptured );
|
||||
|
||||
// current score
|
||||
xfer->xferInt( &m_currentScore );
|
||||
|
||||
// player index
|
||||
xfer->xferInt( &m_myPlayerIdx );
|
||||
|
||||
// objects built
|
||||
xferObjectCountMap( xfer, &m_objectsBuilt );
|
||||
|
||||
// objects destroyed
|
||||
UnsignedShort destroyedArraySize = MAX_PLAYER_COUNT;
|
||||
xfer->xferUnsignedShort( &destroyedArraySize );
|
||||
if( destroyedArraySize != MAX_PLAYER_COUNT )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "ScoreKeeper::xfer - size of objects destroyed array has changed\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
for( UnsignedShort i = 0; i < destroyedArraySize; ++i )
|
||||
{
|
||||
|
||||
// xfer map data
|
||||
xferObjectCountMap( xfer, &m_objectsDestroyed[ i ] );
|
||||
|
||||
} // end for i
|
||||
|
||||
// objects lost
|
||||
xferObjectCountMap( xfer, &m_objectsLost );
|
||||
|
||||
// objects captured
|
||||
xferObjectCountMap( xfer, &m_objectsCaptured );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void ScoreKeeper::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
349
Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp
Normal file
349
Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: SpecialPower.cpp /////////////////////////////////////////////////////////////////////////
|
||||
// Author: Colin Day, April 2002
|
||||
// Desc: Special power templates and the system that holds them
|
||||
// Edited: Kris Morness -- July 2002 (added BitFlag system)
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Player.h"
|
||||
#include "Common/Science.h"
|
||||
#include "Common/SpecialPower.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "Common/BitFlagsIO.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
// GLOBAL /////////////////////////////////////////////////////////////////////////////////////////
|
||||
SpecialPowerStore *TheSpecialPowerStore = NULL;
|
||||
|
||||
#define DEFAULT_DEFECTION_DETECTION_PROTECTION_TIME_LIMIT (LOGICFRAMES_PER_SECOND * 10)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Externs ////////////////////////////////////////////////////////////////////////////////////////
|
||||
const char* SpecialPowerMaskType::s_bitNameList[] =
|
||||
{
|
||||
"SPECIAL_INVALID",
|
||||
|
||||
//Superweapons
|
||||
"SPECIAL_DAISY_CUTTER",
|
||||
"SPECIAL_PARADROP_AMERICA",
|
||||
"SPECIAL_CARPET_BOMB",
|
||||
"SPECIAL_CLUSTER_MINES",
|
||||
"SPECIAL_EMP_PULSE",
|
||||
"SPECIAL_NAPALM_STRIKE",
|
||||
"SPECIAL_CASH_HACK",
|
||||
"SPECIAL_NEUTRON_MISSILE",
|
||||
"SPECIAL_SPY_SATELLITE",
|
||||
"SPECIAL_DEFECTOR",
|
||||
"SPECIAL_TERROR_CELL",
|
||||
"SPECIAL_AMBUSH",
|
||||
"SPECIAL_BLACK_MARKET_NUKE",
|
||||
"SPECIAL_ANTHRAX_BOMB",
|
||||
"SPECIAL_SCUD_STORM",
|
||||
#ifdef ALLOW_DEMORALIZE
|
||||
"SPECIAL_DEMORALIZE",
|
||||
#else
|
||||
"SPECIAL_DEMORALIZE_OBSOLETE",
|
||||
#endif
|
||||
"SPECIAL_CRATE_DROP",
|
||||
"SPECIAL_A10_THUNDERBOLT_STRIKE",
|
||||
"SPECIAL_DETONATE_DIRTY_NUKE",
|
||||
"SPECIAL_ARTILLERY_BARRAGE",
|
||||
|
||||
//Special abilities
|
||||
"SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES",
|
||||
"SPECIAL_REMOTE_CHARGES",
|
||||
"SPECIAL_TIMED_CHARGES",
|
||||
"SPECIAL_HACKER_DISABLE_BUILDING",
|
||||
"SPECIAL_TANKHUNTER_TNT_ATTACK",
|
||||
"SPECIAL_BLACKLOTUS_CAPTURE_BUILDING",
|
||||
"SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK",
|
||||
"SPECIAL_BLACKLOTUS_STEAL_CASH_HACK",
|
||||
"SPECIAL_INFANTRY_CAPTURE_BUILDING",
|
||||
"SPECIAL_RADAR_VAN_SCAN",
|
||||
"SPECIAL_SPY_DRONE",
|
||||
"SPECIAL_DISGUISE_AS_VEHICLE",
|
||||
"SPECIAL_REPAIR_VEHICLES",
|
||||
"SPECIAL_PARTICLE_UPLINK_CANNON",
|
||||
"SPECIAL_CASH_BOUNTY",
|
||||
"SPECIAL_CHANGE_BATTLE_PLANS",
|
||||
"SPECIAL_CIA_INTELLIGENCE",
|
||||
"SPECIAL_CLEANUP_AREA",
|
||||
"SPECIAL_LAUNCH_BAIKONUR_ROCKET",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerStore::parseSpecialPowerDefinition( INI *ini )
|
||||
{
|
||||
// read the name
|
||||
AsciiString name = ini->getNextToken();
|
||||
|
||||
SpecialPowerTemplate* specialPower = TheSpecialPowerStore->findSpecialPowerTemplatePrivate( name );
|
||||
|
||||
if ( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
|
||||
{
|
||||
if (specialPower)
|
||||
{
|
||||
SpecialPowerTemplate* child = (SpecialPowerTemplate*)specialPower->friend_getFinalOverride();
|
||||
specialPower = newInstance(SpecialPowerTemplate);
|
||||
*specialPower = *child;
|
||||
child->setNextOverride(specialPower);
|
||||
specialPower->markAsOverride();
|
||||
//TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower); // nope, do NOT do this
|
||||
}
|
||||
else
|
||||
{
|
||||
specialPower = newInstance(SpecialPowerTemplate);
|
||||
const SpecialPowerTemplate *defaultTemplate = TheSpecialPowerStore->findSpecialPowerTemplate( "DefaultSpecialPower" );
|
||||
if( defaultTemplate )
|
||||
*specialPower = *defaultTemplate;
|
||||
specialPower->friend_setNameAndID(name, ++TheSpecialPowerStore->m_nextSpecialPowerID);
|
||||
specialPower->markAsOverride();
|
||||
TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (specialPower)
|
||||
{
|
||||
throw INI_INVALID_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
specialPower = newInstance(SpecialPowerTemplate);
|
||||
const SpecialPowerTemplate *defaultTemplate = TheSpecialPowerStore->findSpecialPowerTemplate( "DefaultSpecialPower" );
|
||||
if( defaultTemplate )
|
||||
*specialPower = *defaultTemplate;
|
||||
specialPower->friend_setNameAndID(name, ++TheSpecialPowerStore->m_nextSpecialPowerID);
|
||||
TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower);
|
||||
}
|
||||
}
|
||||
|
||||
// parse the ini definition
|
||||
if (specialPower)
|
||||
ini->initFromINI( specialPower, specialPower->getFieldParse() );
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/* static */ const FieldParse SpecialPowerTemplate::m_specialPowerFieldParse[] =
|
||||
{
|
||||
|
||||
{ "ReloadTime", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_reloadTime ) },
|
||||
{ "RequiredScience", INI::parseScience, NULL, offsetof( SpecialPowerTemplate, m_requiredScience ) },
|
||||
{ "InitiateSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerTemplate, m_initiateSound ) },
|
||||
{ "InitiateAtLocationSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerTemplate, m_initiateAtLocationSound ) },
|
||||
{ "PublicTimer", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_publicTimer ) },
|
||||
{ "Enum", INI::parseIndexList, SpecialPowerMaskType::getBitNames(), offsetof( SpecialPowerTemplate, m_type ) },
|
||||
{ "DetectionTime", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_detectionTime ) },
|
||||
{ "SharedSyncedTimer", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_sharedNSync ) },
|
||||
{ "ViewObjectDuration", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_viewObjectDuration ) },
|
||||
{ "ViewObjectRange", INI::parseReal, NULL, offsetof( SpecialPowerTemplate, m_viewObjectRange ) },
|
||||
{ "RadiusCursorRadius", INI::parseReal, NULL, offsetof( SpecialPowerTemplate, m_radiusCursorRadius ) },
|
||||
{ NULL, NULL, NULL, 0 } // keep this last
|
||||
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerTemplate::SpecialPowerTemplate()
|
||||
{
|
||||
m_id = 0;
|
||||
m_type = SPECIAL_INVALID;
|
||||
m_reloadTime = 0;
|
||||
m_requiredScience = SCIENCE_INVALID;
|
||||
m_publicTimer = FALSE;
|
||||
m_detectionTime = DEFAULT_DEFECTION_DETECTION_PROTECTION_TIME_LIMIT;
|
||||
m_sharedNSync = FALSE;
|
||||
m_viewObjectDuration = 0;
|
||||
m_viewObjectRange = 0;
|
||||
m_radiusCursorRadius = 0;
|
||||
|
||||
} // end SpecialPowerTemplate
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerTemplate::~SpecialPowerTemplate()
|
||||
{
|
||||
|
||||
} // end ~SpecialPowerTemplate
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerStore::SpecialPowerStore( void )
|
||||
{
|
||||
|
||||
m_nextSpecialPowerID = 0;
|
||||
|
||||
} // end SpecialPowerStore
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerStore::~SpecialPowerStore( void )
|
||||
{
|
||||
|
||||
// delete all templates
|
||||
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
|
||||
m_specialPowerTemplates[ i ]->deleteInstance();
|
||||
|
||||
// erase the list
|
||||
m_specialPowerTemplates.clear();
|
||||
|
||||
// set our count to zero
|
||||
m_nextSpecialPowerID = 0;
|
||||
|
||||
} // end ~SpecialPowerStore
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
SpecialPowerTemplate* SpecialPowerStore::findSpecialPowerTemplatePrivate( AsciiString name )
|
||||
{
|
||||
|
||||
// search the template list for matching name
|
||||
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
|
||||
if( m_specialPowerTemplates[ i ]->getName() == name )
|
||||
return m_specialPowerTemplates[ i ];
|
||||
|
||||
return NULL; // not found
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Find a special power template given unique ID */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const SpecialPowerTemplate *SpecialPowerStore::findSpecialPowerTemplateByID( UnsignedInt id )
|
||||
{
|
||||
|
||||
// search the template list for matching name
|
||||
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
|
||||
if( m_specialPowerTemplates[ i ]->getID() == id )
|
||||
return m_specialPowerTemplates[ i ];
|
||||
|
||||
return NULL; // not found
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Find a special power template given index (WB) */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const SpecialPowerTemplate *SpecialPowerStore::getSpecialPowerTemplateByIndex( UnsignedInt index )
|
||||
{
|
||||
|
||||
if (index >= 0 && index < m_specialPowerTemplates.size())
|
||||
return m_specialPowerTemplates[ index ];
|
||||
|
||||
return NULL; // not found
|
||||
|
||||
} // end getSpecialPowerTemplateByIndex
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Return the size of the store (WB) */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Int SpecialPowerStore::getNumSpecialPowers( void )
|
||||
{
|
||||
|
||||
return m_specialPowerTemplates.size();
|
||||
|
||||
} // end getNumSpecialPowers
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** does the object (and therefore the player) meet all the requirements to use this power */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool SpecialPowerStore::canUseSpecialPower( Object *obj, const SpecialPowerTemplate *specialPowerTemplate )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( obj == NULL || specialPowerTemplate == NULL )
|
||||
return FALSE;
|
||||
|
||||
// as a first sanity check, the object must have a module capable of executing the power
|
||||
if( obj->getSpecialPowerModule( specialPowerTemplate ) == NULL )
|
||||
return FALSE;
|
||||
|
||||
//
|
||||
// in order to execute the special powers we have attached special power modules to the objects
|
||||
// that can use them. However, just because an object has a module that is capable of
|
||||
// doing the power, does not mean the object and the player can actually execute the
|
||||
// power because some powers require a specialized science that the player must select and
|
||||
// they cannot have all of them.
|
||||
//
|
||||
|
||||
// check for requried science
|
||||
ScienceType requiredScience = specialPowerTemplate->getRequiredScience();
|
||||
if( requiredScience != SCIENCE_INVALID )
|
||||
{
|
||||
Player *player = obj->getControllingPlayer();
|
||||
|
||||
if( player->hasScience( requiredScience ) == FALSE )
|
||||
return FALSE;
|
||||
|
||||
} // end if
|
||||
|
||||
|
||||
// I THINK THIS IS WHERE WE BAIL OUT IF A DIFFERENT CONYARD IS ALREADY CHARGIN THIS SPECIAL RIGHT NOW //LORENZEN
|
||||
|
||||
|
||||
// all is well
|
||||
return TRUE;
|
||||
|
||||
} // end canUseSpecialPower
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Reset */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void SpecialPowerStore::reset( void )
|
||||
{
|
||||
for (SpecialPowerTemplatePtrVector::iterator it = m_specialPowerTemplates.begin(); it != m_specialPowerTemplates.end(); /*++it*/)
|
||||
{
|
||||
SpecialPowerTemplate* si = *it;
|
||||
Overridable* temp = si->deleteOverrides();
|
||||
if (temp == NULL)
|
||||
{
|
||||
it = m_specialPowerTemplates.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
} // end reset
|
||||
2748
Generals/Code/GameEngine/Source/Common/RTS/Team.cpp
Normal file
2748
Generals/Code/GameEngine/Source/Common/RTS/Team.cpp
Normal file
File diff suppressed because it is too large
Load Diff
410
Generals/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp
Normal file
410
Generals/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: TunnelTracker.cpp ///////////////////////////////////////////////////////////
|
||||
// The part of a Player's brain that holds the communal Passenger list of all tunnels.
|
||||
// Author: Graham Smallwood, March, 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/KindOf.h"
|
||||
#include "Common/TunnelTracker.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#include "GameClient/ControlBar.h"
|
||||
#include "GameClient/Drawable.h"
|
||||
|
||||
#include "GameLogic/AI.h"
|
||||
#include "GameLogic/AIPathfind.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/PartitionManager.h"
|
||||
|
||||
#include "GameLogic/Module/BodyModule.h"
|
||||
#include "GameLogic/Module/TunnelContain.h"
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
TunnelTracker::TunnelTracker()
|
||||
{
|
||||
m_tunnelCount = 0;
|
||||
m_containListSize = 0;
|
||||
m_curNemesisID = INVALID_ID;
|
||||
m_nemesisTimestamp = 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
TunnelTracker::~TunnelTracker()
|
||||
{
|
||||
m_tunnelIDs.clear();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void TunnelTracker::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
|
||||
{
|
||||
if (reverse)
|
||||
{
|
||||
// note that this has to be smart enough to handle items in the list being deleted
|
||||
// via the callback function.
|
||||
for(ContainedItemsList::reverse_iterator it = m_containList.rbegin(); it != m_containList.rend(); )
|
||||
{
|
||||
// save the obj...
|
||||
Object* obj = *it;
|
||||
|
||||
// incr the iterator BEFORE calling the func (if the func removes the obj,
|
||||
// the iterator becomes invalid)
|
||||
++it;
|
||||
|
||||
// call it
|
||||
(*func)( obj, userData );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// note that this has to be smart enough to handle items in the list being deleted
|
||||
// via the callback function.
|
||||
for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
|
||||
{
|
||||
// save the obj...
|
||||
Object* obj = *it;
|
||||
|
||||
// incr the iterator BEFORE calling the func (if the func removes the obj,
|
||||
// the iterator becomes invalid)
|
||||
++it;
|
||||
|
||||
// call it
|
||||
(*func)( obj, userData );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
Int TunnelTracker::getContainMax() const
|
||||
{
|
||||
return TheGlobalData->m_maxTunnelCapacity;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void TunnelTracker::updateNemesis(const Object *target)
|
||||
{
|
||||
if (getCurNemesis()==NULL) {
|
||||
if (target) {
|
||||
if (target->isKindOf(KINDOF_VEHICLE) || target->isKindOf(KINDOF_STRUCTURE) ||
|
||||
target->isKindOf(KINDOF_INFANTRY) || target->isKindOf(KINDOF_AIRCRAFT)) {
|
||||
m_curNemesisID = target->getID();
|
||||
m_nemesisTimestamp = TheGameLogic->getFrame();
|
||||
}
|
||||
}
|
||||
} else if (getCurNemesis()==target) {
|
||||
m_nemesisTimestamp = TheGameLogic->getFrame();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
Object *TunnelTracker::getCurNemesis(void)
|
||||
{
|
||||
if (m_curNemesisID == INVALID_ID) {
|
||||
return NULL;
|
||||
}
|
||||
if (m_nemesisTimestamp + 4*LOGICFRAMES_PER_SECOND < TheGameLogic->getFrame()) {
|
||||
m_curNemesisID = INVALID_ID;
|
||||
return NULL;
|
||||
}
|
||||
Object *target = TheGameLogic->findObjectByID(m_curNemesisID);
|
||||
if (target) {
|
||||
//If the enemy unit is stealthed and not detected, then we can't attack it!
|
||||
UnsignedInt status = target->getStatusBits();
|
||||
if( (status & OBJECT_STATUS_STEALTHED) && !(status & OBJECT_STATUS_DETECTED) ) {
|
||||
target = NULL;
|
||||
}
|
||||
}
|
||||
if (target && target->isEffectivelyDead()) {
|
||||
target = NULL;
|
||||
}
|
||||
if (target == NULL) {
|
||||
m_curNemesisID = INVALID_ID;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
Bool TunnelTracker::isValidContainerFor(const Object* obj, Bool checkCapacity) const
|
||||
{
|
||||
//October 11, 2002 -- Kris : Dustin wants ALL units to be able to use tunnels!
|
||||
// srj sez: um, except aircraft.
|
||||
if (obj && !obj->isKindOf(KINDOF_AIRCRAFT))
|
||||
{
|
||||
if (checkCapacity)
|
||||
{
|
||||
Int containMax = getContainMax();
|
||||
Int containCount = getContainCount();
|
||||
return ( containCount < containMax );
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void TunnelTracker::addToContainList( Object *obj )
|
||||
{
|
||||
m_containList.push_back(obj);
|
||||
++m_containListSize;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void TunnelTracker::removeFromContain( Object *obj, Bool exposeStealthUnits )
|
||||
{
|
||||
|
||||
ContainedItemsList::iterator it = std::find(m_containList.begin(), m_containList.end(), obj);
|
||||
if (it != m_containList.end())
|
||||
{
|
||||
// note that this invalidates the iterator!
|
||||
m_containList.erase(it);
|
||||
--m_containListSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
Bool TunnelTracker::isInContainer( Object *obj )
|
||||
{
|
||||
return (std::find(m_containList.begin(), m_containList.end(), obj) != m_containList.end()) ;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void TunnelTracker::onTunnelCreated( const Object *newTunnel )
|
||||
{
|
||||
m_tunnelCount++;
|
||||
m_tunnelIDs.push_back( newTunnel->getID() );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void TunnelTracker::onTunnelDestroyed( const Object *deadTunnel )
|
||||
{
|
||||
m_tunnelCount--;
|
||||
m_tunnelIDs.remove( deadTunnel->getID() );
|
||||
|
||||
if( m_tunnelCount == 0 )
|
||||
{
|
||||
// Kill everyone in our contain list. Cave in!
|
||||
iterateContained( destroyObject, NULL, FALSE );
|
||||
m_containList.clear();
|
||||
m_containListSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Object *validTunnel = TheGameLogic->findObjectByID( m_tunnelIDs.front() );
|
||||
// Otherwise, make sure nobody inside remembers the dead tunnel as the one they entered
|
||||
// (scripts need to use so there must be something valid here)
|
||||
for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
|
||||
{
|
||||
Object* obj = *it;
|
||||
++it;
|
||||
if( obj->getContainedBy() == deadTunnel )
|
||||
obj->onContainedBy( validTunnel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void TunnelTracker::destroyObject( Object *obj, void * )
|
||||
{
|
||||
// Now that tunnels consider ContainedBy to be "the tunnel you entered", I need to say goodbye
|
||||
// llike other contain types so they don't look us up on their deletion and crash
|
||||
obj->onRemovedFrom( obj->getContainedBy() );
|
||||
TheGameLogic->destroyObject( obj );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// heal all the objects within the tunnel system using the iterateContained function
|
||||
void TunnelTracker::healObjects(Real frames)
|
||||
{
|
||||
iterateContained(healObject, &frames, FALSE);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// heal one object within the tunnel network system
|
||||
void TunnelTracker::healObject( Object *obj, void *frames)
|
||||
{
|
||||
|
||||
//get the number of frames to heal
|
||||
Real *framesForFullHeal = (Real*)frames;
|
||||
|
||||
// setup the healing damageInfo structure with all but the amount
|
||||
DamageInfo healInfo;
|
||||
healInfo.in.m_damageType = DAMAGE_HEALING;
|
||||
healInfo.in.m_deathType = DEATH_NONE;
|
||||
//healInfo.in.m_sourceID = getObject()->getID();
|
||||
|
||||
// get body module of the thing to heal
|
||||
BodyModuleInterface *body = obj->getBodyModule();
|
||||
|
||||
// if we've been in here long enough ... set our health to max
|
||||
if( TheGameLogic->getFrame() - obj->getContainedByFrame() >= *framesForFullHeal )
|
||||
{
|
||||
|
||||
// set the amount to max just to be sure we're at the top
|
||||
healInfo.in.m_amount = body->getMaxHealth();
|
||||
|
||||
// set max health
|
||||
body->attemptHealing( &healInfo );
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
//
|
||||
// given the *whole* time it would take to heal this object, lets pretend that the
|
||||
// object is at zero health ... and give it a sliver of health as if it were at 0 health
|
||||
// and would be fully healed at 'framesForFullHeal'
|
||||
//
|
||||
healInfo.in.m_amount = body->getMaxHealth() / *framesForFullHeal;
|
||||
|
||||
// do the healing
|
||||
body->attemptHealing( &healInfo );
|
||||
|
||||
} // end else
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TunnelTracker::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TunnelTracker::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// tunnel object id list
|
||||
xfer->xferSTLObjectIDList( &m_tunnelIDs );
|
||||
|
||||
// contain list count
|
||||
xfer->xferInt( &m_containListSize );
|
||||
|
||||
// contain list data
|
||||
ObjectID objectID;
|
||||
if( xfer->getXferMode() == XFER_SAVE )
|
||||
{
|
||||
ContainedItemsList::const_iterator it;
|
||||
|
||||
for( it = m_containList.begin(); it != m_containList.end(); ++it )
|
||||
{
|
||||
|
||||
objectID = (*it)->getID();
|
||||
xfer->xferObjectID( &objectID );
|
||||
|
||||
} // end for, it
|
||||
|
||||
} // end if, save
|
||||
else
|
||||
{
|
||||
|
||||
for( UnsignedShort i = 0; i < m_containListSize; ++i )
|
||||
{
|
||||
|
||||
xfer->xferObjectID( &objectID );
|
||||
m_xferContainList.push_back( objectID );
|
||||
|
||||
} // end for, i
|
||||
|
||||
} // end else, load
|
||||
|
||||
// tunnel count
|
||||
xfer->xferUnsignedInt( &m_tunnelCount );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void TunnelTracker::loadPostProcess( void )
|
||||
{
|
||||
|
||||
// sanity, the contain list should be empty until we post process the id list
|
||||
if( m_containList.size() != 0 )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "TunnelTracker::loadPostProcess - m_containList should be empty but is not\n" ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// translate each object ids on the xferContainList into real object pointers in the contain list
|
||||
Object *obj;
|
||||
std::list< ObjectID >::const_iterator it;
|
||||
for( it = m_xferContainList.begin(); it != m_xferContainList.end(); ++it )
|
||||
{
|
||||
|
||||
obj = TheGameLogic->findObjectByID( *it );
|
||||
if( obj == NULL )
|
||||
{
|
||||
|
||||
DEBUG_CRASH(( "TunnelTracker::loadPostProcess - Unable to find object ID '%d'\n", *it ));
|
||||
throw SC_INVALID_DATA;
|
||||
|
||||
} // end if
|
||||
|
||||
// push on the back of the contain list
|
||||
m_containList.push_back( obj );
|
||||
|
||||
// Crap. This is in OpenContain as a fix, but not here.
|
||||
{
|
||||
// remove object from its group (if any)
|
||||
obj->leaveGroup();
|
||||
|
||||
// remove rider from partition manager
|
||||
ThePartitionManager->unRegisterObject( obj );
|
||||
|
||||
// hide the drawable associated with rider
|
||||
if( obj->getDrawable() )
|
||||
obj->getDrawable()->setDrawableHidden( true );
|
||||
|
||||
// remove object from pathfind map
|
||||
if( TheAI )
|
||||
TheAI->pathfinder()->removeObjectFromPathfindMap( obj );
|
||||
|
||||
}
|
||||
} // end for, it
|
||||
|
||||
// we're done with the xfer contain list now
|
||||
m_xferContainList.clear();
|
||||
|
||||
} // end loadPostProcess
|
||||
441
Generals/Code/GameEngine/Source/Common/RandomValue.cpp
Normal file
441
Generals/Code/GameEngine/Source/Common/RandomValue.cpp
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// RandomValue.cpp
|
||||
// Pseudo-random number generators
|
||||
// Author: Michael S. Booth, January 1998
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
|
||||
#include "Lib/BaseType.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/CRC.h"
|
||||
#include "Common/Debug.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
|
||||
//#define DETERMINISTIC // to allow repetition for debugging
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
#undef DEBUG_RANDOM_CLIENT
|
||||
#undef DEBUG_RANDOM_LOGIC
|
||||
#undef DEBUG_RANDOM_AUDIO
|
||||
|
||||
//#define DEBUG_RANDOM_CLIENT
|
||||
//#define DEBUG_RANDOM_LOGIC
|
||||
//#define DEBUG_RANDOM_AUDIO
|
||||
|
||||
static const Real theMultFactor = 1.0f / (powf(2, 8 * sizeof(UnsignedInt)) - 1.0f);
|
||||
|
||||
// Initial seed values.
|
||||
static UnsignedInt theGameClientSeed[6] =
|
||||
{
|
||||
0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
|
||||
};
|
||||
|
||||
static UnsignedInt theGameAudioSeed[6] =
|
||||
{
|
||||
0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
|
||||
};
|
||||
|
||||
static UnsignedInt theGameLogicBaseSeed = 0;
|
||||
static UnsignedInt theGameLogicSeed[6] =
|
||||
{
|
||||
0xf22d0e56L, 0x883126e9L, 0xc624dd2fL, 0x702c49cL, 0x9e353f7dL, 0x6fdf3b64L
|
||||
};
|
||||
|
||||
// Add with carry. SUM is replaced with A + B + C, C is replaced with 1 if there was a carry, 0 if there wasn't. A carry occurred if the sum is less than one of the inputs. This is addition, so carry can never be more than one.
|
||||
#define ADC(SUM, A, B, C) SUM = (A) + (B) + (C); C = ((SUM < (A)) || (SUM < (B)))
|
||||
|
||||
static UnsignedInt randomValue(UnsignedInt *seed)
|
||||
{
|
||||
UnsignedInt ax;
|
||||
UnsignedInt c = 0;
|
||||
|
||||
|
||||
ADC(ax, seed[5], seed[4], c); /* mov ax,seed+20 */
|
||||
/* add ax,seed+16 */
|
||||
seed[4] = ax; /* mov seed+8,ax */
|
||||
|
||||
ADC(ax, ax, seed[3], c); /* adc ax,seed+12 */
|
||||
seed[3] = ax; /* mov seed+12,ax */
|
||||
|
||||
ADC(ax, ax, seed[2], c); /* adc ax,seed+8 */
|
||||
seed[2] = ax; /* mov seed+8,ax */
|
||||
|
||||
ADC(ax, ax, seed[1], c); /* adc ax,seed+4 */
|
||||
seed[1] = ax; /* mov seed+4,ax */
|
||||
|
||||
ADC(ax, ax, seed[0], c); /* adc ax,seed+0 */
|
||||
seed[0] = ax; /* mov seed+0,ax */
|
||||
|
||||
/* Increment seed array, bubbling up the carries. */
|
||||
if (!++seed[5])
|
||||
{
|
||||
if (!++seed[4])
|
||||
{
|
||||
if (!++seed[3])
|
||||
{
|
||||
if (!++seed[2])
|
||||
{
|
||||
if (!++seed[1])
|
||||
{
|
||||
++seed[0];
|
||||
++ax;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(ax);
|
||||
}
|
||||
|
||||
static void seedRandom(UnsignedInt SEED, UnsignedInt *seed)
|
||||
{
|
||||
UnsignedInt ax;
|
||||
|
||||
ax = SEED; /* mov eax,SEED */
|
||||
ax += 0xf22d0e56; /* add eax,0f22d0e56h */
|
||||
seed[0] = ax; /* mov seed,eax */
|
||||
ax += 0x883126e9 - 0xf22d0e56; /* add eax,0883126e9h-0f22d0e56h */
|
||||
seed[1] = ax; /* mov seed+4,eax */
|
||||
ax += 0xc624dd2f - 0x883126e9; /* add eax,0c624dd2fh-0883126e9h */
|
||||
seed[2] = ax; /* mov seed+8,eax */
|
||||
ax += 0x0702c49c - 0xc624dd2f; /* add eax,00702c49ch-0c624dd2fh */
|
||||
seed[3] = ax; /* mov seed+12,eax */
|
||||
ax += 0x9e353f7d - 0x0702c49c; /* add eax,09e353f7dh-00702c49ch */
|
||||
seed[4] = ax; /* mov seed+16,eax */
|
||||
ax += 0x6fdf3b64 - 0x9e353f7d; /* add eax,06fdf3b64h-09e353f7dh */
|
||||
seed[5] = ax; /* mov seed+20,eax */
|
||||
}
|
||||
|
||||
//
|
||||
// It is necessary to separate the GameClient and GameLogic usage of random
|
||||
// values to ensure that the GameLogic remains deterministic, regardless
|
||||
// of the effects displayed on the GameClient.
|
||||
//
|
||||
|
||||
UnsignedInt GetGameLogicRandomSeed( void )
|
||||
{
|
||||
return theGameLogicBaseSeed;
|
||||
}
|
||||
|
||||
UnsignedInt GetGameLogicRandomSeedCRC( void )
|
||||
{
|
||||
CRC c;
|
||||
c.computeCRC(theGameLogicSeed, 6*sizeof(UnsignedInt));
|
||||
return c.get();
|
||||
}
|
||||
|
||||
void InitRandom( void )
|
||||
{
|
||||
#ifdef DETERMINISTIC
|
||||
// needs to be the same every time
|
||||
seedRandom(0, theGameClientSeed);
|
||||
seedRandom(0, theGameAudioSeed);
|
||||
seedRandom(0, theGameLogicSeed);
|
||||
theGameLogicBaseSeed = 0;
|
||||
#else
|
||||
time_t seconds = time( NULL );
|
||||
|
||||
seedRandom(seconds, theGameAudioSeed);
|
||||
seedRandom(seconds, theGameClientSeed);
|
||||
seedRandom(seconds, theGameLogicSeed);
|
||||
theGameLogicBaseSeed = seconds;
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitRandom( UnsignedInt seed )
|
||||
{
|
||||
seedRandom(seed, theGameAudioSeed);
|
||||
seedRandom(seed, theGameClientSeed);
|
||||
seedRandom(seed, theGameLogicSeed);
|
||||
theGameLogicBaseSeed = seed;
|
||||
#ifdef DEBUG_RANDOM_LOGIC
|
||||
DEBUG_LOG(( "InitRandom %08lx\n",seed));
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitGameLogicRandom( UnsignedInt seed )
|
||||
{
|
||||
#ifdef DETERMINISTIC
|
||||
// needs to be the same every time
|
||||
seedRandom(0, theGameLogicSeed);
|
||||
theGameLogicBaseSeed = 0;
|
||||
#else
|
||||
seedRandom(seed, theGameLogicSeed);
|
||||
theGameLogicBaseSeed = seed;
|
||||
#endif
|
||||
#ifdef DEBUG_RANDOM_LOGIC
|
||||
DEBUG_LOG(( "InitRandom Logic %08lx\n",seed));
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Integer random value
|
||||
//
|
||||
Int GetGameLogicRandomValue( int lo, int hi, char *file, int line )
|
||||
{
|
||||
//Int delta = hi - lo + 1;
|
||||
//Int rval;
|
||||
|
||||
//if (delta == 0)
|
||||
//return hi;
|
||||
|
||||
//rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
|
||||
|
||||
UnsignedInt delta = hi - lo + 1;
|
||||
//UnsignedInt temp;
|
||||
Int rval;
|
||||
|
||||
if (delta == 0)
|
||||
return hi;
|
||||
|
||||
rval = ((Int)(randomValue(theGameLogicSeed) % delta)) + lo;
|
||||
//temp = randomValue(theGameLogicSeed);
|
||||
//temp = temp % delta;
|
||||
|
||||
//rval = temp + lo;
|
||||
|
||||
/**/
|
||||
#ifdef DEBUG_RANDOM_LOGIC
|
||||
DEBUG_LOG(( "%d: GetGameLogicRandomValue = %d (%d - %d), %s line %d\n",
|
||||
TheGameLogic->getFrame(), rval, lo, hi, file, line ));
|
||||
#endif
|
||||
/**/
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
//
|
||||
// Integer random value
|
||||
//
|
||||
Int GetGameClientRandomValue( int lo, int hi, char *file, int line )
|
||||
{
|
||||
UnsignedInt delta = hi - lo + 1;
|
||||
Int rval;
|
||||
|
||||
if (delta == 0)
|
||||
return hi;
|
||||
|
||||
rval = ((Int)(randomValue(theGameClientSeed) % delta)) + lo;
|
||||
|
||||
/**/
|
||||
#ifdef DEBUG_RANDOM_CLIENT
|
||||
DEBUG_LOG(( "%d: GetGameClientRandomValue = %d (%d - %d), %s line %d\n",
|
||||
TheGameLogic->getFrame(), rval, lo, hi, file, line ));
|
||||
#endif
|
||||
/**/
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
//
|
||||
// Integer random value
|
||||
//
|
||||
Int GetGameAudioRandomValue( int lo, int hi, char *file, int line )
|
||||
{
|
||||
UnsignedInt delta = hi - lo + 1;
|
||||
Int rval;
|
||||
|
||||
if (delta == 0)
|
||||
return hi;
|
||||
|
||||
rval = ((Int)(randomValue(theGameAudioSeed) % delta)) + lo;
|
||||
|
||||
/**/
|
||||
#ifdef DEBUG_RANDOM_AUDIO
|
||||
DEBUG_LOG(( "%d: GetGameAudioRandomValue = %d (%d - %d), %s line %d\n",
|
||||
TheGameLogic->getFrame(), rval, lo, hi, file, line ));
|
||||
#endif
|
||||
/**/
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
//
|
||||
// Real valued random value
|
||||
//
|
||||
Real GetGameLogicRandomValueReal( Real lo, Real hi, char *file, int line )
|
||||
{
|
||||
Real delta = hi - lo;
|
||||
Real rval;
|
||||
|
||||
if (delta <= 0.0f)
|
||||
return hi;
|
||||
|
||||
rval = ((Real)(randomValue(theGameLogicSeed)) * theMultFactor ) * delta + lo;
|
||||
|
||||
DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
|
||||
/**/
|
||||
#ifdef DEBUG_RANDOM_LOGIC
|
||||
DEBUG_LOG(( "%d: GetGameLogicRandomValueReal = %f, %s line %d\n",
|
||||
TheGameLogic->getFrame(), rval, file, line ));
|
||||
#endif
|
||||
/**/
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
//
|
||||
// Real valued random value
|
||||
//
|
||||
Real GetGameClientRandomValueReal( Real lo, Real hi, char *file, int line )
|
||||
{
|
||||
Real delta = hi - lo;
|
||||
Real rval;
|
||||
|
||||
if (delta <= 0.0f)
|
||||
return hi;
|
||||
|
||||
rval = ((Real)(randomValue(theGameClientSeed)) * theMultFactor ) * delta + lo;
|
||||
|
||||
DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
|
||||
/**/
|
||||
#ifdef DEBUG_RANDOM_CLIENT
|
||||
DEBUG_LOG(( "%d: GetGameClientRandomValueReal = %f, %s line %d\n",
|
||||
TheGameLogic->getFrame(), rval, file, line ));
|
||||
#endif
|
||||
/**/
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
//
|
||||
// Real valued random value
|
||||
//
|
||||
Real GetGameAudioRandomValueReal( Real lo, Real hi, char *file, int line )
|
||||
{
|
||||
Real delta = hi - lo;
|
||||
Real rval;
|
||||
|
||||
if (delta <= 0.0f)
|
||||
return hi;
|
||||
|
||||
rval = ((Real)(randomValue(theGameAudioSeed)) * theMultFactor ) * delta + lo;
|
||||
|
||||
DEBUG_ASSERTCRASH( rval >= lo && rval <= hi, ("Bad random val"));
|
||||
/**/
|
||||
#ifdef DEBUG_RANDOM_AUDIO
|
||||
DEBUG_LOG(( "%d: GetGameAudioRandomValueReal = %f, %s line %d\n",
|
||||
TheGameLogic->getFrame(), rval, file, line ));
|
||||
#endif
|
||||
/**/
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
// GameClientRandomVariable
|
||||
//
|
||||
|
||||
/*static*/ const char *GameClientRandomVariable::DistributionTypeNames[] =
|
||||
{
|
||||
"CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
|
||||
};
|
||||
|
||||
/**
|
||||
define the range of random values, and the distribution of values
|
||||
*/
|
||||
void GameClientRandomVariable::setRange( Real low, Real high, DistributionType type )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameClientRandomVariables should have low == high"));
|
||||
m_low = low;
|
||||
m_high = high;
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value from the random distribution
|
||||
*/
|
||||
Real GameClientRandomVariable::getValue( void ) const
|
||||
{
|
||||
switch( m_type )
|
||||
{
|
||||
case CONSTANT:
|
||||
DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameClientRandomVariable\n"));
|
||||
if (m_low == m_high) {
|
||||
return m_low;
|
||||
} // else return as though a UNIFORM.
|
||||
|
||||
case UNIFORM:
|
||||
return GameClientRandomValueReal( m_low, m_high );
|
||||
|
||||
default:
|
||||
/// @todo fill in support for nonuniform GameClientRandomVariables.
|
||||
DEBUG_CRASH(("unsupported DistributionType in GameClientRandomVariable::getValue\n"));
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
// GameLogicRandomVariable
|
||||
//
|
||||
|
||||
/*static*/ const char *GameLogicRandomVariable::DistributionTypeNames[] =
|
||||
{
|
||||
"CONSTANT", "UNIFORM", "GAUSSIAN", "TRIANGULAR", "LOW_BIAS", "HIGH_BIAS"
|
||||
};
|
||||
|
||||
/**
|
||||
define the range of random values, and the distribution of values
|
||||
*/
|
||||
void GameLogicRandomVariable::setRange( Real low, Real high, DistributionType type )
|
||||
{
|
||||
DEBUG_ASSERTCRASH(!(m_type == CONSTANT && m_low != m_high), ("CONSTANT GameLogicRandomVariables should have low == high"));
|
||||
m_low = low;
|
||||
m_high = high;
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value from the random distribution
|
||||
*/
|
||||
Real GameLogicRandomVariable::getValue( void ) const
|
||||
{
|
||||
switch( m_type )
|
||||
{
|
||||
case CONSTANT:
|
||||
DEBUG_ASSERTLOG(m_low == m_high, ("m_low != m_high for a CONSTANT GameLogicRandomVariable"));
|
||||
if (m_low == m_high) {
|
||||
return m_low;
|
||||
} // else return as though a UNIFORM.
|
||||
|
||||
case UNIFORM:
|
||||
return GameLogicRandomValueReal( m_low, m_high );
|
||||
|
||||
default:
|
||||
/// @todo fill in support for nonuniform GameLogicRandomVariables.
|
||||
DEBUG_CRASH(("unsupported DistributionType in GameLogicRandomVariable::getValue\n"));
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1616
Generals/Code/GameEngine/Source/Common/Recorder.cpp
Normal file
1616
Generals/Code/GameEngine/Source/Common/Recorder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
213
Generals/Code/GameEngine/Source/Common/SkirmishBattleHonors.cpp
Normal file
213
Generals/Code/GameEngine/Source/Common/SkirmishBattleHonors.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// FILE: SkirmishBattleHonors.cpp
|
||||
// Author: Matthew D. Campbell, April 2002
|
||||
// Description: Saving/Loading of skirmish battle honors info
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "Common/UserPreferences.h"
|
||||
#include "Common/SkirmishBattleHonors.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/PlayerTemplate.h"
|
||||
#include "Common/QuotedPrintable.h"
|
||||
#include "Common/MultiplayerSettings.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEFINES ////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE TYPES //////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE DATA ///////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PUBLIC DATA ////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SkirmishBattleHonors base class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
SkirmishBattleHonors::SkirmishBattleHonors()
|
||||
{
|
||||
load("SkirmishStats.ini");
|
||||
}
|
||||
|
||||
SkirmishBattleHonors::~SkirmishBattleHonors()
|
||||
{
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setWins(Int val)
|
||||
{
|
||||
setInt("Wins", val);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getWins(void) const
|
||||
{
|
||||
return getInt("Wins", 0);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setLosses(Int val)
|
||||
{
|
||||
setInt("Losses", val);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getLosses(void) const
|
||||
{
|
||||
return getInt("Losses", 0);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setWinStreak(Int val)
|
||||
{
|
||||
setInt("WinStreak", val);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getWinStreak(void) const
|
||||
{
|
||||
return getInt("WinStreak", 0);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setBestWinStreak(Int val)
|
||||
{
|
||||
setInt("BestWinStreak", val);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getBestWinStreak(void) const
|
||||
{
|
||||
return getInt("BestWinStreak", 0);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setChallengeMedals(Int val)
|
||||
{
|
||||
setInt("Challenge", val);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getChallengeMedals(void) const
|
||||
{
|
||||
return getInt("Challenge", 0);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setBuiltSCUD(void)
|
||||
{
|
||||
setBool("SCUD", TRUE);
|
||||
}
|
||||
|
||||
Bool SkirmishBattleHonors::builtSCUD(void) const
|
||||
{
|
||||
return getBool("SCUD", FALSE);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setBuiltParticleCannon(void)
|
||||
{
|
||||
setBool("PPC", TRUE);
|
||||
}
|
||||
|
||||
Bool SkirmishBattleHonors::builtParticleCannon(void) const
|
||||
{
|
||||
return getBool("PPC", FALSE);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setBuiltNuke(void)
|
||||
{
|
||||
setBool("Nuke", TRUE);
|
||||
}
|
||||
|
||||
Bool SkirmishBattleHonors::builtNuke(void) const
|
||||
{
|
||||
return getBool("Nuke", FALSE);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setHonors(Int which)
|
||||
{
|
||||
Int honors = getInt("Honors", 0);
|
||||
setInt("Honors", honors | which);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getHonors(void) const
|
||||
{
|
||||
return getInt("Honors", 0);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setEnduranceMedal(AsciiString mapName, Int difficulty, int numAIs)
|
||||
{
|
||||
AsciiString key;
|
||||
key.format("%s_%d", mapName.str(), difficulty);
|
||||
setInt(key, numAIs);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getEnduranceMedal(AsciiString mapName, Int difficulty) const
|
||||
{
|
||||
AsciiString key;
|
||||
key.format("%s_%d", mapName.str(), difficulty);
|
||||
return getInt(key, 0);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setLastGeneral(AsciiString val)
|
||||
{
|
||||
setAsciiString("LastHouse", val);
|
||||
}
|
||||
|
||||
AsciiString SkirmishBattleHonors::getLastGeneral(void) const
|
||||
{
|
||||
return getAsciiString("LastHouse", AsciiString::TheEmptyString);
|
||||
}
|
||||
|
||||
void SkirmishBattleHonors::setNumGamesLoyal(Int val)
|
||||
{
|
||||
setInt("LoyalGames", val);
|
||||
}
|
||||
|
||||
Int SkirmishBattleHonors::getNumGamesLoyal(void) const
|
||||
{
|
||||
return getInt("LoyalGames", 0);
|
||||
}
|
||||
|
||||
806
Generals/Code/GameEngine/Source/Common/StateMachine.cpp
Normal file
806
Generals/Code/GameEngine/Source/Common/StateMachine.cpp
Normal file
@@ -0,0 +1,806 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// StateMachine.cpp
|
||||
// Implementation of basic state machine
|
||||
// Author: Michael S. Booth, January 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/Errors.h"
|
||||
#include "Common/StateMachine.h"
|
||||
#include "Common/ThingTemplate.h"
|
||||
#include "Common/GameState.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/Xfer.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameLogic/Object.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------ Performance Timers
|
||||
//#include "Common/PerfMetrics.h"
|
||||
//#include "Common/PerfTimer.h"
|
||||
|
||||
//static PerfTimer s_stateMachineTimer("StateMachine::update", false, PERFMETRICS_LOGIC_STARTFRAME, PERFMETRICS_LOGIC_STOPFRAME);
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
State::State( StateMachine *machine, AsciiString name )
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
: m_name(name)
|
||||
#endif
|
||||
{
|
||||
m_ID = INVALID_STATE_ID;
|
||||
m_successStateID = INVALID_STATE_ID;
|
||||
m_failureStateID = INVALID_STATE_ID;
|
||||
m_machine = machine;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Add another state transition condition for this state
|
||||
*/
|
||||
void State::friend_onCondition( StateTransFuncPtr test, StateID toStateID, void* userData, const char* description )
|
||||
{
|
||||
m_transitions.push_back(TransitionInfo(test, toStateID, userData, description));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class StIncrementer
|
||||
{
|
||||
private:
|
||||
Int& num;
|
||||
public:
|
||||
StIncrementer(Int& n) : num(n)
|
||||
{
|
||||
++num;
|
||||
}
|
||||
~StIncrementer()
|
||||
{
|
||||
--num;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Given a return code, handle state transitions
|
||||
*/
|
||||
StateReturnType State::friend_checkForTransitions( StateReturnType status )
|
||||
{
|
||||
static Int checkfortransitionsnum = 0;
|
||||
|
||||
StIncrementer inc(checkfortransitionsnum);
|
||||
if (checkfortransitionsnum >= 20)
|
||||
{
|
||||
DEBUG_CRASH(("checkfortransitionsnum is > 20"));
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
DEBUG_ASSERTCRASH(!IS_STATE_SLEEP(status), ("Please handle sleep states prior to this"));
|
||||
|
||||
// handle transitions
|
||||
switch( status )
|
||||
{
|
||||
case STATE_SUCCESS:
|
||||
// check if machine should exit
|
||||
if (m_successStateID == EXIT_MACHINE_WITH_SUCCESS)
|
||||
{
|
||||
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
|
||||
return STATE_SUCCESS;
|
||||
}
|
||||
else if (m_successStateID == EXIT_MACHINE_WITH_FAILURE)
|
||||
{
|
||||
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
// move to new state
|
||||
return getMachine()->internalSetState( m_successStateID );
|
||||
|
||||
case STATE_FAILURE:
|
||||
// check if machine should exit
|
||||
if (m_failureStateID == EXIT_MACHINE_WITH_SUCCESS)
|
||||
{
|
||||
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
|
||||
return STATE_SUCCESS;
|
||||
}
|
||||
else if (m_failureStateID == EXIT_MACHINE_WITH_FAILURE)
|
||||
{
|
||||
getMachine()->internalSetState( MACHINE_DONE_STATE_ID );
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
// move to new state
|
||||
return getMachine()->internalSetState( m_failureStateID );
|
||||
|
||||
case STATE_CONTINUE:
|
||||
|
||||
// check transition condition list
|
||||
if (!m_transitions.empty())
|
||||
{
|
||||
for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
|
||||
{
|
||||
if (it->test( this, it->userData ))
|
||||
{
|
||||
// test returned true, change to associated state
|
||||
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (getMachine()->getWantsDebugOutput())
|
||||
{
|
||||
DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
|
||||
getMachine()->getName().str(), it->description ? it->description : "[no description]"));
|
||||
}
|
||||
#endif
|
||||
|
||||
// check if machine should exit
|
||||
if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
|
||||
{
|
||||
return STATE_SUCCESS;
|
||||
}
|
||||
else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
|
||||
{
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
// move to new state
|
||||
return getMachine()->internalSetState( it->toStateID );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// the machine keeps running
|
||||
return STATE_CONTINUE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Given a return code, handle state transitions
|
||||
*/
|
||||
StateReturnType State::friend_checkForSleepTransitions( StateReturnType status )
|
||||
{
|
||||
static Int checkfortransitionsnum = 0;
|
||||
|
||||
StIncrementer inc(checkfortransitionsnum);
|
||||
if (checkfortransitionsnum >= 20)
|
||||
{
|
||||
DEBUG_CRASH(("checkforsleeptransitionsnum is > 20"));
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
DEBUG_ASSERTCRASH(IS_STATE_SLEEP(status), ("Please only pass sleep states here"));
|
||||
|
||||
// check transition condition list
|
||||
if (m_transitions.empty())
|
||||
return status;
|
||||
|
||||
for(std::vector<TransitionInfo>::const_iterator it = m_transitions.begin(); it != m_transitions.end(); ++it)
|
||||
{
|
||||
if (!it->test( this, it->userData ))
|
||||
continue;
|
||||
|
||||
// test returned true, change to associated state
|
||||
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (getMachine()->getWantsDebugOutput())
|
||||
{
|
||||
DEBUG_LOG(("%d '%s' -- '%s' condition '%s' returned true!\n", TheGameLogic->getFrame(), getMachineOwner()->getTemplate()->getName().str(),
|
||||
getMachine()->getName().str(), it->description ? it->description : "[no description]"));
|
||||
}
|
||||
#endif
|
||||
|
||||
// check if machine should exit
|
||||
if (it->toStateID == EXIT_MACHINE_WITH_SUCCESS)
|
||||
{
|
||||
return STATE_SUCCESS;
|
||||
}
|
||||
else if (it->toStateID == EXIT_MACHINE_WITH_FAILURE)
|
||||
{
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// move to new state
|
||||
return getMachine()->internalSetState( it->toStateID );
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
StateMachine::StateMachine( Object *owner, AsciiString name )
|
||||
{
|
||||
m_owner = owner;
|
||||
m_sleepTill = 0;
|
||||
m_defaultStateID = INVALID_STATE_ID;
|
||||
m_defaultStateInited = false;
|
||||
m_currentState = NULL;
|
||||
m_locked = false;
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
m_name = name;
|
||||
m_debugOutput = false;
|
||||
m_lockedby = NULL;
|
||||
#endif
|
||||
internalClear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Destructor. Destroy any states attached to this machine.
|
||||
*/
|
||||
StateMachine::~StateMachine()
|
||||
{
|
||||
|
||||
// do not allow current state to exit
|
||||
if (m_currentState)
|
||||
m_currentState->onExit( EXIT_RESET );
|
||||
|
||||
std::map<StateID, State *>::iterator i;
|
||||
|
||||
// delete all states in the mapping
|
||||
for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
|
||||
{
|
||||
if ((*i).second)
|
||||
(*i).second->deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
Bool StateMachine::getWantsDebugOutput() const
|
||||
{
|
||||
if (m_debugOutput)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TheGlobalData->m_stateMachineDebug)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OBJECT_ID_EXISTS
|
||||
if (TheObjectIDToDebug != 0 && getOwner() != NULL && getOwner()->getID() == TheObjectIDToDebug)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Clear the internal variables of state machine to known values.
|
||||
*/
|
||||
void StateMachine::internalClear()
|
||||
{
|
||||
m_goalObjectID = INVALID_ID;
|
||||
m_goalPosition.x = 0.0f;
|
||||
m_goalPosition.y = 0.0f;
|
||||
m_goalPosition.z = 0.0f;
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (getWantsDebugOutput())
|
||||
{
|
||||
DEBUG_LOG(("%d '%s' -- '%s' %x internalClear()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Clear the machine
|
||||
*/
|
||||
void StateMachine::clear()
|
||||
{
|
||||
// if the machine is locked, it cannot be cleared
|
||||
if (m_locked)
|
||||
{
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
|
||||
DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// invoke the old state's onExit()
|
||||
if (m_currentState)
|
||||
m_currentState->onExit( EXIT_RESET );
|
||||
|
||||
m_currentState = NULL;
|
||||
|
||||
internalClear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Reset the machine to its default state
|
||||
*/
|
||||
StateReturnType StateMachine::resetToDefaultState()
|
||||
{
|
||||
// if the machine is locked, it cannot be reset
|
||||
if (m_locked)
|
||||
{
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
|
||||
DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
|
||||
#endif
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
if (!m_defaultStateInited)
|
||||
{
|
||||
DEBUG_CRASH(("you may not call resetToDefaultState before initDefaultState"));
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
// allow current state to exit with EXIT_RESET if present
|
||||
if (m_currentState)
|
||||
m_currentState->onExit( EXIT_RESET );
|
||||
m_currentState = NULL;
|
||||
|
||||
//
|
||||
// the current state has done an onExit, clear the internal guts before we set
|
||||
// the new state, to clear it after the new state is set might be overwriting
|
||||
// things the new state transition causes to happen
|
||||
//
|
||||
internalClear();
|
||||
|
||||
// change to the default state
|
||||
StateReturnType status = internalSetState( m_defaultStateID );
|
||||
|
||||
DEBUG_ASSERTCRASH( status != STATE_FAILURE, ( "StateMachine::resetToDefaultState() Error setting default state" ) );
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Run one step of the machine
|
||||
*/
|
||||
StateReturnType StateMachine::updateStateMachine()
|
||||
{
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
if (m_sleepTill != 0 && now < m_sleepTill)
|
||||
{
|
||||
if( m_currentState == NULL )
|
||||
{
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
|
||||
}
|
||||
|
||||
// not sleeping anymore
|
||||
m_sleepTill = 0;
|
||||
|
||||
if (m_currentState)
|
||||
{
|
||||
// update() can change m_currentState, so save it for a moment...
|
||||
State* stateBeforeUpdate = m_currentState;
|
||||
|
||||
// execute this state
|
||||
StateReturnType status = m_currentState->update();
|
||||
|
||||
// it is possible that the state's update() method may cause the state to be destroyed
|
||||
if (m_currentState == NULL)
|
||||
{
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
// here's the scenario:
|
||||
// -- State A calls foo() and then says "sleep for 2000 frames".
|
||||
// -- however, foo() called setState() to State B. thus our current state is not the same.
|
||||
// -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
|
||||
// so that the new state will be called immediately.
|
||||
if (stateBeforeUpdate != m_currentState)
|
||||
{
|
||||
status = STATE_CONTINUE;
|
||||
}
|
||||
|
||||
if (IS_STATE_SLEEP(status))
|
||||
{
|
||||
// hey, we're sleepy!
|
||||
m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
|
||||
return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// check for state transitions, possibly exiting this machine
|
||||
return m_currentState->friend_checkForTransitions( status );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_CRASH(("State machine has no current state -- did you remember to call initDefaultState?"));
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Given a unique (for this machine) id number, and an instance of the
|
||||
* State class, the machine records this as a possible state, and
|
||||
* retains the id mapping.
|
||||
* These state id's are used to change the machine's state via setState().
|
||||
*/
|
||||
void StateMachine::defineState( StateID id, State *state, StateID successID, StateID failureID, const StateConditionInfo* conditions )
|
||||
{
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
DEBUG_ASSERTCRASH(m_stateMap.find( id ) == m_stateMap.end(), ("duplicate state ID in statemachine %s\n",m_name.str()));
|
||||
#endif
|
||||
|
||||
// map the ID to the state
|
||||
m_stateMap.insert( std::map<StateID, State *>::value_type( id, state ) );
|
||||
|
||||
// store the ID in the state itself, as well
|
||||
state->friend_setID( id );
|
||||
|
||||
state->friend_onSuccess(successID);
|
||||
state->friend_onFailure(failureID);
|
||||
|
||||
while (conditions && conditions->test != NULL)
|
||||
{
|
||||
state->friend_onCondition(conditions->test, conditions->toStateID, conditions->userData);
|
||||
++conditions;
|
||||
}
|
||||
|
||||
if (m_defaultStateID == INVALID_STATE_ID)
|
||||
m_defaultStateID = id;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Given a state ID, return the state instance
|
||||
*/
|
||||
State *StateMachine::internalGetState( StateID id )
|
||||
{
|
||||
// locate the actual state associated with the given ID
|
||||
std::map<StateID, State *>::iterator i;
|
||||
i = m_stateMap.find( id );
|
||||
|
||||
if (i == m_stateMap.end())
|
||||
{
|
||||
DEBUG_CRASH(( "StateMachine::internalGetState(): Invalid state" ));
|
||||
throw ERROR_BAD_ARG;
|
||||
}
|
||||
|
||||
return (*i).second;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Change the current state of the machine.
|
||||
* This causes the old state's onExit() method to be invoked,
|
||||
* and the new state's onEnter() method to be invoked.
|
||||
*/
|
||||
StateReturnType StateMachine::setState( StateID newStateID )
|
||||
{
|
||||
// if the machine is locked, it cannot change state via external events
|
||||
if (m_locked)
|
||||
{
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (m_currentState) DEBUG_LOG((" cur state '%s'\n", m_currentState->getName().str()));
|
||||
DEBUG_LOG(("machine is locked (by %s), cannot be cleared (Please don't ignore; this generally indicates a potential logic flaw)\n",m_lockedby));
|
||||
#endif
|
||||
return STATE_CONTINUE;
|
||||
}
|
||||
|
||||
return internalSetState( newStateID );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Change the current state of the machine.
|
||||
* This causes the old state's onExit() method to be invoked,
|
||||
* and the new state's onEnter() method to be invoked.
|
||||
*/
|
||||
StateReturnType StateMachine::internalSetState( StateID newStateID )
|
||||
{
|
||||
State *newState = NULL;
|
||||
|
||||
// anytime the state changes, stop sleeping
|
||||
m_sleepTill = 0;
|
||||
|
||||
// if we're not setting the "done" state ID we will continue with the actual transition
|
||||
if( newStateID != MACHINE_DONE_STATE_ID )
|
||||
{
|
||||
|
||||
// if incoming state is invalid, go to the machine's default state
|
||||
if (newStateID == INVALID_STATE_ID)
|
||||
{
|
||||
newStateID = m_defaultStateID;
|
||||
if (newStateID == INVALID_STATE_ID)
|
||||
{
|
||||
DEBUG_CRASH(("you may NEVER set the current state to an invalid state id."));
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// extract the state associated with the given ID
|
||||
newState = internalGetState( newStateID );
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (getWantsDebugOutput())
|
||||
{
|
||||
StateID curState = INVALID_STATE_ID;
|
||||
if (m_currentState) {
|
||||
curState = m_currentState->getID();
|
||||
}
|
||||
DEBUG_LOG(("%d '%s' -- '%s' %x exit ", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
|
||||
if (m_currentState) {
|
||||
DEBUG_LOG((" '%s' ", m_currentState->getName().str()));
|
||||
} else {
|
||||
DEBUG_LOG((" INVALID_STATE_ID "));
|
||||
}
|
||||
if (newState) {
|
||||
DEBUG_LOG(("enter '%s' \n", newState->getName().str()));
|
||||
} else {
|
||||
DEBUG_LOG(("to INVALID_STATE\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// invoke the old state's onExit()
|
||||
if (m_currentState)
|
||||
m_currentState->onExit( EXIT_NORMAL );
|
||||
|
||||
// set the new state
|
||||
m_currentState = newState;
|
||||
|
||||
// invoke the new state's onEnter()
|
||||
/// @todo It might be useful to pass the old state in... (MSB)
|
||||
if( m_currentState )
|
||||
{
|
||||
// onEnter() could conceivably change m_currentState, so save it for a moment...
|
||||
State* stateBeforeEnter = m_currentState;
|
||||
|
||||
StateReturnType status = m_currentState->onEnter();
|
||||
|
||||
// it is possible that the state's onEnter() method may cause the state to be destroyed
|
||||
if (m_currentState == NULL)
|
||||
{
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
|
||||
// here's the scenario:
|
||||
// -- State A calls foo() and then says "sleep for 2000 frames".
|
||||
// -- however, foo() called setState() to State B. thus our current state is not the same.
|
||||
// -- thus, if the state changed, we must ignore any sleep result and pretend we got STATE_CONTINUE,
|
||||
// so that the new state will be called immediately.
|
||||
if (stateBeforeEnter != m_currentState)
|
||||
{
|
||||
status = STATE_CONTINUE;
|
||||
}
|
||||
|
||||
if (IS_STATE_SLEEP(status))
|
||||
{
|
||||
// hey, we're sleepy!
|
||||
UnsignedInt now = TheGameLogic->getFrame();
|
||||
m_sleepTill = now + GET_STATE_SLEEP_FRAMES(status);
|
||||
return m_currentState->friend_checkForSleepTransitions( STATE_SLEEP(m_sleepTill - now) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// check for state transitions, possibly exiting this machine
|
||||
return m_currentState->friend_checkForTransitions( status );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return STATE_CONTINUE; // irrelevant return code, but we must return something
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/**
|
||||
* Define the default state of the machine, and
|
||||
* set the machine's state to it.
|
||||
*/
|
||||
StateReturnType StateMachine::initDefaultState()
|
||||
{
|
||||
DEBUG_ASSERTCRASH(!m_locked, ("Machine is locked here, but probably should not be"));
|
||||
if (m_defaultStateInited)
|
||||
{
|
||||
DEBUG_CRASH(("you may not call initDefaultState twice for the same StateMachine"));
|
||||
return STATE_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_defaultStateInited = true;
|
||||
return internalSetState( m_defaultStateID );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void StateMachine::setGoalObject( const Object *obj )
|
||||
{
|
||||
if (m_locked)
|
||||
return;
|
||||
|
||||
internalSetGoalObject( obj );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool StateMachine::isGoalObjectDestroyed() const
|
||||
{
|
||||
if (m_goalObjectID == 0)
|
||||
{
|
||||
return false; // never had a goal object
|
||||
}
|
||||
return getGoalObject() == NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void StateMachine::halt()
|
||||
{
|
||||
m_locked = true;
|
||||
m_currentState = NULL; // don't exit current state, just clear it.
|
||||
#ifdef STATE_MACHINE_DEBUG
|
||||
if (getWantsDebugOutput())
|
||||
{
|
||||
DEBUG_LOG(("%d '%s' -- '%s' %x halt()\n", TheGameLogic->getFrame(), m_owner->getTemplate()->getName().str(), m_name.str(), this));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void StateMachine::internalSetGoalObject( const Object *obj )
|
||||
{
|
||||
if (obj) {
|
||||
m_goalObjectID = obj->getID();
|
||||
internalSetGoalPosition(obj->getPosition());
|
||||
}
|
||||
else {
|
||||
m_goalObjectID = INVALID_ID;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Object *StateMachine::getGoalObject()
|
||||
{
|
||||
return TheGameLogic->findObjectByID( m_goalObjectID );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const Object *StateMachine::getGoalObject() const
|
||||
{
|
||||
return TheGameLogic->findObjectByID( m_goalObjectID );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void StateMachine::setGoalPosition( const Coord3D *pos )
|
||||
{
|
||||
if (m_locked)
|
||||
return;
|
||||
|
||||
internalSetGoalPosition( pos );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void StateMachine::internalSetGoalPosition( const Coord3D *pos )
|
||||
{
|
||||
if (pos) {
|
||||
m_goalPosition = *pos;
|
||||
// Don't clear the goal object, or everything breaks. Like construction of buildings.
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void StateMachine::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer Method
|
||||
* Version Info
|
||||
* 1: Initial version
|
||||
*/
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void StateMachine::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
xfer->xferUnsignedInt(&m_sleepTill);
|
||||
xfer->xferUnsignedInt(&m_defaultStateID);
|
||||
StateID curStateID = getCurrentStateID();
|
||||
xfer->xferUnsignedInt(&curStateID);
|
||||
if (xfer->getXferMode() == XFER_LOAD) {
|
||||
// We are going to jump into the current state. We don't call onEnter or onExit, because the
|
||||
// state was already active when we saved.
|
||||
m_currentState = internalGetState( curStateID );
|
||||
}
|
||||
|
||||
Bool snapshotAllStates = false;
|
||||
#ifdef _DEBUG
|
||||
//snapshotAllStates = true;
|
||||
#endif
|
||||
xfer->xferBool(&snapshotAllStates);
|
||||
if (snapshotAllStates) {
|
||||
std::map<StateID, State *>::iterator i;
|
||||
// count all states in the mapping
|
||||
Int count = 0;
|
||||
for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i )
|
||||
count++;
|
||||
Int saveCount = count;
|
||||
xfer->xferInt(&saveCount);
|
||||
if (saveCount!=count) {
|
||||
DEBUG_CRASH(("State count mismatch - %d expected, %d read", count, saveCount));
|
||||
throw SC_INVALID_DATA;
|
||||
}
|
||||
for( i = m_stateMap.begin(); i != m_stateMap.end(); ++i ) {
|
||||
State *state = (*i).second;
|
||||
StateID id = state->getID();
|
||||
xfer->xferUnsignedInt(&id);
|
||||
if (id!=state->getID()) {
|
||||
DEBUG_CRASH(("State ID mismatch - %d expected, %d read", state->getID(), id));
|
||||
throw SC_INVALID_DATA;
|
||||
}
|
||||
xfer->xferSnapshot(state);
|
||||
}
|
||||
|
||||
} else {
|
||||
xfer->xferSnapshot(m_currentState);
|
||||
}
|
||||
|
||||
|
||||
xfer->xferObjectID(&m_goalObjectID);
|
||||
xfer->xferCoord3D(&m_goalPosition);
|
||||
xfer->xferBool(&m_locked);
|
||||
xfer->xferBool(&m_defaultStateInited);
|
||||
} // end xfer
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void StateMachine::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
|
||||
425
Generals/Code/GameEngine/Source/Common/StatsCollector.cpp
Normal file
425
Generals/Code/GameEngine/Source/Common/StatsCollector.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: StatsCollector.cpp /////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Electronic Arts Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2002 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// created: Jul 2002
|
||||
//
|
||||
// Filename: StatsCollector.cpp
|
||||
//
|
||||
// author: Chris Huybregts
|
||||
//
|
||||
// purpose: Convinience class to gather player stats
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "Common/StatsCollector.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "Common/PlayerList.h"
|
||||
#include "Common/Player.h"
|
||||
#include "Common/GlobalData.h"
|
||||
#include "Common/Money.h"
|
||||
#include "GameLogic/Object.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "GameNetwork/NetworkUtil.h"
|
||||
#include "GameNetwork/LANAPICallbacks.h"
|
||||
//-----------------------------------------------------------------------------
|
||||
// DEFINES ////////////////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
StatsCollector *TheStatsCollector = NULL;
|
||||
|
||||
static char statsDir[255] = "Stats\\";
|
||||
//-----------------------------------------------------------------------------
|
||||
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// init all
|
||||
//=============================================================================
|
||||
StatsCollector::StatsCollector( void )
|
||||
{
|
||||
//Added By Sadullah Nader
|
||||
//Initialization(s) inserted
|
||||
m_isScrolling = FALSE;
|
||||
m_scrollBeginTime = 0;
|
||||
m_scrollTime = 0;
|
||||
|
||||
//
|
||||
m_timeCount = 0;
|
||||
m_buildCommands = 0;
|
||||
m_moveCommands = 0;
|
||||
m_attackCommands = 0;
|
||||
m_scrollMapCommands = 0;
|
||||
m_AIUnits = 0;
|
||||
m_playerUnits = 0;
|
||||
|
||||
m_lastUpdate = 0;
|
||||
m_startFrame = TheGameLogic->getFrame();
|
||||
|
||||
}
|
||||
//Destructor
|
||||
//=============================================================================
|
||||
StatsCollector::~StatsCollector( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Reset and create the file header
|
||||
//=============================================================================
|
||||
void StatsCollector::reset( void )
|
||||
{
|
||||
|
||||
// make sure we have a stats Dir.
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (TheGlobalData->m_saveStats)
|
||||
{
|
||||
AsciiString playtestDir = TheGlobalData->m_baseStatsDir;
|
||||
playtestDir.concat(statsDir);
|
||||
if (TheNetwork)
|
||||
{
|
||||
if (TheLAN)
|
||||
{
|
||||
TheFileSystem->createDirectory(playtestDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
TheFileSystem->createDirectory(AsciiString(statsDir));
|
||||
createFileName();
|
||||
writeInitialFileInfo();
|
||||
|
||||
// zero out
|
||||
zeroOutStats();
|
||||
|
||||
m_lastUpdate = TheGameLogic->getFrame(); // timeGetTime();
|
||||
}
|
||||
|
||||
// Msgs pass through here so we can track whichever ones we want
|
||||
//=============================================================================
|
||||
void StatsCollector::collectMsgStats( const GameMessage *msg )
|
||||
{
|
||||
// We only care about our own messages.
|
||||
if(ThePlayerList->getLocalPlayer()->getPlayerIndex() != msg->getPlayerIndex())
|
||||
return;
|
||||
|
||||
switch (msg->getType())
|
||||
{
|
||||
case GameMessage::MSG_QUEUE_UNIT_CREATE:
|
||||
case GameMessage::MSG_DOZER_CONSTRUCT:
|
||||
case GameMessage::MSG_DOZER_CONSTRUCT_LINE:
|
||||
{
|
||||
++m_buildCommands;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Loop through all objects and count up the ones we want. (Very Slow!!!)
|
||||
//=============================================================================
|
||||
void StatsCollector::collectUnitCountStats( void )
|
||||
{
|
||||
|
||||
for(Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject())
|
||||
{
|
||||
|
||||
if((!(obj->isKindOf(KINDOF_INFANTRY) || obj->isKindOf(KINDOF_VEHICLE))) || ( obj->isNeutralControlled()) ||(obj->getControllingPlayer()->getSide().compare("Civilian") == 0))
|
||||
continue;
|
||||
|
||||
if(obj->getControllingPlayer()->isLocalPlayer())
|
||||
{
|
||||
++m_playerUnits;
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_AIUnits;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// call every frame and only do stuff when our time is up
|
||||
//=============================================================================
|
||||
void StatsCollector::update( void )
|
||||
{
|
||||
if(m_lastUpdate + (TheGlobalData->m_playStats * LOGICFRAMES_PER_SECOND) > TheGameLogic->getFrame())
|
||||
return;
|
||||
|
||||
collectUnitCountStats();
|
||||
|
||||
if(m_isScrolling)
|
||||
{
|
||||
m_scrollTime += TheGameLogic->getFrame() - m_scrollBeginTime;
|
||||
m_scrollBeginTime = TheGameLogic->getFrame();
|
||||
}
|
||||
|
||||
m_timeCount += TheGlobalData->m_playStats;
|
||||
writeStatInfo();
|
||||
|
||||
zeroOutStats();
|
||||
|
||||
m_lastUpdate = TheGameLogic->getFrame(); //timeGetTime();
|
||||
|
||||
}
|
||||
|
||||
void StatsCollector::incrementScrollMoveCount( void )
|
||||
{
|
||||
++m_scrollMapCommands;
|
||||
}
|
||||
|
||||
void StatsCollector::incrementAttackCount( void )
|
||||
{
|
||||
++m_attackCommands;
|
||||
}
|
||||
|
||||
void StatsCollector::incrementBuildCount( void )
|
||||
{
|
||||
++m_buildCommands;
|
||||
}
|
||||
void StatsCollector::incrementMoveCount( void )
|
||||
{
|
||||
++m_moveCommands;
|
||||
}
|
||||
|
||||
void StatsCollector::writeFileEnd( void )
|
||||
{
|
||||
//open the file
|
||||
FILE *f = fopen(m_statsFileName.str(), "a");
|
||||
if(!f)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(f, ("Unable to open file %s to write", m_statsFileName.str()));
|
||||
return;
|
||||
}
|
||||
|
||||
m_timeCount += (TheGameLogic->getFrame() - m_lastUpdate) / LOGICFRAMES_PER_SECOND;
|
||||
writeStatInfo();
|
||||
fprintf(f, "---------------------------------------------------\n");
|
||||
|
||||
// Time
|
||||
struct tm *newTime;
|
||||
time_t aclock;
|
||||
time( &aclock );
|
||||
newTime = localtime( &aclock );
|
||||
fprintf(f, "End Time:\t%s\n",asctime(newTime) );
|
||||
|
||||
fprintf(f, "=KEY===============================================\n");
|
||||
fprintf(f, "Time* = The Time Interval\n");
|
||||
fprintf(f, "BC = Build Commands\n");
|
||||
fprintf(f, "MC = Move Commands\n");
|
||||
fprintf(f, "AC = Attack Commands\n");
|
||||
fprintf(f, "SMC = Scroll Map Commands\n");
|
||||
fprintf(f, "ST* = Scroll Time in Seconds\n");
|
||||
fprintf(f, "OC = Other Commands (N/A)\n");
|
||||
fprintf(f, "$$$ = Local Player's Cash Amount\n");
|
||||
fprintf(f, "#PU = # of Player's Units\n");
|
||||
fprintf(f, "#AIU = # of AI's Units\n");
|
||||
fprintf(f, "===================================================\n");
|
||||
fprintf(f, "* Times are in Game Seconds which are based off of frames. Current fps is set to %d\n", LOGICFRAMES_PER_SECOND);
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (TheGlobalData->m_benchmarkTimer > 0)
|
||||
{
|
||||
fprintf(f, "\n*** BENCHMARK MODE STATS ***\n");
|
||||
fprintf(f, " Frames = %d\n", TheGameLogic->getFrame()-m_startFrame);
|
||||
fprintf(f, "Seconds = %d\n", TheGlobalData->m_benchmarkTimer);
|
||||
fprintf(f, " FPS = %.2f\n", ((Real)TheGameLogic->getFrame()-(Real)m_startFrame)/(Real)TheGlobalData->m_benchmarkTimer);
|
||||
}
|
||||
#endif
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
void StatsCollector::startScrollTime( void )
|
||||
{
|
||||
m_isScrolling = TRUE;
|
||||
m_scrollBeginTime = TheGameLogic->getFrame();
|
||||
++m_scrollMapCommands;
|
||||
}
|
||||
|
||||
void StatsCollector::endScrollTime( void )
|
||||
{
|
||||
if(!m_isScrolling)
|
||||
return;
|
||||
|
||||
m_isScrolling = FALSE;
|
||||
|
||||
m_scrollTime += TheGameLogic->getFrame() - m_scrollBeginTime;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void StatsCollector::zeroOutStats( void )
|
||||
{
|
||||
m_buildCommands = 0;
|
||||
m_moveCommands = 0;
|
||||
m_attackCommands = 0;
|
||||
m_scrollMapCommands = 0;
|
||||
m_AIUnits = 0;
|
||||
m_playerUnits = 0;
|
||||
m_scrollTime = 0;
|
||||
}
|
||||
|
||||
// create the filename based off of map time and date
|
||||
//=============================================================================
|
||||
void StatsCollector::createFileName( void )
|
||||
{
|
||||
m_statsFileName.clear();
|
||||
// Date and Time
|
||||
char datestr[256] = "";
|
||||
time_t longTime;
|
||||
struct tm *curtime;
|
||||
time(&longTime);
|
||||
curtime = localtime(&longTime);
|
||||
strftime(datestr, 256, "_%b%d_%I%M%p", curtime);
|
||||
// const MapMetaData *m = TheMapCache->findMap(TheGlobalData->m_mapName);
|
||||
AsciiString name = TheGlobalData->m_mapName;
|
||||
const char *fname = name.reverseFind('\\');
|
||||
if (fname)
|
||||
name = fname+1;
|
||||
name.removeLastChar(); // p
|
||||
name.removeLastChar(); // a
|
||||
name.removeLastChar(); // m
|
||||
name.removeLastChar(); // .
|
||||
m_statsFileName.clear();
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (TheGlobalData->m_saveStats)
|
||||
{
|
||||
m_statsFileName.set(TheGlobalData->m_baseStatsDir);
|
||||
m_statsFileName.concat(statsDir);
|
||||
|
||||
if (TheNetwork)
|
||||
{
|
||||
if (TheLAN)
|
||||
{
|
||||
GameInfo *game = TheLAN->GetMyGame();
|
||||
AsciiString players;
|
||||
AsciiString full;
|
||||
AsciiString fullPlusNum;
|
||||
for (Int i=0; i<MAX_SLOTS; ++i)
|
||||
{
|
||||
GameSlot *slot = game->getSlot(i);
|
||||
if (slot && slot->isHuman())
|
||||
{
|
||||
AsciiString player;
|
||||
player.format("%ls_", slot->getName().str());
|
||||
players.concat(player);
|
||||
}
|
||||
}
|
||||
full.format("%s%s_%d_%d", players.str(), name.str(), game->getSeed(), game->getLocalSlotNum());
|
||||
AsciiString testString;
|
||||
testString.format("%s%s.txt", m_statsFileName.str(), full.str());
|
||||
m_statsFileName = testString;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_statsFileName.format("%s%s%s.txt",statsDir, name.str(),datestr);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_statsFileName.format("%s%s%s.txt",statsDir, name.str(),datestr);
|
||||
}
|
||||
}
|
||||
|
||||
// create the header of the file
|
||||
//=============================================================================
|
||||
void StatsCollector::writeInitialFileInfo()
|
||||
{
|
||||
//open the file
|
||||
FILE *f = fopen(m_statsFileName.str(), "w");
|
||||
if(!f)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(f, ("Unable to open file %s to write", m_statsFileName.str()));
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "---------------------------------------------------\n");
|
||||
// Time
|
||||
struct tm *newTime;
|
||||
time_t aclock;
|
||||
time( &aclock );
|
||||
newTime = localtime( &aclock );
|
||||
fprintf(f, "Date:\t%s",asctime(newTime) );
|
||||
|
||||
// Map
|
||||
fprintf(f, "Map:\t%s\n", TheGlobalData->m_mapName.str());
|
||||
|
||||
// Side
|
||||
fprintf(f, "Side:\t%s\n", ThePlayerList->getLocalPlayer()->getSide().str());
|
||||
fprintf(f, "---------------------------------------------------\n\n");
|
||||
|
||||
fprintf(f, "Time*\tBC\tMC\tAC\tSMC\tST*\tOC\t$$$\t#PU\t#AIU\n");
|
||||
collectUnitCountStats();
|
||||
Money *m = ThePlayerList->getLocalPlayer()->getMoney();
|
||||
fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 0, m_buildCommands, m_moveCommands, m_attackCommands,
|
||||
m_scrollMapCommands, 0 ,/*other commands*/0, m->countMoney(),
|
||||
m_playerUnits, m_AIUnits );
|
||||
// initial stats
|
||||
// we don't want a file pointer open for seconds on end... we'll open it each time.
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// Write out the stats
|
||||
//=============================================================================
|
||||
void StatsCollector::writeStatInfo()
|
||||
{
|
||||
//open the file
|
||||
FILE *f = fopen(m_statsFileName.str(), "a");
|
||||
if(!f)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(f, ("Unable to open file %s to write", m_statsFileName.str()));
|
||||
return;
|
||||
}
|
||||
Money *m = ThePlayerList->getLocalPlayer()->getMoney();
|
||||
|
||||
fprintf(f, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", m_timeCount, m_buildCommands, m_moveCommands, m_attackCommands,
|
||||
m_scrollMapCommands, m_scrollTime / LOGICFRAMES_PER_SECOND, /*other commands*/0,m->countMoney() ,
|
||||
m_playerUnits, m_AIUnits );
|
||||
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
235
Generals/Code/GameEngine/Source/Common/System/ArchiveFile.cpp
Normal file
235
Generals/Code/GameEngine/Source/Common/System/ArchiveFile.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////ArchiveFile.cpp ///////////////////////
|
||||
// Bryan Cleveland, August 2002
|
||||
////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h"
|
||||
|
||||
#include "Common/ArchiveFile.h"
|
||||
#include "Common/ArchiveFileSystem.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/PerfTimer.h"
|
||||
|
||||
|
||||
// checks to see if str matches searchString. Search string is done in the
|
||||
// using * and ? as wildcards. * is used to denote any number of characters,
|
||||
// and ? is used to denote a single wildcard character.
|
||||
static Bool SearchStringMatches(AsciiString str, AsciiString searchString)
|
||||
{
|
||||
if (str.getLength() == 0) {
|
||||
if (searchString.getLength() == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
if (searchString.getLength() == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const char *c1 = str.str();
|
||||
const char *c2 = searchString.str();
|
||||
|
||||
while ((*c1 == *c2) || (*c2 == '?') || (*c2 == '*')) {
|
||||
if ((*c1 == *c2) || (*c2 == '?')) {
|
||||
++c1;
|
||||
++c2;
|
||||
} else if (*c2 == '*') {
|
||||
++c2;
|
||||
if (*c2 == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
while (*c1 != 0) {
|
||||
if (SearchStringMatches(AsciiString(c1), AsciiString(c2))) {
|
||||
return TRUE;
|
||||
}
|
||||
++c1;
|
||||
}
|
||||
}
|
||||
if (*c1 == 0) {
|
||||
if (*c2 == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
if (*c2 == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ArchiveFile::~ArchiveFile()
|
||||
{
|
||||
if (m_file != NULL) {
|
||||
m_file->close();
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ArchiveFile::ArchiveFile()
|
||||
{
|
||||
m_rootDirectory.clear();
|
||||
}
|
||||
|
||||
void ArchiveFile::addFile(const AsciiString& path, const ArchivedFileInfo *fileInfo)
|
||||
{
|
||||
AsciiString temp;
|
||||
temp = path;
|
||||
temp.toLower();
|
||||
AsciiString token;
|
||||
AsciiString debugpath;
|
||||
|
||||
DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
|
||||
|
||||
temp.nextToken(&token, "\\/");
|
||||
|
||||
while (token.getLength() > 0) {
|
||||
if (dirInfo->m_directories.find(token) == dirInfo->m_directories.end())
|
||||
{
|
||||
dirInfo->m_directories[token].clear();
|
||||
dirInfo->m_directories[token].m_directoryName = token;
|
||||
}
|
||||
|
||||
debugpath.concat(token);
|
||||
debugpath.concat('\\');
|
||||
dirInfo = &(dirInfo->m_directories[token]);
|
||||
temp.nextToken(&token, "\\/");
|
||||
}
|
||||
|
||||
dirInfo->m_files[fileInfo->m_filename] = *fileInfo;
|
||||
//path.concat(fileInfo->m_filename);
|
||||
}
|
||||
|
||||
void ArchiveFile::getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
|
||||
{
|
||||
|
||||
AsciiString searchDir;
|
||||
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
|
||||
|
||||
searchDir = originalDirectory;
|
||||
searchDir.toLower();
|
||||
AsciiString token;
|
||||
|
||||
searchDir.nextToken(&token, "\\/");
|
||||
|
||||
while (token.getLength() > 0) {
|
||||
|
||||
DetailedArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
|
||||
if (it != dirInfo->m_directories.end())
|
||||
{
|
||||
dirInfo = &it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the directory doesn't exist, then there aren't any files to be had.
|
||||
return;
|
||||
}
|
||||
|
||||
searchDir.nextToken(&token, "\\/");
|
||||
}
|
||||
|
||||
getFileListInDirectory(dirInfo, originalDirectory, searchName, filenameList, searchSubdirectories);
|
||||
}
|
||||
|
||||
void ArchiveFile::getFileListInDirectory(const DetailedArchivedDirectoryInfo *dirInfo, const AsciiString& currentDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
|
||||
{
|
||||
DetailedArchivedDirectoryInfoMap::const_iterator diriter = dirInfo->m_directories.begin();
|
||||
while (diriter != dirInfo->m_directories.end()) {
|
||||
const DetailedArchivedDirectoryInfo *tempDirInfo = &(diriter->second);
|
||||
AsciiString tempdirname;
|
||||
tempdirname = currentDirectory;
|
||||
if ((tempdirname.getLength() > 0) && (!tempdirname.endsWith("\\"))) {
|
||||
tempdirname.concat('\\');
|
||||
}
|
||||
tempdirname.concat(tempDirInfo->m_directoryName);
|
||||
getFileListInDirectory(tempDirInfo, tempdirname, searchName, filenameList, searchSubdirectories);
|
||||
diriter++;
|
||||
}
|
||||
|
||||
ArchivedFileInfoMap::const_iterator fileiter = dirInfo->m_files.begin();
|
||||
while (fileiter != dirInfo->m_files.end()) {
|
||||
if (SearchStringMatches(fileiter->second.m_filename, searchName)) {
|
||||
AsciiString tempfilename;
|
||||
tempfilename = currentDirectory;
|
||||
if ((tempfilename.getLength() > 0) && (!tempfilename.endsWith("\\"))) {
|
||||
tempfilename.concat('\\');
|
||||
}
|
||||
tempfilename.concat(fileiter->second.m_filename);
|
||||
if (filenameList.find(tempfilename) == filenameList.end()) {
|
||||
// only insert into the list if its not already in there.
|
||||
filenameList.insert(tempfilename);
|
||||
}
|
||||
}
|
||||
fileiter++;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFile::attachFile(File *file)
|
||||
{
|
||||
if (m_file != NULL) {
|
||||
m_file->close();
|
||||
m_file = NULL;
|
||||
}
|
||||
m_file = file;
|
||||
}
|
||||
|
||||
const ArchivedFileInfo * ArchiveFile::getArchivedFileInfo(const AsciiString& filename) const
|
||||
{
|
||||
AsciiString path;
|
||||
path = filename;
|
||||
path.toLower();
|
||||
AsciiString token;
|
||||
|
||||
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
|
||||
|
||||
path.nextToken(&token, "\\/");
|
||||
|
||||
while ((token.find('.') == NULL) || (path.find('.') != NULL)) {
|
||||
|
||||
DetailedArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
|
||||
if (it != dirInfo->m_directories.end())
|
||||
{
|
||||
dirInfo = &it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path.nextToken(&token, "\\/");
|
||||
}
|
||||
|
||||
ArchivedFileInfoMap::const_iterator it = dirInfo->m_files.find(token);
|
||||
if (it != dirInfo->m_files.end())
|
||||
{
|
||||
return &it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: Generals
|
||||
//
|
||||
// Module: Game Engine Common
|
||||
//
|
||||
// File name: ArchiveFileSystem.cpp
|
||||
//
|
||||
// Created: 11/26/01 TR
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "Common/ArchiveFile.h"
|
||||
#include "Common/ArchiveFileSystem.h"
|
||||
#include "Common/AsciiString.h"
|
||||
#include "Common/PerfTimer.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ArchiveFileSystem *TheArchiveFileSystem = NULL;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------
|
||||
// ArchivedFileInfo
|
||||
//------------------------------------------------------
|
||||
ArchiveFileSystem::ArchiveFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
ArchiveFileSystem::~ArchiveFileSystem()
|
||||
{
|
||||
ArchiveFileMap::iterator iter = m_archiveFileMap.begin();
|
||||
while (iter != m_archiveFileMap.end()) {
|
||||
ArchiveFile *file = iter->second;
|
||||
if (file != NULL) {
|
||||
delete file;
|
||||
file = NULL;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFileSystem::loadIntoDirectoryTree(const ArchiveFile *archiveFile, const AsciiString& archiveFilename, Bool overwrite)
|
||||
{
|
||||
|
||||
FilenameList filenameList;
|
||||
|
||||
archiveFile->getFileListInDirectory(AsciiString(""), AsciiString(""), AsciiString("*"), filenameList, TRUE);
|
||||
|
||||
FilenameListIter it = filenameList.begin();
|
||||
|
||||
while (it != filenameList.end()) {
|
||||
// add this filename to the directory tree.
|
||||
AsciiString path = *it;
|
||||
path.toLower();
|
||||
AsciiString token;
|
||||
AsciiString debugpath;
|
||||
|
||||
ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
|
||||
|
||||
Bool infoInPath;
|
||||
infoInPath = path.nextToken(&token, "\\/");
|
||||
|
||||
while (infoInPath && (!token.find('.') || path.find('.'))) {
|
||||
ArchivedDirectoryInfoMap::iterator tempiter = dirInfo->m_directories.find(token);
|
||||
if (tempiter == dirInfo->m_directories.end())
|
||||
{
|
||||
dirInfo->m_directories[token].clear();
|
||||
dirInfo->m_directories[token].m_directoryName = token;
|
||||
}
|
||||
|
||||
dirInfo = &(dirInfo->m_directories[token]);
|
||||
debugpath.concat(token);
|
||||
debugpath.concat('\\');
|
||||
infoInPath = path.nextToken(&token, "\\/");
|
||||
}
|
||||
|
||||
// token is the filename, and dirInfo is the directory that this file is in.
|
||||
if (dirInfo->m_files.find(token) == dirInfo->m_files.end() || overwrite) {
|
||||
AsciiString path2;
|
||||
path2 = debugpath;
|
||||
path2.concat(token);
|
||||
// DEBUG_LOG(("ArchiveFileSystem::loadIntoDirectoryTree - adding file %s, archived in %s\n", path2.str(), archiveFilename.str()));
|
||||
dirInfo->m_files[token] = archiveFilename;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFileSystem::loadMods() {
|
||||
if (TheGlobalData->m_modBIG.isNotEmpty())
|
||||
{
|
||||
ArchiveFile *archiveFile = openArchiveFile(TheGlobalData->m_modBIG.str());
|
||||
|
||||
if (archiveFile != NULL) {
|
||||
DEBUG_LOG(("ArchiveFileSystem::loadMods - loading %s into the directory tree.\n", TheGlobalData->m_modBIG.str()));
|
||||
loadIntoDirectoryTree(archiveFile, TheGlobalData->m_modBIG, TRUE);
|
||||
m_archiveFileMap[TheGlobalData->m_modBIG] = archiveFile;
|
||||
DEBUG_LOG(("ArchiveFileSystem::loadMods - %s inserted into the archive file map.\n", TheGlobalData->m_modBIG.str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("ArchiveFileSystem::loadMods - could not openArchiveFile(%s)\n", TheGlobalData->m_modBIG.str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (TheGlobalData->m_modDir.isNotEmpty())
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
Bool ret =
|
||||
#endif
|
||||
loadBigFilesFromDirectory(TheGlobalData->m_modDir, "*.big", TRUE);
|
||||
DEBUG_ASSERTLOG(ret, ("loadBigFilesFromDirectory(%s) returned FALSE!\n", TheGlobalData->m_modDir.str()));
|
||||
}
|
||||
}
|
||||
|
||||
Bool ArchiveFileSystem::doesFileExist(const Char *filename) const
|
||||
{
|
||||
AsciiString path = filename;
|
||||
path.toLower();
|
||||
AsciiString token;
|
||||
|
||||
const ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
|
||||
|
||||
path.nextToken(&token, "\\/");
|
||||
|
||||
while (!token.find('.') || path.find('.'))
|
||||
{
|
||||
ArchivedDirectoryInfoMap::const_iterator tempiter = dirInfo->m_directories.find(token);
|
||||
if (tempiter != dirInfo->m_directories.end())
|
||||
{
|
||||
dirInfo = &tempiter->second;
|
||||
path.nextToken(&token, "\\/");
|
||||
}
|
||||
else
|
||||
{
|
||||
// the directory doesn't exist, so return false
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// token is the filename, and dirInfo is the directory that this file is in.
|
||||
if (dirInfo->m_files.find(token) == dirInfo->m_files.end()) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
File * ArchiveFileSystem::openFile(const Char *filename, Int access /* = 0 */)
|
||||
{
|
||||
AsciiString archiveFilename;
|
||||
archiveFilename = getArchiveFilenameForFile(AsciiString(filename));
|
||||
|
||||
if (archiveFilename.getLength() == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_archiveFileMap[archiveFilename]->openFile(filename, access);
|
||||
}
|
||||
|
||||
Bool ArchiveFileSystem::getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const
|
||||
{
|
||||
if (fileInfo == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (filename.getLength() <= 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
AsciiString archiveFilename = getArchiveFilenameForFile(filename);
|
||||
ArchiveFileMap::const_iterator it = m_archiveFileMap.find(archiveFilename);
|
||||
if (it != m_archiveFileMap.end())
|
||||
{
|
||||
return it->second->getFileInfo(filename, fileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
AsciiString ArchiveFileSystem::getArchiveFilenameForFile(const AsciiString& filename) const
|
||||
{
|
||||
AsciiString path;
|
||||
path = filename;
|
||||
path.toLower();
|
||||
AsciiString token;
|
||||
AsciiString debugpath;
|
||||
|
||||
const ArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
|
||||
|
||||
path.nextToken(&token, "\\/");
|
||||
|
||||
while (!token.find('.') || path.find('.')) {
|
||||
|
||||
ArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
|
||||
if (it != dirInfo->m_directories.end())
|
||||
{
|
||||
dirInfo = &it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the directory doesn't exist, so return NULL
|
||||
|
||||
// dump the directories;
|
||||
//DEBUG_LOG(("directory %s not found in %s in archive file system\n", token.str(), debugpath.str()));
|
||||
//DEBUG_LOG(("directories in %s in archive file system are:\n", debugpath.str()));
|
||||
//ArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.begin();
|
||||
//while (it != dirInfo->m_directories.end()) {
|
||||
// DEBUG_LOG(("\t%s\n", it->second.m_directoryName.str()));
|
||||
// it++;
|
||||
//}
|
||||
//DEBUG_LOG(("end of directory list.\n"));
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
debugpath.concat(token);
|
||||
debugpath.concat('\\');
|
||||
|
||||
path.nextToken(&token, "\\/");
|
||||
}
|
||||
|
||||
ArchivedFileLocationMap::const_iterator it = dirInfo->m_files.find(token);
|
||||
if (it != dirInfo->m_files.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ArchiveFileSystem::getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
|
||||
{
|
||||
ArchiveFileMap::const_iterator it = m_archiveFileMap.begin();
|
||||
while (it != m_archiveFileMap.end()) {
|
||||
it->second->getFileListInDirectory(currentDirectory, originalDirectory, searchName, filenameList, searchSubdirectories);
|
||||
it++;
|
||||
}
|
||||
}
|
||||
483
Generals/Code/GameEngine/Source/Common/System/AsciiString.cpp
Normal file
483
Generals/Code/GameEngine/Source/Common/System/AsciiString.cpp
Normal file
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: AsciiString.cpp
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: AsciiString.cpp
|
||||
//
|
||||
// Created: Steven Johnson, October 2001
|
||||
//
|
||||
// Desc: General-purpose string classes
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/CriticalSection.h"
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
|
||||
/*static*/ AsciiString AsciiString::TheEmptyString;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline char* skipSeps(char* p, const char* seps)
|
||||
{
|
||||
while (*p && strchr(seps, *p) != NULL)
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline char* skipNonSeps(char* p, const char* seps)
|
||||
{
|
||||
while (*p && strchr(seps, *p) == NULL)
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline char* skipWhitespace(char* p)
|
||||
{
|
||||
while (*p && isspace(*p))
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
inline char* skipNonWhitespace(char* p)
|
||||
{
|
||||
while (*p && !isspace(*p))
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
AsciiString::AsciiString(const AsciiString& stringSrc) : m_data(stringSrc.m_data)
|
||||
{
|
||||
ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
|
||||
if (m_data)
|
||||
++m_data->m_refCount;
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
#ifdef _DEBUG
|
||||
void AsciiString::validate() const
|
||||
{
|
||||
if (!m_data) return;
|
||||
DEBUG_ASSERTCRASH(m_data->m_refCount > 0, ("m_refCount is zero"));
|
||||
DEBUG_ASSERTCRASH(m_data->m_refCount < 32000, ("m_refCount is suspiciously large"));
|
||||
DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated > 0, ("m_numCharsAllocated is zero"));
|
||||
// DEBUG_ASSERTCRASH(m_data->m_numCharsAllocated < 1024, ("m_numCharsAllocated suspiciously large"));
|
||||
DEBUG_ASSERTCRASH(strlen(m_data->peek())+1 <= m_data->m_numCharsAllocated,("str is too long (%d) for storage",strlen(m_data->peek())+1));
|
||||
}
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::debugIgnoreLeaks()
|
||||
{
|
||||
#ifdef MEMORYPOOL_DEBUG
|
||||
if (m_data)
|
||||
{
|
||||
TheDynamicMemoryAllocator->debugIgnoreLeaksForThisBlock(m_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("cannot ignore the leak (no data)\n"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::ensureUniqueBufferOfSize(int numCharsNeeded, Bool preserveData, const char* strToCopy, const char* strToCat)
|
||||
{
|
||||
validate();
|
||||
|
||||
if (m_data &&
|
||||
m_data->m_refCount == 1 &&
|
||||
m_data->m_numCharsAllocated >= numCharsNeeded)
|
||||
{
|
||||
// no buffer manhandling is needed (it's already large enough, and unique to us)
|
||||
if (strToCopy)
|
||||
strcpy(m_data->peek(), strToCopy);
|
||||
if (strToCat)
|
||||
strcat(m_data->peek(), strToCat);
|
||||
return;
|
||||
}
|
||||
|
||||
int minBytes = sizeof(AsciiStringData) + numCharsNeeded*sizeof(char);
|
||||
if (minBytes > MAX_LEN)
|
||||
throw ERROR_OUT_OF_MEMORY;
|
||||
|
||||
int actualBytes = TheDynamicMemoryAllocator->getActualAllocationSize(minBytes);
|
||||
AsciiStringData* newData = (AsciiStringData*)TheDynamicMemoryAllocator->allocateBytesDoNotZero(actualBytes, "STR_AsciiString::ensureUniqueBufferOfSize");
|
||||
newData->m_refCount = 1;
|
||||
newData->m_numCharsAllocated = (actualBytes - sizeof(AsciiStringData))/sizeof(char);
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
newData->m_debugptr = newData->peek(); // just makes it easier to read in the debugger
|
||||
#endif
|
||||
|
||||
if (m_data && preserveData)
|
||||
strcpy(newData->peek(), m_data->peek());
|
||||
else
|
||||
newData->peek()[0] = 0;
|
||||
|
||||
// do these BEFORE releasing the old buffer, so that self-copies
|
||||
// or self-cats will work correctly.
|
||||
if (strToCopy)
|
||||
strcpy(newData->peek(), strToCopy);
|
||||
if (strToCat)
|
||||
strcat(newData->peek(), strToCat);
|
||||
|
||||
releaseBuffer();
|
||||
m_data = newData;
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::releaseBuffer()
|
||||
{
|
||||
ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
|
||||
|
||||
validate();
|
||||
if (m_data)
|
||||
{
|
||||
if (--m_data->m_refCount == 0)
|
||||
{
|
||||
TheDynamicMemoryAllocator->freeBytes(m_data);
|
||||
}
|
||||
m_data = 0;
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
AsciiString::AsciiString(const char* s) : m_data(0)
|
||||
{
|
||||
//DEBUG_ASSERTCRASH(isMemoryManagerOfficiallyInited(), ("Initializing AsciiStrings prior to main (ie, as static vars) can cause memory leak reporting problems. Are you sure you want to do this?\n"));
|
||||
int len = (s)?strlen(s):0;
|
||||
if (len)
|
||||
{
|
||||
ensureUniqueBufferOfSize(len + 1, false, s, NULL);
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::set(const AsciiString& stringSrc)
|
||||
{
|
||||
ScopedCriticalSection scopedCriticalSection(TheAsciiStringCriticalSection);
|
||||
|
||||
validate();
|
||||
if (&stringSrc != this)
|
||||
{
|
||||
releaseBuffer();
|
||||
m_data = stringSrc.m_data;
|
||||
if (m_data)
|
||||
++m_data->m_refCount;
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::set(const char* s)
|
||||
{
|
||||
validate();
|
||||
if (!m_data || s != peek())
|
||||
{
|
||||
int len = s ? strlen(s) : 0;
|
||||
if (len)
|
||||
{
|
||||
ensureUniqueBufferOfSize(len + 1, false, s, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
releaseBuffer();
|
||||
}
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
char* AsciiString::getBufferForRead(Int len)
|
||||
{
|
||||
validate();
|
||||
DEBUG_ASSERTCRASH(len>0, ("No need to allocate 0 len strings."));
|
||||
ensureUniqueBufferOfSize(len + 1, false, NULL, NULL);
|
||||
validate();
|
||||
return peek();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::translate(const UnicodeString& stringSrc)
|
||||
{
|
||||
validate();
|
||||
/// @todo srj put in a real translation here; this will only work for 7-bit ascii
|
||||
clear();
|
||||
Int len = stringSrc.getLength();
|
||||
for (Int i = 0; i < len; i++)
|
||||
concat((char)stringSrc.getCharAt(i));
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::concat(const char* s)
|
||||
{
|
||||
validate();
|
||||
int addlen = strlen(s);
|
||||
if (addlen == 0)
|
||||
return; // my, that was easy
|
||||
|
||||
if (m_data)
|
||||
{
|
||||
ensureUniqueBufferOfSize(getLength() + addlen + 1, true, NULL, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
set(s);
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::trim()
|
||||
{
|
||||
validate();
|
||||
|
||||
if (m_data)
|
||||
{
|
||||
char *c = peek();
|
||||
|
||||
// Strip leading white space from the string.
|
||||
c = skipWhitespace(c);
|
||||
if (c != peek())
|
||||
{
|
||||
set(c);
|
||||
}
|
||||
|
||||
if (m_data) // another check, because the previous set() could erase m_data
|
||||
{
|
||||
// Clip trailing white space from the string.
|
||||
int len = strlen(peek());
|
||||
for (int index = len-1; index >= 0; index--)
|
||||
{
|
||||
if (isspace(getCharAt(index)))
|
||||
{
|
||||
removeLastChar();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::toLower()
|
||||
{
|
||||
validate();
|
||||
if (m_data)
|
||||
{
|
||||
char buf[MAX_FORMAT_BUF_LEN];
|
||||
strcpy(buf, peek());
|
||||
|
||||
char *c = buf;
|
||||
while (c && *c)
|
||||
{
|
||||
*c = tolower(*c);
|
||||
c++;
|
||||
}
|
||||
set(buf);
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::removeLastChar()
|
||||
{
|
||||
validate();
|
||||
if (m_data)
|
||||
{
|
||||
int len = strlen(peek());
|
||||
if (len > 0)
|
||||
{
|
||||
ensureUniqueBufferOfSize(len+1, true, NULL, NULL);
|
||||
peek()[len - 1] = 0;
|
||||
}
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::format(AsciiString format, ...)
|
||||
{
|
||||
validate();
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
format_va(format, args);
|
||||
va_end(args);
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::format(const char* format, ...)
|
||||
{
|
||||
validate();
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
format_va(format, args);
|
||||
va_end(args);
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::format_va(const AsciiString& format, va_list args)
|
||||
{
|
||||
validate();
|
||||
char buf[MAX_FORMAT_BUF_LEN];
|
||||
if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format.str(), args) < 0)
|
||||
throw ERROR_OUT_OF_MEMORY;
|
||||
set(buf);
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
void AsciiString::format_va(const char* format, va_list args)
|
||||
{
|
||||
validate();
|
||||
char buf[MAX_FORMAT_BUF_LEN];
|
||||
if (_vsnprintf(buf, sizeof(buf)/sizeof(char)-1, format, args) < 0)
|
||||
throw ERROR_OUT_OF_MEMORY;
|
||||
set(buf);
|
||||
validate();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Bool AsciiString::startsWith(const char* p) const
|
||||
{
|
||||
if (*p == 0)
|
||||
return true; // everything starts with the empty string
|
||||
|
||||
int lenThis = getLength();
|
||||
int lenThat = strlen(p);
|
||||
if (lenThis < lenThat)
|
||||
return false; // that must be smaller than this
|
||||
|
||||
return strncmp(peek(), p, lenThat) == 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Bool AsciiString::startsWithNoCase(const char* p) const
|
||||
{
|
||||
if (*p == 0)
|
||||
return true; // everything starts with the empty string
|
||||
|
||||
int lenThis = getLength();
|
||||
int lenThat = strlen(p);
|
||||
if (lenThis < lenThat)
|
||||
return false; // that must be smaller than this
|
||||
|
||||
return strnicmp(peek(), p, lenThat) == 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Bool AsciiString::endsWith(const char* p) const
|
||||
{
|
||||
if (*p == 0)
|
||||
return true; // everything ends with the empty string
|
||||
|
||||
int lenThis = getLength();
|
||||
int lenThat = strlen(p);
|
||||
if (lenThis < lenThat)
|
||||
return false; // that must be smaller than this
|
||||
|
||||
return strncmp(peek() + lenThis - lenThat, p, lenThat) == 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
Bool AsciiString::endsWithNoCase(const char* p) const
|
||||
{
|
||||
if (*p == 0)
|
||||
return true; // everything ends with the empty string
|
||||
|
||||
int lenThis = getLength();
|
||||
int lenThat = strlen(p);
|
||||
if (lenThis < lenThat)
|
||||
return false; // that must be smaller than this
|
||||
|
||||
return strnicmp(peek() + lenThis - lenThat, p, lenThat) == 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool AsciiString::isNone() const
|
||||
{
|
||||
return m_data && stricmp(peek(), "None") == 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Bool AsciiString::nextToken(AsciiString* tok, const char* seps)
|
||||
{
|
||||
if (this->isEmpty() || tok == this)
|
||||
return false;
|
||||
|
||||
if (seps == NULL)
|
||||
seps = " \n\r\t";
|
||||
|
||||
char* start = skipSeps(peek(), seps);
|
||||
char* end = skipNonSeps(start, seps);
|
||||
|
||||
if (end > start)
|
||||
{
|
||||
Int len = end - start;
|
||||
char* tmp = tok->getBufferForRead(len + 1);
|
||||
memcpy(tmp, start, len);
|
||||
tmp[len] = 0;
|
||||
|
||||
this->set(end);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->clear();
|
||||
tok->clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
1579
Generals/Code/GameEngine/Source/Common/System/BuildAssistant.cpp
Normal file
1579
Generals/Code/GameEngine/Source/Common/System/BuildAssistant.cpp
Normal file
File diff suppressed because it is too large
Load Diff
297
Generals/Code/GameEngine/Source/Common/System/CDManager.cpp
Normal file
297
Generals/Code/GameEngine/Source/Common/System/CDManager.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: Generals
|
||||
//
|
||||
// Module: Game Engine Common
|
||||
//
|
||||
// File name: CDManager.cpp
|
||||
//
|
||||
// Created: 11/26/01 TR
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/CDManager.h"
|
||||
#include "GameLogic/GameLogic.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
CDManagerInterface* TheCDManager = NULL;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//============================================================================
|
||||
// CDDrive::CDDrive
|
||||
//============================================================================
|
||||
|
||||
CDDrive::CDDrive()
|
||||
: m_disk(CD::UNKNOWN_DISK)
|
||||
{
|
||||
m_diskName.clear();
|
||||
m_drivePath.clear();
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDDrive::~CDDrive
|
||||
//============================================================================
|
||||
|
||||
CDDrive::~CDDrive()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDDrive::getPath
|
||||
//============================================================================
|
||||
|
||||
AsciiString CDDrive::getPath( void )
|
||||
{
|
||||
return m_drivePath;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDDrive::getDiskName
|
||||
//============================================================================
|
||||
|
||||
AsciiString CDDrive::getDiskName( void )
|
||||
{
|
||||
return m_diskName;
|
||||
}
|
||||
|
||||
void CDDrive::refreshInfo( void )
|
||||
{
|
||||
// map disk names to disk ID
|
||||
m_disk = CD::UNKNOWN_DISK;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDDrive::getDisk
|
||||
//============================================================================
|
||||
|
||||
CD::Disk CDDrive::getDisk( void )
|
||||
{
|
||||
return m_disk;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDDrive::setPath
|
||||
//============================================================================
|
||||
|
||||
void CDDrive::setPath( const Char *path )
|
||||
{
|
||||
m_drivePath = path;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::CDManager
|
||||
//============================================================================
|
||||
|
||||
CDManager::CDManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::~CDManager
|
||||
//============================================================================
|
||||
|
||||
CDManager::~CDManager()
|
||||
{
|
||||
destroyAllDrives();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::init
|
||||
//============================================================================
|
||||
|
||||
void CDManager::init( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::update
|
||||
//============================================================================
|
||||
|
||||
void CDManager::update( void )
|
||||
{
|
||||
// Every so often, check to make sure the CD is still in the drive
|
||||
if ((TheGameLogic->getFrame() % 300) == 299) {
|
||||
refreshDrives();
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::reset
|
||||
//============================================================================
|
||||
|
||||
void CDManager::reset( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::driveCount
|
||||
//============================================================================
|
||||
|
||||
Int CDManager::driveCount( void )
|
||||
{
|
||||
return m_drives.nodeCount();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::getDrive
|
||||
//============================================================================
|
||||
|
||||
CDDriveInterface* CDManager::getDrive( Int index )
|
||||
{
|
||||
CDDriveInterface *cd = NULL;
|
||||
LListNode *node = m_drives.getNode( index );
|
||||
|
||||
if ( node )
|
||||
{
|
||||
cd = (CDDriveInterface*) node->item();
|
||||
}
|
||||
|
||||
return cd;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::newDrive
|
||||
//============================================================================
|
||||
|
||||
CDDriveInterface* CDManager::newDrive( const Char *path )
|
||||
{
|
||||
CDDrive *drive= (CDDrive*) createDrive();
|
||||
|
||||
if ( drive )
|
||||
{
|
||||
drive->setPath( path );
|
||||
|
||||
drive->m_node.setItem( drive );
|
||||
m_drives.add( &drive->m_node );
|
||||
}
|
||||
return drive;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// CDManager::refreshDrives
|
||||
//============================================================================
|
||||
|
||||
void CDManager::refreshDrives( void )
|
||||
{
|
||||
LListNode *node = m_drives.firstNode();
|
||||
|
||||
while ( node )
|
||||
{
|
||||
CDDriveInterface *drive = (CDDriveInterface *) node->item();
|
||||
if ( drive )
|
||||
{
|
||||
drive->refreshInfo();
|
||||
}
|
||||
|
||||
node = node->next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
// CDManager::destroyAllDrives
|
||||
//============================================================================
|
||||
|
||||
void CDManager::destroyAllDrives( void )
|
||||
{
|
||||
LListNode *node;
|
||||
|
||||
while ( (node = m_drives.firstNode() ) != NULL )
|
||||
{
|
||||
node->remove();
|
||||
CDDriveInterface *drive = (CDDriveInterface *) node->item();
|
||||
if ( drive )
|
||||
{
|
||||
delete drive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
204
Generals/Code/GameEngine/Source/Common/System/Compression.cpp
Normal file
204
Generals/Code/GameEngine/Source/Common/System/Compression.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Compression.cpp /////////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "Compression.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
///// Performance Testing ///////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//#define TEST_COMPRESSION
|
||||
#ifdef TEST_COMPRESSION
|
||||
|
||||
#include "GameClient/MapUtil.h"
|
||||
#include "Common/FileSystem.h"
|
||||
#include "Common/File.h"
|
||||
|
||||
#include "Common/PerfTimer.h"
|
||||
enum { NUM_TIMES = 1 };
|
||||
|
||||
struct CompData
|
||||
{
|
||||
public:
|
||||
Int origSize;
|
||||
Int compressedSize[COMPRESSION_MAX+1];
|
||||
};
|
||||
|
||||
#define TEST_COMPRESSION_MIN COMPRESSION_BTREE
|
||||
#define TEST_COMPRESSION_MAX COMPRESSION_MAX
|
||||
|
||||
void DoCompressTest( void )
|
||||
{
|
||||
|
||||
Int i;
|
||||
|
||||
/*
|
||||
PerfGather *s_compressGathers[TEST_COMPRESSION_MAX+1];
|
||||
PerfGather *s_decompressGathers[TEST_COMPRESSION_MAX+1];
|
||||
for (i = TEST_COMPRESSION_MIN; i < TEST_COMPRESSION_MAX+1; ++i)
|
||||
{
|
||||
s_compressGathers[i] = new PerfGather(CompressionManager::getCompressionNameByType((CompressionType)i));
|
||||
s_decompressGathers[i] = new PerfGather(CompressionManager::getDecompressionNameByType((CompressionType)i));
|
||||
}
|
||||
*/
|
||||
|
||||
std::map<AsciiString, CompData> s_sizes;
|
||||
|
||||
std::map<AsciiString, MapMetaData>::const_iterator it = TheMapCache->find("userdata\\maps\\_usa01\\_usa01.map");
|
||||
if (it != TheMapCache->end())
|
||||
{
|
||||
//if (it->second.m_isOfficial)
|
||||
//{
|
||||
//++it;
|
||||
//continue;
|
||||
//}
|
||||
//static Int count = 0;
|
||||
//if (count++ > 2)
|
||||
//break;
|
||||
File *f = TheFileSystem->openFile(it->first.str());
|
||||
if (f)
|
||||
{
|
||||
DEBUG_LOG(("***************************\nTesting '%s'\n\n", it->first.str()));
|
||||
Int origSize = f->size();
|
||||
UnsignedByte *buf = (UnsignedByte *)f->readEntireAndClose();
|
||||
UnsignedByte *uncompressedBuf = NEW UnsignedByte[origSize];
|
||||
|
||||
CompData d = s_sizes[it->first];
|
||||
d.origSize = origSize;
|
||||
d.compressedSize[COMPRESSION_NONE] = origSize;
|
||||
|
||||
for (i=TEST_COMPRESSION_MIN; i<=TEST_COMPRESSION_MAX; ++i)
|
||||
{
|
||||
DEBUG_LOG(("=================================================\n"));
|
||||
DEBUG_LOG(("Compression Test %d\n", i));
|
||||
|
||||
Int maxCompressedSize = CompressionManager::getMaxCompressedSize( origSize, (CompressionType)i );
|
||||
DEBUG_LOG(("Orig size is %d, max compressed size is %d bytes\n", origSize, maxCompressedSize));
|
||||
|
||||
UnsignedByte *compressedBuf = NEW UnsignedByte[maxCompressedSize];
|
||||
memset(compressedBuf, 0, maxCompressedSize);
|
||||
memset(uncompressedBuf, 0, origSize);
|
||||
|
||||
Int compressedLen, decompressedLen;
|
||||
|
||||
for (Int j=0; j < NUM_TIMES; ++j)
|
||||
{
|
||||
//s_compressGathers[i]->startTimer();
|
||||
compressedLen = CompressionManager::compressData((CompressionType)i, buf, origSize, compressedBuf, maxCompressedSize);
|
||||
//s_compressGathers[i]->stopTimer();
|
||||
//s_decompressGathers[i]->startTimer();
|
||||
decompressedLen = CompressionManager::decompressData(compressedBuf, compressedLen, uncompressedBuf, origSize);
|
||||
//s_decompressGathers[i]->stopTimer();
|
||||
}
|
||||
d.compressedSize[i] = compressedLen;
|
||||
DEBUG_LOG(("Compressed len is %d (%g%% of original size)\n", compressedLen, (double)compressedLen/(double)origSize*100.0));
|
||||
DEBUG_ASSERTCRASH(compressedLen, ("Failed to compress\n"));
|
||||
DEBUG_LOG(("Decompressed len is %d (%g%% of original size)\n", decompressedLen, (double)decompressedLen/(double)origSize*100.0));
|
||||
|
||||
DEBUG_ASSERTCRASH(decompressedLen == origSize, ("orig size does not match compressed+uncompressed output\n"));
|
||||
if (decompressedLen == origSize)
|
||||
{
|
||||
Int ret = memcmp(buf, uncompressedBuf, origSize);
|
||||
if (ret != 0)
|
||||
{
|
||||
DEBUG_CRASH(("orig buffer does not match compressed+uncompressed output - ret was %d\n", ret));
|
||||
}
|
||||
}
|
||||
|
||||
delete compressedBuf;
|
||||
compressedBuf = NULL;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("d = %d -> %d\n", d.origSize, d.compressedSize[i]));
|
||||
s_sizes[it->first] = d;
|
||||
DEBUG_LOG(("s_sizes[%s] = %d -> %d\n", it->first.str(), s_sizes[it->first].origSize, s_sizes[it->first].compressedSize[i]));
|
||||
|
||||
delete[] buf;
|
||||
buf = NULL;
|
||||
|
||||
delete[] uncompressedBuf;
|
||||
uncompressedBuf = NULL;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
for (i=TEST_COMPRESSION_MIN; i<=TEST_COMPRESSION_MAX; ++i)
|
||||
{
|
||||
Real maxCompression = 1000.0f;
|
||||
Real minCompression = 0.0f;
|
||||
Int totalUncompressedBytes = 0;
|
||||
Int totalCompressedBytes = 0;
|
||||
for (std::map<AsciiString, CompData>::iterator cd = s_sizes.begin(); cd != s_sizes.end(); ++cd)
|
||||
{
|
||||
CompData d = cd->second;
|
||||
|
||||
Real ratio = d.compressedSize[i]/(Real)d.origSize;
|
||||
maxCompression = min(maxCompression, ratio);
|
||||
minCompression = max(minCompression, ratio);
|
||||
|
||||
totalUncompressedBytes += d.origSize;
|
||||
totalCompressedBytes += d.compressedSize[i];
|
||||
}
|
||||
DEBUG_LOG(("***************************************************\n"));
|
||||
DEBUG_LOG(("Compression method %s:\n", CompressionManager::getCompressionNameByType((CompressionType)i)));
|
||||
DEBUG_LOG(("%d bytes compressed to %d (%g%%)\n", totalUncompressedBytes, totalCompressedBytes,
|
||||
totalCompressedBytes/(Real)totalUncompressedBytes*100.0f));
|
||||
DEBUG_LOG(("Min ratio: %g%%, Max ratio: %g%%\n",
|
||||
minCompression*100.0f, maxCompression*100.0f));
|
||||
DEBUG_LOG(("\n"));
|
||||
}
|
||||
|
||||
/*
|
||||
PerfGather::dumpAll(10000);
|
||||
PerfGather::resetAll();
|
||||
CopyFile( "AAAPerfStats.csv", "AAACompressPerfStats.csv", FALSE );
|
||||
|
||||
for (i = TEST_COMPRESSION_MIN; i < TEST_COMPRESSION_MAX+1; ++i)
|
||||
{
|
||||
delete s_compressGathers[i];
|
||||
s_compressGathers[i] = NULL;
|
||||
|
||||
delete s_decompressGathers[i];
|
||||
s_decompressGathers[i] = NULL;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
#endif // TEST_COMPRESSION
|
||||
233
Generals/Code/GameEngine/Source/Common/System/CopyProtection.cpp
Normal file
233
Generals/Code/GameEngine/Source/Common/System/CopyProtection.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: CopyProtection.cpp //////////////////////////////////////////////////
|
||||
// Author: Matthew D. Campbell
|
||||
// Taken From: Denzil Long's code in Tiberian Sun, by way of Yuri's Revenge
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h"
|
||||
|
||||
#include "Common/CopyProtection.h"
|
||||
|
||||
#ifndef DO_COPY_PROTECTION
|
||||
//#pragma MESSAGE("*********************************************************")
|
||||
//#pragma MESSAGE("************* COPY PROTECTION IS DISABLED ***************")
|
||||
//#pragma MESSAGE("*********************************************************")
|
||||
#else
|
||||
|
||||
LPVOID CopyProtect::s_protectedData = NULL;
|
||||
static const char* const LAUNCHER_GUID =
|
||||
"150C6462-4E49-4ccf-B073-57579569D994"; // Generals Multiplayer Test Launcher GUID
|
||||
static const char* const protectGUID =
|
||||
"6096561D-8A70-48ed-9FF8-18552419E50D"; // Generals Multiplayer Test Protect GUID
|
||||
|
||||
/*
|
||||
static const char* const LAUNCHER_GUID =
|
||||
"FB327081-64F2-43d8-9B72-503C3B765134"; // Generals Launcher GUID
|
||||
static const char* const protectGUID =
|
||||
"7BEB9006-CC19-4aca-913A-C870A88DE01A"; // Generals Protect GUID
|
||||
*/
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
Bool skipProtection(void)
|
||||
{
|
||||
//return FALSE;
|
||||
if (FindWindow("Afx:400000:8:10011:0:fe8e0125", NULL) != NULL) // DevStudio
|
||||
{
|
||||
DEBUG_LOG(("DevStudio is running - skipping notifyLauncher()\n"));
|
||||
return TRUE;
|
||||
}
|
||||
//return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
Bool CopyProtect::isLauncherRunning(void)
|
||||
{
|
||||
DEBUG_LOG(("COPYPROTECTION - Checking if launcher is running\n"));
|
||||
|
||||
HANDLE launcherMutex = CreateMutex(NULL, FALSE, LAUNCHER_GUID);
|
||||
|
||||
Bool isRunning = (GetLastError() == ERROR_ALREADY_EXISTS);
|
||||
|
||||
if (launcherMutex != NULL)
|
||||
{
|
||||
CloseHandle(launcherMutex);
|
||||
}
|
||||
DEBUG_LOG(("result was %d\n", (int)isRunning));
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (skipProtection())
|
||||
{
|
||||
DEBUG_LOG(("DevStudio is running - forcing to TRUE\n"));
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
Bool CopyProtect::notifyLauncher(void)
|
||||
{
|
||||
DEBUG_LOG(("COPYPROTECTION - Notify launcher\n"));
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (skipProtection())
|
||||
{
|
||||
DEBUG_LOG(("DevStudio is running - skipping notifyLauncher()\n"));
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Force system to create message queue
|
||||
MSG msg;
|
||||
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
|
||||
|
||||
// Signal launcher to send the beef
|
||||
unsigned long eventTime = (timeGetTime() + 60000);
|
||||
|
||||
HANDLE event = NULL;
|
||||
|
||||
while (timeGetTime() < eventTime)
|
||||
{
|
||||
event = OpenEvent(EVENT_MODIFY_STATE, TRUE, protectGUID);
|
||||
|
||||
if (event)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep(0);
|
||||
}
|
||||
|
||||
if (event)
|
||||
{
|
||||
SetEvent(event);
|
||||
CloseHandle(event);
|
||||
DEBUG_LOG(("Launcher notified.\n"));
|
||||
DEBUG_LOG(("Waiting for message from launcher.\n"));
|
||||
|
||||
unsigned long endTime = (timeGetTime() + 10000);
|
||||
|
||||
while (timeGetTime() <= endTime)
|
||||
{
|
||||
if (PeekMessage(&msg, NULL, 0xBEEF, 0xBEEF, PM_REMOVE))
|
||||
{
|
||||
// 0xBEEF is in the WM_APP - 0xBFFF range
|
||||
if (msg.message == 0xBEEF)
|
||||
{
|
||||
DEBUG_LOG(("COPYPROTECTION - Received message from launcher (Elapsed time %ld).\n",
|
||||
(10000 - (endTime - timeGetTime()))));
|
||||
|
||||
HANDLE mappedFile = (HANDLE)msg.lParam;
|
||||
s_protectedData = MapViewOfFileEx(mappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
|
||||
|
||||
if (s_protectedData == NULL)
|
||||
{
|
||||
DEBUG_LOG(("***** MapViewOfFileEx() Failed!\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("The message says: %s\n", s_protectedData));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Sleep(0);
|
||||
}
|
||||
|
||||
DEBUG_LOG(("***** Failed to notify launcher!\n"));
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(("***** Failed to notify launcher!\n"));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void CopyProtect::checkForMessage(UINT message, LPARAM lParam)
|
||||
{
|
||||
// 0xBEEF is in the WM_APP - 0xBFFF range
|
||||
if (message == 0xBEEF)
|
||||
{
|
||||
DEBUG_LOG(("COPYPROTECTION - Received message from launcher.\n"));
|
||||
|
||||
HANDLE mappedFile = (HANDLE)lParam;
|
||||
s_protectedData = MapViewOfFileEx(mappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
|
||||
|
||||
if (s_protectedData == NULL)
|
||||
{
|
||||
DEBUG_LOG(("***** MapViewOfFileEx() Failed!"));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("The message says: %s\n", s_protectedData));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
Bool CopyProtect::validate(void)
|
||||
{
|
||||
DEBUG_LOG(("COPYPROTECTION - Validating\n"));
|
||||
DEBUG_LOG(("s_protectedData = %d (%s)\n", s_protectedData, (s_protectedData)?s_protectedData:"EEK!"));
|
||||
|
||||
if (s_protectedData != NULL)
|
||||
{
|
||||
return (strcmp((const char*)s_protectedData,
|
||||
"Play the \"Command & Conquer: Generals\" Multiplayer Test.") == 0);
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
if (skipProtection())
|
||||
{
|
||||
DEBUG_LOG(("DevStudio is running - forcing to TRUE\n"));
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void CopyProtect::shutdown(void)
|
||||
{
|
||||
DEBUG_LOG(("COPYPROTECTION - Shutdown\n"));
|
||||
|
||||
if (s_protectedData != NULL)
|
||||
{
|
||||
UnmapViewOfFile(s_protectedData);
|
||||
s_protectedData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DO_COPY_PROTECTION
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#include "Common/CriticalSection.h"
|
||||
|
||||
// Definitions.
|
||||
CriticalSection *TheAsciiStringCriticalSection = NULL;
|
||||
CriticalSection *TheUnicodeStringCriticalSection = NULL;
|
||||
CriticalSection *TheDmaCriticalSection = NULL;
|
||||
CriticalSection *TheMemoryPoolCriticalSection = NULL;
|
||||
CriticalSection *TheDebugLogCriticalSection = NULL;
|
||||
|
||||
#ifdef PERF_TIMERS
|
||||
PerfGather TheCritSecPerfGather("CritSec");
|
||||
#endif
|
||||
|
||||
964
Generals/Code/GameEngine/Source/Common/System/DataChunk.cpp
Normal file
964
Generals/Code/GameEngine/Source/Common/System/DataChunk.cpp
Normal file
@@ -0,0 +1,964 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// DataChunk.cpp
|
||||
// Implementation of Data Chunk save/load system
|
||||
// Author: Michael S. Booth, October 2000
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "Compression.h"
|
||||
#include "Common/DataChunk.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/FileSystem.h"
|
||||
|
||||
// If verbose, lots of debug logging.
|
||||
#define not_VERBOSE
|
||||
|
||||
CachedFileInputStream::CachedFileInputStream(void):m_buffer(NULL),m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
CachedFileInputStream::~CachedFileInputStream(void)
|
||||
{
|
||||
if (m_buffer) {
|
||||
delete[] m_buffer;
|
||||
m_buffer=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Bool CachedFileInputStream::open(AsciiString path)
|
||||
{
|
||||
File *file=TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
|
||||
m_size = 0;
|
||||
|
||||
if (file) {
|
||||
m_size=file->size();
|
||||
if (m_size) {
|
||||
m_buffer = file->readEntireAndClose();
|
||||
file = NULL;
|
||||
}
|
||||
m_pos=0;
|
||||
}
|
||||
|
||||
if (CompressionManager::isDataCompressed(m_buffer, m_size) == 0)
|
||||
{
|
||||
//DEBUG_LOG(("CachedFileInputStream::open() - file %s is uncompressed at %d bytes!\n", path.str(), m_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
Int uncompLen = CompressionManager::getUncompressedSize(m_buffer, m_size);
|
||||
//DEBUG_LOG(("CachedFileInputStream::open() - file %s is compressed! It should go from %d to %d\n", path.str(),
|
||||
// m_size, uncompLen));
|
||||
char *uncompBuffer = NEW char[uncompLen];
|
||||
Int actualLen = CompressionManager::decompressData(m_buffer, m_size, uncompBuffer, uncompLen);
|
||||
if (actualLen == uncompLen)
|
||||
{
|
||||
//DEBUG_LOG(("Using uncompressed data\n"));
|
||||
delete[] m_buffer;
|
||||
m_buffer = uncompBuffer;
|
||||
m_size = uncompLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
//DEBUG_LOG(("Decompression failed - using compressed data\n"));
|
||||
// decompression failed. Maybe we invalidly thought it was compressed?
|
||||
delete[] uncompBuffer;
|
||||
}
|
||||
}
|
||||
//if (m_size >= 4)
|
||||
//{
|
||||
// DEBUG_LOG(("File starts as '%c%c%c%c'\n", m_buffer[0], m_buffer[1],
|
||||
// m_buffer[2], m_buffer[3]));
|
||||
//}
|
||||
|
||||
if (file)
|
||||
{
|
||||
file->close();
|
||||
}
|
||||
return m_size != 0;
|
||||
}
|
||||
|
||||
void CachedFileInputStream::close(void)
|
||||
{
|
||||
if (m_buffer) {
|
||||
delete[] m_buffer;
|
||||
m_buffer=NULL;
|
||||
}
|
||||
m_pos=0;
|
||||
m_size=0;
|
||||
}
|
||||
|
||||
Int CachedFileInputStream::read(void *pData, Int numBytes)
|
||||
{
|
||||
if (m_buffer) {
|
||||
if ((numBytes+m_pos)>m_size) {
|
||||
numBytes=m_size-m_pos;
|
||||
}
|
||||
if (numBytes) {
|
||||
memcpy(pData,m_buffer+m_pos,numBytes);
|
||||
m_pos+=numBytes;
|
||||
}
|
||||
return(numBytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UnsignedInt CachedFileInputStream::tell(void)
|
||||
{
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
Bool CachedFileInputStream::absoluteSeek(UnsignedInt pos)
|
||||
{
|
||||
if (pos<0) return false;
|
||||
if (pos>m_size) {
|
||||
pos=m_size;
|
||||
}
|
||||
m_pos=pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool CachedFileInputStream::eof(void)
|
||||
{
|
||||
return m_size==m_pos;
|
||||
}
|
||||
|
||||
void CachedFileInputStream::rewind()
|
||||
{
|
||||
m_pos=0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
|
||||
//
|
||||
// FileInputStream - helper class. Used to read in data using a FILE *
|
||||
//
|
||||
/*
|
||||
FileInputStream::FileInputStream(void):m_file(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
FileInputStream::~FileInputStream(void)
|
||||
{
|
||||
if (m_file != NULL) {
|
||||
m_file->close();
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Bool FileInputStream::open(AsciiString path)
|
||||
{
|
||||
m_file = TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
|
||||
return m_file==NULL?false:true;
|
||||
}
|
||||
|
||||
void FileInputStream::close(void)
|
||||
{
|
||||
if (m_file != NULL) {
|
||||
m_file->close();
|
||||
m_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Int FileInputStream::read(void *pData, Int numBytes)
|
||||
{
|
||||
int bytesRead = 0;
|
||||
if (m_file != NULL) {
|
||||
bytesRead = m_file->read(pData, numBytes);
|
||||
}
|
||||
return(bytesRead);
|
||||
}
|
||||
|
||||
UnsignedInt FileInputStream::tell(void)
|
||||
{
|
||||
UnsignedInt pos = 0;
|
||||
if (m_file != NULL) {
|
||||
pos = m_file->position();
|
||||
}
|
||||
return(pos);
|
||||
}
|
||||
|
||||
Bool FileInputStream::absoluteSeek(UnsignedInt pos)
|
||||
{
|
||||
if (m_file != NULL) {
|
||||
return (m_file->seek(pos, File::START) != -1);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
Bool FileInputStream::eof(void)
|
||||
{
|
||||
if (m_file != NULL) {
|
||||
return (m_file->size() == m_file->position());
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void FileInputStream::rewind()
|
||||
{
|
||||
if (m_file != NULL) {
|
||||
m_file->seek(0, File::START);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DataChunkOutput
|
||||
// Data will be stored to a temporary m_tmp_file until the DataChunkOutput
|
||||
// object is destroyed. At that time, the actual output m_tmp_file will
|
||||
// be written, including a table of m_contents.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
#define TEMP_FILENAME "_tmpChunk.dat"
|
||||
|
||||
DataChunkOutput::DataChunkOutput( OutputStream *pOut ) :
|
||||
m_pOut(pOut)
|
||||
{
|
||||
AsciiString tmpFileName = TheGlobalData->getPath_UserData();
|
||||
tmpFileName.concat(TEMP_FILENAME);
|
||||
m_tmp_file = ::fopen( tmpFileName.str(), "wb" );
|
||||
// Added Sadullah Nader
|
||||
// Initializations missing and needed
|
||||
m_chunkStack = NULL;
|
||||
|
||||
// End Add
|
||||
}
|
||||
|
||||
DataChunkOutput::~DataChunkOutput()
|
||||
{
|
||||
// store the table of m_contents
|
||||
m_contents.write(*m_pOut);
|
||||
|
||||
// Rewind the temp m_tmp_file
|
||||
::fclose(m_tmp_file);
|
||||
|
||||
AsciiString tmpFileName = TheGlobalData->getPath_UserData();
|
||||
tmpFileName.concat(TEMP_FILENAME);
|
||||
|
||||
m_tmp_file = ::fopen( tmpFileName.str(), "rb" );
|
||||
::fseek(m_tmp_file, 0, SEEK_SET);
|
||||
|
||||
// append the temp m_tmp_file m_contents
|
||||
char buffer[256];
|
||||
int len = 256;
|
||||
while( len == 256 )
|
||||
{
|
||||
// copy data from the temp m_tmp_file to the output m_tmp_file
|
||||
len = ::fread( buffer, 1, 256, m_tmp_file );
|
||||
m_pOut->write( buffer, len );
|
||||
}
|
||||
|
||||
::fclose(m_tmp_file);
|
||||
}
|
||||
|
||||
void DataChunkOutput::openDataChunk( char *name, DataChunkVersionType ver )
|
||||
{
|
||||
// allocate (or get existing) ID from the table of m_contents
|
||||
UnsignedInt id = m_contents.allocateID( AsciiString(name) );
|
||||
|
||||
// allocate a new chunk and place it on top of the chunk stack
|
||||
OutputChunk *c = newInstance(OutputChunk);
|
||||
c->next = m_chunkStack;
|
||||
m_chunkStack = c;
|
||||
m_chunkStack->id = id;
|
||||
|
||||
// store the chunk ID
|
||||
::fwrite( (const char *)&id, sizeof(UnsignedInt), 1, m_tmp_file );
|
||||
|
||||
// store the chunk version number
|
||||
::fwrite( (const char *)&ver, sizeof(DataChunkVersionType), 1, m_tmp_file );
|
||||
|
||||
// remember this m_tmp_file position so we can write the real data size later
|
||||
c->filepos = ::ftell(m_tmp_file);
|
||||
#ifdef VERBOSE
|
||||
DEBUG_LOG(("Writing chunk %s at %d (%x)\n", name, ::ftell(m_tmp_file), ::ftell(m_tmp_file)));
|
||||
#endif
|
||||
// store a placeholder for the data size
|
||||
Int dummy = 0xffff;
|
||||
::fwrite( (const char *)&dummy, sizeof(Int), 1, m_tmp_file );
|
||||
}
|
||||
|
||||
void DataChunkOutput::closeDataChunk( void )
|
||||
{
|
||||
if (m_chunkStack == NULL)
|
||||
{
|
||||
// TODO: Throw exception
|
||||
return;
|
||||
}
|
||||
|
||||
// remember where we are
|
||||
Int here = ::ftell(m_tmp_file);
|
||||
|
||||
// rewind to store the data size
|
||||
::fseek(m_tmp_file, m_chunkStack->filepos , SEEK_SET);
|
||||
|
||||
// compute data size (not including the actual data size itself)
|
||||
Int size = here - m_chunkStack->filepos - sizeof(Int);
|
||||
|
||||
// store the data size
|
||||
::fwrite( (const char *)&size, sizeof(Int) , 1, m_tmp_file );
|
||||
|
||||
// go back to where we were
|
||||
::fseek(m_tmp_file, here , SEEK_SET);
|
||||
|
||||
// pop the chunk off the stack
|
||||
OutputChunk *c = m_chunkStack;
|
||||
#ifdef VERBOSE
|
||||
DEBUG_LOG(("Closing chunk %s at %d (%x)\n", m_contents.getName(c->id).str(), here, here));
|
||||
#endif
|
||||
m_chunkStack = m_chunkStack->next;
|
||||
c->deleteInstance();
|
||||
}
|
||||
|
||||
void DataChunkOutput::writeReal( Real r )
|
||||
{
|
||||
::fwrite( (const char *)&r, sizeof(float) , 1, m_tmp_file );
|
||||
}
|
||||
|
||||
void DataChunkOutput::writeInt( Int i )
|
||||
{
|
||||
::fwrite( (const char *)&i, sizeof(Int) , 1, m_tmp_file );
|
||||
}
|
||||
|
||||
void DataChunkOutput::writeByte( Byte b )
|
||||
{
|
||||
::fwrite( (const char *)&b, sizeof(Byte) , 1, m_tmp_file );
|
||||
}
|
||||
|
||||
void DataChunkOutput::writeArrayOfBytes(char *ptr, Int len)
|
||||
{
|
||||
::fwrite( (const char *)ptr, 1, len , m_tmp_file );
|
||||
}
|
||||
|
||||
void DataChunkOutput::writeAsciiString( const AsciiString& theString )
|
||||
{
|
||||
UnsignedShort len = theString.getLength();
|
||||
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
|
||||
::fwrite( theString.str(), len , 1, m_tmp_file );
|
||||
}
|
||||
|
||||
void DataChunkOutput::writeUnicodeString( UnicodeString theString )
|
||||
{
|
||||
UnsignedShort len = theString.getLength();
|
||||
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
|
||||
::fwrite( theString.str(), len*sizeof(WideChar) , 1, m_tmp_file );
|
||||
}
|
||||
|
||||
void DataChunkOutput::writeDict( const Dict& d )
|
||||
{
|
||||
UnsignedShort len = d.getPairCount();
|
||||
::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
NameKeyType k = d.getNthKey(i);
|
||||
AsciiString kname = TheNameKeyGenerator->keyToName(k);
|
||||
|
||||
Int keyAndType = m_contents.allocateID(kname);
|
||||
keyAndType <<= 8;
|
||||
Dict::DataType t = d.getNthType(i);
|
||||
keyAndType |= (t & 0xff);
|
||||
writeInt(keyAndType);
|
||||
|
||||
switch(t)
|
||||
{
|
||||
case Dict::DICT_BOOL:
|
||||
writeByte(d.getNthBool(i)?1:0);
|
||||
break;
|
||||
case Dict::DICT_INT:
|
||||
writeInt(d.getNthInt(i));
|
||||
break;
|
||||
case Dict::DICT_REAL:
|
||||
writeReal(d.getNthReal(i));
|
||||
break;
|
||||
case Dict::DICT_ASCIISTRING:
|
||||
writeAsciiString(d.getNthAsciiString(i));
|
||||
break;
|
||||
case Dict::DICT_UNICODESTRING:
|
||||
writeUnicodeString(d.getNthUnicodeString(i));
|
||||
break;
|
||||
default:
|
||||
DEBUG_CRASH(("impossible"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DataChunkTableOfContents
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
DataChunkTableOfContents::DataChunkTableOfContents( void ) :
|
||||
m_list(NULL),
|
||||
m_nextID(1),
|
||||
m_listLength(0),
|
||||
m_headerOpened(false)
|
||||
{
|
||||
}
|
||||
|
||||
DataChunkTableOfContents::~DataChunkTableOfContents()
|
||||
{
|
||||
Mapping *m, *next;
|
||||
|
||||
// free all list elements
|
||||
for( m=m_list; m; m=next )
|
||||
{
|
||||
next = m->next;
|
||||
m->deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
// return mapping data
|
||||
Mapping *DataChunkTableOfContents::findMapping( const AsciiString& name )
|
||||
{
|
||||
Mapping *m;
|
||||
|
||||
for( m=m_list; m; m=m->next )
|
||||
if (name == m->name )
|
||||
return m;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// convert name to integer identifier
|
||||
UnsignedInt DataChunkTableOfContents::getID( const AsciiString& name )
|
||||
{
|
||||
Mapping *m = findMapping( name );
|
||||
|
||||
if (m)
|
||||
return m->id;
|
||||
|
||||
DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for name %s\n",name.str()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert integer identifier to name
|
||||
AsciiString DataChunkTableOfContents::getName( UnsignedInt id )
|
||||
{
|
||||
Mapping *m;
|
||||
|
||||
for( m=m_list; m; m=m->next )
|
||||
if (m->id == id)
|
||||
return m->name;
|
||||
|
||||
DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for id %d\n",id));
|
||||
return AsciiString::TheEmptyString;
|
||||
}
|
||||
|
||||
// create new ID for given name or return existing mapping
|
||||
UnsignedInt DataChunkTableOfContents::allocateID(const AsciiString& name )
|
||||
{
|
||||
Mapping *m = findMapping( name );
|
||||
|
||||
if (m)
|
||||
return m->id;
|
||||
else
|
||||
{
|
||||
// allocate new id mapping
|
||||
m = newInstance(Mapping);
|
||||
|
||||
m->id = m_nextID++;
|
||||
m->name = name ;
|
||||
|
||||
// prepend to list
|
||||
m->next = m_list;
|
||||
m_list = m;
|
||||
|
||||
m_listLength++;
|
||||
|
||||
return m->id;
|
||||
}
|
||||
}
|
||||
|
||||
// output the table of m_contents to a binary m_tmp_file stream
|
||||
void DataChunkTableOfContents::write( OutputStream &s )
|
||||
{
|
||||
Mapping *m;
|
||||
unsigned char len;
|
||||
|
||||
Byte tag[4]={'C','k', 'M', 'p'}; // Chunky height map. jba.
|
||||
s.write(tag,sizeof(tag));
|
||||
|
||||
// output number of elements in the table
|
||||
s.write( (void *)&this->m_listLength, sizeof(Int) );
|
||||
|
||||
// output symbol table
|
||||
for( m=this->m_list; m; m=m->next )
|
||||
{
|
||||
len = m->name.getLength();
|
||||
s.write( (char *)&len, sizeof(unsigned char) );
|
||||
s.write( (char *)m->name.str(), len);
|
||||
s.write( (char *)&m->id, sizeof(UnsignedInt) );
|
||||
}
|
||||
}
|
||||
|
||||
// read the table of m_contents from a binary m_tmp_file stream
|
||||
// TODO: Should this reset the symbol table?
|
||||
// Append symbols to table
|
||||
void DataChunkTableOfContents::read( ChunkInputStream &s)
|
||||
{
|
||||
Int count, i;
|
||||
UnsignedInt maxID = 0;
|
||||
unsigned char len;
|
||||
Mapping *m;
|
||||
|
||||
Byte tag[4]={'x','x', 'x', 'x'}; // Chunky height map. jba.
|
||||
s.read(tag,sizeof(tag));
|
||||
if (tag[0] != 'C' || tag[1] != 'k' || tag[2] != 'M' || tag[3] != 'p') {
|
||||
return; // Don't throw, may happen with legacy files.
|
||||
}
|
||||
|
||||
// get number of symbols in table
|
||||
s.read( (char *)&count, sizeof(Int) );
|
||||
|
||||
for( i=0; i<count; i++ )
|
||||
{
|
||||
// allocate new id mapping
|
||||
m = newInstance(Mapping);
|
||||
|
||||
// read string length
|
||||
s.read( (char *)&len, sizeof(unsigned char) );
|
||||
|
||||
// allocate and read in string
|
||||
if (len>0) {
|
||||
char *str = m->name.getBufferForRead(len);
|
||||
s.read( str, len );
|
||||
str[len] = '\000';
|
||||
}
|
||||
|
||||
// read id
|
||||
s.read( (char *)&m->id, sizeof(UnsignedInt) );
|
||||
|
||||
// prepend to list
|
||||
m->next = this->m_list;
|
||||
this->m_list = m;
|
||||
|
||||
this->m_listLength++;
|
||||
|
||||
// track max ID used
|
||||
if (m->id > maxID)
|
||||
maxID = m->id;
|
||||
}
|
||||
m_headerOpened = count > 0 && !s.eof();
|
||||
|
||||
// adjust next ID so no ID's are reused
|
||||
this->m_nextID = max( this->m_nextID, maxID+1 );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DataChunkInput
|
||||
//----------------------------------------------------------------------
|
||||
DataChunkInput::DataChunkInput( ChunkInputStream *pStream ) : m_file( pStream ),
|
||||
m_userData(NULL),
|
||||
m_currentObject(NULL),
|
||||
m_chunkStack(NULL),
|
||||
m_parserList(NULL)
|
||||
{
|
||||
// read table of m_contents
|
||||
m_contents.read(*m_file);
|
||||
|
||||
// store location of first data chunk
|
||||
m_fileposOfFirstChunk = m_file->tell();
|
||||
}
|
||||
|
||||
DataChunkInput::~DataChunkInput()
|
||||
{
|
||||
clearChunkStack();
|
||||
|
||||
UserParser *p, *next;
|
||||
for (p=m_parserList; p; p=next) {
|
||||
next = p->next;
|
||||
p->deleteInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// register a user parsing function for a given DataChunk label
|
||||
void DataChunkInput::registerParser( const AsciiString& label, const AsciiString& parentLabel,
|
||||
DataChunkParserPtr parser, void *userData )
|
||||
{
|
||||
UserParser *p = newInstance(UserParser);
|
||||
|
||||
p->label.set( label );
|
||||
p->parentLabel.set(parentLabel );
|
||||
p->parser = parser;
|
||||
p->userData = userData;
|
||||
|
||||
// prepend parser to parser list
|
||||
p->next = m_parserList;
|
||||
m_parserList = p;
|
||||
}
|
||||
|
||||
// parse the chunk stream using registered parsers
|
||||
// it is assumed that the file position is at the start of a data chunk
|
||||
// (it can be inside a parent chunk) when parse is called.
|
||||
Bool DataChunkInput::parse( void *userData )
|
||||
{
|
||||
AsciiString label;
|
||||
AsciiString parentLabel;
|
||||
DataChunkVersionType ver;
|
||||
UserParser *parser;
|
||||
Bool scopeOK;
|
||||
DataChunkInfo info;
|
||||
|
||||
// If the header wasn't a chunk table of contents, we can't parse.
|
||||
if (!m_contents.isOpenedForRead()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we are inside a data chunk right now, get its name
|
||||
if (m_chunkStack)
|
||||
parentLabel = m_contents.getName( m_chunkStack->id );
|
||||
|
||||
while( atEndOfFile() == false )
|
||||
{
|
||||
if (m_chunkStack) { // If we are parsing chunks in a chunk, check current length.
|
||||
if (m_chunkStack->dataLeft < CHUNK_HEADER_BYTES) {
|
||||
DEBUG_ASSERTCRASH( m_chunkStack->dataLeft==0, ("Unexpected extra data in chunk."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// open the chunk
|
||||
label = openDataChunk( &ver );
|
||||
if (atEndOfFile()) { // FILE * returns eof after you read past end of file, so check.
|
||||
break;
|
||||
}
|
||||
|
||||
// find a registered parser for this chunk
|
||||
for( parser=m_parserList; parser; parser=parser->next )
|
||||
{
|
||||
// chunk labels must match
|
||||
if ( parser->label == label )
|
||||
{
|
||||
// make sure parent name (scope) also matches
|
||||
scopeOK = true;
|
||||
|
||||
if (parentLabel != parser->parentLabel)
|
||||
scopeOK = false;
|
||||
|
||||
if (scopeOK)
|
||||
{
|
||||
// m_tmp_file out the chunk info and call the user parser
|
||||
info.label = label;
|
||||
info.parentLabel = parentLabel;
|
||||
info.version = ver;
|
||||
info.dataSize = getChunkDataSize();
|
||||
|
||||
if (parser->parser( *this, &info, userData ) == false)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// close chunk (and skip to end if need be)
|
||||
closeDataChunk();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear the stack
|
||||
void DataChunkInput::clearChunkStack( void )
|
||||
{
|
||||
InputChunk *c, *next;
|
||||
|
||||
for( c=m_chunkStack; c; c=next )
|
||||
{
|
||||
next = c->next;
|
||||
c->deleteInstance();
|
||||
}
|
||||
|
||||
m_chunkStack = NULL;
|
||||
}
|
||||
|
||||
// reset the stream to just-opened state - ready to parse the first chunk
|
||||
void DataChunkInput::reset( void )
|
||||
{
|
||||
clearChunkStack();
|
||||
m_file->absoluteSeek( m_fileposOfFirstChunk );
|
||||
}
|
||||
|
||||
// Checks if the file has our initial tag word.
|
||||
Bool DataChunkInput::isValidFileType(void)
|
||||
{
|
||||
return m_contents.isOpenedForRead();
|
||||
}
|
||||
|
||||
AsciiString DataChunkInput::openDataChunk(DataChunkVersionType *ver )
|
||||
{
|
||||
// allocate a new chunk and place it on top of the chunk stack
|
||||
InputChunk *c = newInstance(InputChunk);
|
||||
c->id = 0;
|
||||
c->version = 0;
|
||||
c->dataSize = 0;
|
||||
//DEBUG_LOG(("Opening data chunk at offset %d (%x)\n", m_file->tell(), m_file->tell()));
|
||||
// read the chunk ID
|
||||
m_file->read( (char *)&c->id, sizeof(UnsignedInt) );
|
||||
decrementDataLeft( sizeof(UnsignedInt) );
|
||||
|
||||
// read the chunk version number
|
||||
m_file->read( (char *)&c->version, sizeof(DataChunkVersionType) );
|
||||
decrementDataLeft( sizeof(DataChunkVersionType) );
|
||||
|
||||
// read the chunk data size
|
||||
m_file->read( (char *)&c->dataSize, sizeof(Int) );
|
||||
decrementDataLeft( sizeof(Int) );
|
||||
|
||||
// all of the data remains to be read
|
||||
c->dataLeft = c->dataSize;
|
||||
c->chunkStart = m_file->tell();
|
||||
|
||||
*ver = c->version;
|
||||
|
||||
c->next = m_chunkStack;
|
||||
m_chunkStack = c;
|
||||
if (this->atEndOfFile()) {
|
||||
return (AsciiString(""));
|
||||
}
|
||||
return m_contents.getName( c->id );
|
||||
}
|
||||
|
||||
// close chunk and move to start of next chunk
|
||||
void DataChunkInput::closeDataChunk( void )
|
||||
{
|
||||
if (m_chunkStack == NULL)
|
||||
{
|
||||
// TODO: Throw exception
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_chunkStack->dataLeft > 0)
|
||||
{
|
||||
// skip past the remainder of this chunk
|
||||
m_file->absoluteSeek( m_file->tell()+m_chunkStack->dataLeft );
|
||||
decrementDataLeft( m_chunkStack->dataLeft );
|
||||
|
||||
}
|
||||
|
||||
// pop the chunk off the stack
|
||||
InputChunk *c = m_chunkStack;
|
||||
m_chunkStack = m_chunkStack->next;
|
||||
c->deleteInstance();
|
||||
}
|
||||
|
||||
|
||||
// return label of current data chunk
|
||||
AsciiString DataChunkInput::getChunkLabel( void )
|
||||
{
|
||||
if (m_chunkStack == NULL)
|
||||
{
|
||||
// TODO: Throw exception
|
||||
DEBUG_CRASH(("Bad."));
|
||||
return AsciiString("");
|
||||
}
|
||||
|
||||
return m_contents.getName( m_chunkStack->id );
|
||||
}
|
||||
|
||||
// return version of current data chunk
|
||||
DataChunkVersionType DataChunkInput::getChunkVersion( void )
|
||||
{
|
||||
if (m_chunkStack == NULL)
|
||||
{
|
||||
// TODO: Throw exception
|
||||
DEBUG_CRASH(("Bad."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_chunkStack->version;
|
||||
}
|
||||
|
||||
// return size of data stored in this chunk
|
||||
UnsignedInt DataChunkInput::getChunkDataSize( void )
|
||||
{
|
||||
if (m_chunkStack == NULL)
|
||||
{
|
||||
// TODO: Throw exception
|
||||
DEBUG_CRASH(("Bad."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_chunkStack->dataSize;
|
||||
}
|
||||
|
||||
|
||||
// return size of data left to read in this chunk
|
||||
UnsignedInt DataChunkInput::getChunkDataSizeLeft( void )
|
||||
{
|
||||
if (m_chunkStack == NULL)
|
||||
{
|
||||
// TODO: Throw exception
|
||||
DEBUG_CRASH(("Bad."));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_chunkStack->dataLeft;
|
||||
}
|
||||
|
||||
Bool DataChunkInput::atEndOfChunk( void )
|
||||
{
|
||||
if (m_chunkStack)
|
||||
{
|
||||
if (m_chunkStack->dataLeft <= 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// update data left in chunk(s)
|
||||
// since data read from a chunk is also read from all parent chunks,
|
||||
// traverse the chunk stack and decrement the data left for each
|
||||
void DataChunkInput::decrementDataLeft( Int size )
|
||||
{
|
||||
InputChunk *c;
|
||||
|
||||
c = m_chunkStack;
|
||||
while (c) {
|
||||
c->dataLeft -= size;
|
||||
c = c->next;
|
||||
}
|
||||
// The sizes of the parent chunks on the stack are adjusted in closeDataChunk.
|
||||
}
|
||||
|
||||
Real DataChunkInput::readReal(void)
|
||||
{
|
||||
Real r;
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Real), ("Read past end of chunk."));
|
||||
m_file->read( (char *)&r, sizeof(Real) );
|
||||
decrementDataLeft( sizeof(Real) );
|
||||
return r;
|
||||
}
|
||||
|
||||
Int DataChunkInput::readInt(void)
|
||||
{
|
||||
Int i;
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Int), ("Read past end of chunk."));
|
||||
m_file->read( (char *)&i, sizeof(Int) );
|
||||
decrementDataLeft( sizeof(Int) );
|
||||
return i;
|
||||
}
|
||||
|
||||
Byte DataChunkInput::readByte(void)
|
||||
{
|
||||
Byte b;
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Byte), ("Read past end of chunk."));
|
||||
m_file->read( (char *)&b, sizeof(Byte) );
|
||||
decrementDataLeft( sizeof(Byte) );
|
||||
return b;
|
||||
}
|
||||
|
||||
void DataChunkInput::readArrayOfBytes(char *ptr, Int len)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
|
||||
m_file->read( ptr, len );
|
||||
decrementDataLeft( len );
|
||||
}
|
||||
|
||||
Dict DataChunkInput::readDict()
|
||||
{
|
||||
UnsignedShort len;
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
|
||||
m_file->read( &len, sizeof(UnsignedShort) );
|
||||
decrementDataLeft( sizeof(UnsignedShort) );
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
|
||||
|
||||
Dict d(len);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
Int keyAndType = readInt();
|
||||
Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
|
||||
keyAndType >>= 8;
|
||||
|
||||
AsciiString kname = m_contents.getName(keyAndType);
|
||||
NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
|
||||
|
||||
switch(t)
|
||||
{
|
||||
case Dict::DICT_BOOL:
|
||||
d.setBool(k, readByte() ? true : false);
|
||||
break;
|
||||
case Dict::DICT_INT:
|
||||
d.setInt(k, readInt());
|
||||
break;
|
||||
case Dict::DICT_REAL:
|
||||
d.setReal(k, readReal());
|
||||
break;
|
||||
case Dict::DICT_ASCIISTRING:
|
||||
d.setAsciiString(k, readAsciiString());
|
||||
break;
|
||||
case Dict::DICT_UNICODESTRING:
|
||||
d.setUnicodeString(k, readUnicodeString());
|
||||
break;
|
||||
default:
|
||||
throw ERROR_CORRUPT_FILE_FORMAT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
AsciiString DataChunkInput::readAsciiString(void)
|
||||
{
|
||||
UnsignedShort len;
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
|
||||
m_file->read( &len, sizeof(UnsignedShort) );
|
||||
decrementDataLeft( sizeof(UnsignedShort) );
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
|
||||
AsciiString theString;
|
||||
if (len>0) {
|
||||
char *str = theString.getBufferForRead(len);
|
||||
m_file->read( str, len );
|
||||
decrementDataLeft( len );
|
||||
// add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
|
||||
str[len] = '\000';
|
||||
}
|
||||
|
||||
return theString;
|
||||
}
|
||||
|
||||
UnicodeString DataChunkInput::readUnicodeString(void)
|
||||
{
|
||||
UnsignedShort len;
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
|
||||
m_file->read( &len, sizeof(UnsignedShort) );
|
||||
decrementDataLeft( sizeof(UnsignedShort) );
|
||||
DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
|
||||
UnicodeString theString;
|
||||
if (len>0) {
|
||||
WideChar *str = theString.getBufferForRead(len);
|
||||
m_file->read( (char*)str, len*sizeof(WideChar) );
|
||||
decrementDataLeft( len*sizeof(WideChar) );
|
||||
// add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
|
||||
str[len] = '\000';
|
||||
}
|
||||
|
||||
return theString;
|
||||
}
|
||||
779
Generals/Code/GameEngine/Source/Common/System/Debug.cpp
Normal file
779
Generals/Code/GameEngine/Source/Common/System/Debug.cpp
Normal file
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Debug.cpp
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: Debug.cpp
|
||||
//
|
||||
// Created: Steven Johnson, August 2001
|
||||
//
|
||||
// Desc: Debug logging and other debug utilities
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// SYSTEM INCLUDES
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
|
||||
// USER INCLUDES
|
||||
#define DEBUG_THREADSAFE
|
||||
#ifdef DEBUG_THREADSAFE
|
||||
#include "Common/CriticalSection.h"
|
||||
#endif
|
||||
#include "Common/Debug.h"
|
||||
#include "Common/registry.h"
|
||||
#include "Common/SystemInfo.h"
|
||||
#include "Common/UnicodeString.h"
|
||||
#include "GameClient/GameText.h"
|
||||
#include "GameClient/Keyboard.h"
|
||||
#include "GameClient/Mouse.h"
|
||||
#include "Common/StackDump.h"
|
||||
|
||||
// Horrible reference, but we really, really need to know if we are windowed.
|
||||
extern bool DX8Wrapper_IsWindowed;
|
||||
extern HWND ApplicationHWnd;
|
||||
|
||||
extern char *gAppPrefix; /// So WB can have a different log file name.
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// this should ALWAYS be present
|
||||
#pragma optimize("", off)
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DEFINES
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifdef DEBUG_LOGGING
|
||||
|
||||
#if defined(_INTERNAL)
|
||||
#define DEBUG_FILE_NAME "DebugLogFileI.txt"
|
||||
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrevI.txt"
|
||||
#elif defined(_DEBUG)
|
||||
#define DEBUG_FILE_NAME "DebugLogFileD.txt"
|
||||
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrevD.txt"
|
||||
#else
|
||||
#define DEBUG_FILE_NAME "DebugLogFile.txt"
|
||||
#define DEBUG_FILE_NAME_PREV "DebugLogFilePrev.txt"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PRIVATE TYPES
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PRIVATE DATA
|
||||
// ----------------------------------------------------------------------------
|
||||
#ifdef DEBUG_LOGGING
|
||||
static FILE *theLogFile = NULL;
|
||||
#endif
|
||||
#define LARGE_BUFFER 8192
|
||||
static char theBuffer[ LARGE_BUFFER ]; // make it big to avoid weird overflow bugs in debug mode
|
||||
static int theDebugFlags = 0;
|
||||
static DWORD theMainThreadID = 0;
|
||||
// ----------------------------------------------------------------------------
|
||||
// PUBLIC DATA
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
char* TheCurrentIgnoreCrashPtr = NULL;
|
||||
#ifdef DEBUG_LOGGING
|
||||
UnsignedInt DebugLevelMask = 0;
|
||||
const char *TheDebugLevels[DEBUG_LEVEL_MAX] = {
|
||||
"NET"
|
||||
};
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PRIVATE PROTOTYPES
|
||||
// ----------------------------------------------------------------------------
|
||||
static const char *getCurrentTimeString(void);
|
||||
static const char *getCurrentTickString(void);
|
||||
static const char *prepBuffer(const char* format, char *buffer);
|
||||
#ifdef DEBUG_LOGGING
|
||||
static void doLogOutput(const char *buffer);
|
||||
#endif
|
||||
static int doCrashBox(const char *buffer, Bool logResult);
|
||||
static void whackFunnyCharacters(char *buf);
|
||||
#ifdef DEBUG_STACKTRACE
|
||||
static void doStackDump();
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PRIVATE FUNCTIONS
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
inline Bool ignoringAsserts()
|
||||
{
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
return !DX8Wrapper_IsWindowed || TheGlobalData->m_debugIgnoreAsserts;
|
||||
#else
|
||||
return !DX8Wrapper_IsWindowed;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
inline HWND getThreadHWND()
|
||||
{
|
||||
return (theMainThreadID == GetCurrentThreadId())?ApplicationHWnd:NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
int MessageBoxWrapper( LPCSTR lpText, LPCSTR lpCaption, UINT uType )
|
||||
{
|
||||
HWND threadHWND = getThreadHWND();
|
||||
if (!threadHWND)
|
||||
return (uType & MB_ABORTRETRYIGNORE)?IDIGNORE:IDYES;
|
||||
|
||||
return ::MessageBox(threadHWND, lpText, lpCaption, uType);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// getCurrentTimeString
|
||||
/**
|
||||
Return the current time in string form
|
||||
*/
|
||||
// ----------------------------------------------------------------------------
|
||||
static const char *getCurrentTimeString(void)
|
||||
{
|
||||
time_t aclock;
|
||||
time(&aclock);
|
||||
struct tm *newtime = localtime(&aclock);
|
||||
return asctime(newtime);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// getCurrentTickString
|
||||
/**
|
||||
Return the current TickCount in string form
|
||||
*/
|
||||
// ----------------------------------------------------------------------------
|
||||
static const char *getCurrentTickString(void)
|
||||
{
|
||||
static char TheTickString[32];
|
||||
sprintf(TheTickString, "(T=%08lx)",::GetTickCount());
|
||||
return TheTickString;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// prepBuffer
|
||||
// zap the buffer and optionally prepend the tick time.
|
||||
// ----------------------------------------------------------------------------
|
||||
/**
|
||||
Empty the buffer passed in, then optionally prepend the current TickCount
|
||||
value in string form, depending on the setting of theDebugFlags.
|
||||
*/
|
||||
static const char *prepBuffer(const char* format, char *buffer)
|
||||
{
|
||||
buffer[0] = 0;
|
||||
#ifdef ALLOW_DEBUG_UTILS
|
||||
if (theDebugFlags & DEBUG_FLAG_PREPEND_TIME)
|
||||
{
|
||||
strcpy(buffer, getCurrentTickString());
|
||||
strcat(buffer, " ");
|
||||
}
|
||||
#endif
|
||||
return format;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// doLogOutput
|
||||
/**
|
||||
send a string directly to the log file and/or console without further processing.
|
||||
*/
|
||||
// ----------------------------------------------------------------------------
|
||||
#ifdef DEBUG_LOGGING
|
||||
static void doLogOutput(const char *buffer)
|
||||
{
|
||||
// log message to file
|
||||
if (theDebugFlags & DEBUG_FLAG_LOG_TO_FILE)
|
||||
{
|
||||
if (theLogFile)
|
||||
{
|
||||
fprintf(theLogFile, "%s", buffer); // note, no \n (should be there already)
|
||||
fflush(theLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
// log message to dev studio output window
|
||||
if (theDebugFlags & DEBUG_FLAG_LOG_TO_CONSOLE)
|
||||
{
|
||||
::OutputDebugString(buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// doCrashBox
|
||||
/*
|
||||
present a messagebox with the given message. Depending on user selection,
|
||||
we exit the app, break into debugger, or continue execution.
|
||||
*/
|
||||
// ----------------------------------------------------------------------------
|
||||
static int doCrashBox(const char *buffer, Bool logResult)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!ignoringAsserts()) {
|
||||
//result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING|MB_DEFBUTTON3);
|
||||
result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING);
|
||||
} else {
|
||||
result = IDIGNORE;
|
||||
}
|
||||
|
||||
switch(result)
|
||||
{
|
||||
case IDABORT:
|
||||
#ifdef DEBUG_LOGGING
|
||||
if (logResult)
|
||||
DebugLog("[Abort]\n");
|
||||
#endif
|
||||
_exit(1);
|
||||
break;
|
||||
case IDRETRY:
|
||||
#ifdef DEBUG_LOGGING
|
||||
if (logResult)
|
||||
DebugLog("[Retry]\n");
|
||||
#endif
|
||||
::DebugBreak();
|
||||
break;
|
||||
case IDIGNORE:
|
||||
#ifdef DEBUG_LOGGING
|
||||
// do nothing, just keep going
|
||||
if (logResult)
|
||||
DebugLog("[Ignore]\n");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_STACKTRACE
|
||||
// ----------------------------------------------------------------------------
|
||||
/**
|
||||
Dumps a stack trace (from the current PC) to logfile and/or console.
|
||||
*/
|
||||
static void doStackDump()
|
||||
{
|
||||
const int STACKTRACE_SIZE = 24;
|
||||
const int STACKTRACE_SKIP = 2;
|
||||
void* stacktrace[STACKTRACE_SIZE];
|
||||
|
||||
doLogOutput("\nStack Dump:\n");
|
||||
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
|
||||
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, doLogOutput);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// whackFunnyCharacters
|
||||
/**
|
||||
Eliminates any undesirable nonprinting characters, aside from newline,
|
||||
replacing them with spaces.
|
||||
*/
|
||||
// ----------------------------------------------------------------------------
|
||||
static void whackFunnyCharacters(char *buf)
|
||||
{
|
||||
for (char *p = buf + strlen(buf) - 1; p >= buf; --p)
|
||||
{
|
||||
// ok, these are naughty magic numbers, but I'm guessing you know ASCII....
|
||||
if (*p >= 0 && *p < 32 && *p != 10 && *p != 13)
|
||||
*p = 32;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PUBLIC FUNCTIONS
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DebugInit
|
||||
// ----------------------------------------------------------------------------
|
||||
#ifdef ALLOW_DEBUG_UTILS
|
||||
/**
|
||||
Initialize the debug utilities. This should be called once, as near to the
|
||||
start of the app as possible, before anything else (since other code will
|
||||
probably want to make use of it).
|
||||
*/
|
||||
void DebugInit(int flags)
|
||||
{
|
||||
// if (theDebugFlags != 0)
|
||||
// ::MessageBox(NULL, "Debug already inited", "", MB_OK|MB_APPLMODAL);
|
||||
|
||||
// just quietly allow multiple calls to this, so that static ctors can call it.
|
||||
if (theDebugFlags == 0 && strcmp(gAppPrefix, "wb_") != 0)
|
||||
{
|
||||
theDebugFlags = flags;
|
||||
|
||||
theMainThreadID = GetCurrentThreadId();
|
||||
|
||||
#ifdef DEBUG_LOGGING
|
||||
|
||||
char dirbuf[ _MAX_PATH ];
|
||||
::GetModuleFileName( NULL, dirbuf, sizeof( dirbuf ) );
|
||||
char *pEnd = dirbuf + strlen( dirbuf );
|
||||
while( pEnd != dirbuf )
|
||||
{
|
||||
if( *pEnd == '\\' )
|
||||
{
|
||||
*(pEnd + 1) = 0;
|
||||
break;
|
||||
}
|
||||
pEnd--;
|
||||
}
|
||||
|
||||
char prevbuf[ _MAX_PATH ];
|
||||
char curbuf[ _MAX_PATH ];
|
||||
|
||||
strcpy(prevbuf, dirbuf);
|
||||
strcat(prevbuf, gAppPrefix);
|
||||
strcat(prevbuf, DEBUG_FILE_NAME_PREV);
|
||||
strcpy(curbuf, dirbuf);
|
||||
strcat(curbuf, gAppPrefix);
|
||||
strcat(curbuf, DEBUG_FILE_NAME);
|
||||
|
||||
remove(prevbuf);
|
||||
rename(curbuf, prevbuf);
|
||||
theLogFile = fopen(curbuf, "w");
|
||||
if (theLogFile != NULL)
|
||||
{
|
||||
DebugLog("Log %s opened: %s\n", curbuf, getCurrentTimeString());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DebugLog
|
||||
// ----------------------------------------------------------------------------
|
||||
#ifdef DEBUG_LOGGING
|
||||
/**
|
||||
Print a character string to the logfile and/or console.
|
||||
*/
|
||||
void DebugLog(const char *format, ...)
|
||||
{
|
||||
#ifdef DEBUG_THREADSAFE
|
||||
ScopedCriticalSection scopedCriticalSection(TheDebugLogCriticalSection);
|
||||
#endif
|
||||
|
||||
if (theDebugFlags == 0)
|
||||
MessageBoxWrapper("DebugLog - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
|
||||
|
||||
format = prepBuffer(format, theBuffer);
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
vsprintf(theBuffer + strlen(theBuffer), format, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (strlen(theBuffer) >= sizeof(theBuffer))
|
||||
MessageBoxWrapper("String too long for debug buffer", "", MB_OK|MB_TASKMODAL);
|
||||
|
||||
whackFunnyCharacters(theBuffer);
|
||||
doLogOutput(theBuffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DebugCrash
|
||||
// ----------------------------------------------------------------------------
|
||||
#ifdef DEBUG_CRASHING
|
||||
/**
|
||||
Print a character string to the logfile and/or console, then halt execution
|
||||
while presenting the user with an exit/debug/ignore dialog containing the same
|
||||
text message.
|
||||
*/
|
||||
void DebugCrash(const char *format, ...)
|
||||
{
|
||||
// Note: You might want to make this thread safe, but we cannot. The reason is that
|
||||
// there is an implicit requirement on other threads that the message loop be running.
|
||||
|
||||
// make it not static so that it'll be thread-safe.
|
||||
// make it big to avoid weird overflow bugs in debug mode
|
||||
char theCrashBuffer[ LARGE_BUFFER ];
|
||||
if (theDebugFlags == 0)
|
||||
{
|
||||
if (!DX8Wrapper_IsWindowed) {
|
||||
if (ApplicationHWnd) {
|
||||
ShowWindow(ApplicationHWnd, SW_HIDE);
|
||||
}
|
||||
}
|
||||
MessageBoxWrapper("DebugCrash - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
|
||||
}
|
||||
|
||||
format = prepBuffer(format, theCrashBuffer);
|
||||
strcat(theCrashBuffer, "ASSERTION FAILURE: ");
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
vsprintf(theCrashBuffer + strlen(theCrashBuffer), format, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (strlen(theCrashBuffer) >= sizeof(theCrashBuffer))
|
||||
{
|
||||
if (!DX8Wrapper_IsWindowed) {
|
||||
if (ApplicationHWnd) {
|
||||
ShowWindow(ApplicationHWnd, SW_HIDE);
|
||||
}
|
||||
}
|
||||
MessageBoxWrapper("String too long for debug buffers", "", MB_OK|MB_TASKMODAL);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOGGING
|
||||
if (ignoringAsserts())
|
||||
{
|
||||
doLogOutput("**** CRASH IN FULL SCREEN - Auto-ignored, CHECK THIS LOG!\n");
|
||||
}
|
||||
whackFunnyCharacters(theCrashBuffer);
|
||||
doLogOutput(theCrashBuffer);
|
||||
#endif
|
||||
#ifdef DEBUG_STACKTRACE
|
||||
if (!TheGlobalData->m_debugIgnoreStackTrace)
|
||||
{
|
||||
doStackDump();
|
||||
}
|
||||
#endif
|
||||
|
||||
strcat(theCrashBuffer, "\n\nAbort->exception; Retry->debugger; Ignore->continue\n");
|
||||
|
||||
int result = doCrashBox(theCrashBuffer, true);
|
||||
|
||||
if (result == IDIGNORE && TheCurrentIgnoreCrashPtr != NULL)
|
||||
{
|
||||
int yn;
|
||||
if (!ignoringAsserts())
|
||||
{
|
||||
yn = MessageBoxWrapper("Ignore this crash from now on?", "", MB_YESNO|MB_TASKMODAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
yn = IDYES;
|
||||
}
|
||||
if (yn == IDYES)
|
||||
*TheCurrentIgnoreCrashPtr = 1;
|
||||
if( TheKeyboard )
|
||||
TheKeyboard->resetKeys();
|
||||
if( TheMouse )
|
||||
TheMouse->reset();
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DebugShutdown
|
||||
// ----------------------------------------------------------------------------
|
||||
#ifdef ALLOW_DEBUG_UTILS
|
||||
/**
|
||||
Shut down the debug utilities. This should be called once, as near to the
|
||||
end of the app as possible, after everything else (since other code will
|
||||
probably want to make use of it).
|
||||
*/
|
||||
void DebugShutdown()
|
||||
{
|
||||
#ifdef DEBUG_LOGGING
|
||||
if (theLogFile)
|
||||
{
|
||||
DebugLog("Log closed: %s\n", getCurrentTimeString());
|
||||
fclose(theLogFile);
|
||||
}
|
||||
theLogFile = NULL;
|
||||
#endif
|
||||
theDebugFlags = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DebugGetFlags
|
||||
// ----------------------------------------------------------------------------
|
||||
/**
|
||||
Get the current values for the flags passed to DebugInit. Most code will never
|
||||
need to use this; the most common usage would be to temporarily enable or disable
|
||||
the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
|
||||
*/
|
||||
int DebugGetFlags()
|
||||
{
|
||||
return theDebugFlags;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DebugSetFlags
|
||||
// ----------------------------------------------------------------------------
|
||||
/**
|
||||
Set the current values for the flags passed to DebugInit. Most code will never
|
||||
need to use this; the most common usage would be to temporarily enable or disable
|
||||
the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
|
||||
*/
|
||||
void DebugSetFlags(int flags)
|
||||
{
|
||||
theDebugFlags = flags;
|
||||
}
|
||||
|
||||
#endif // ALLOW_DEBUG_UTILS
|
||||
|
||||
#ifdef ALLOW_DEBUG_UTILS
|
||||
// ----------------------------------------------------------------------------
|
||||
SimpleProfiler::SimpleProfiler()
|
||||
{
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq);
|
||||
m_startThisSession = 0;
|
||||
m_totalThisSession = 0;
|
||||
m_totalAllSessions = 0;
|
||||
m_numSessions = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void SimpleProfiler::start()
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_startThisSession == 0, ("already started"));
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&m_startThisSession);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void SimpleProfiler::stop()
|
||||
{
|
||||
if (m_startThisSession != 0)
|
||||
{
|
||||
__int64 stop;
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&stop);
|
||||
m_totalThisSession = stop - m_startThisSession;
|
||||
m_totalAllSessions += stop - m_startThisSession;
|
||||
m_startThisSession = 0;
|
||||
++m_numSessions;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void SimpleProfiler::stopAndLog(const char *msg, int howOftenToLog, int howOftenToResetAvg)
|
||||
{
|
||||
stop();
|
||||
// howOftenToResetAvg==0 means "never reset"
|
||||
if (howOftenToResetAvg > 0 && m_numSessions >= howOftenToResetAvg)
|
||||
{
|
||||
m_numSessions = 0;
|
||||
m_totalAllSessions = 0;
|
||||
DEBUG_LOG(("%s: reset averages\n",msg));
|
||||
}
|
||||
DEBUG_ASSERTLOG(m_numSessions % howOftenToLog != 0, ("%s: %f msec, total %f msec, avg %f msec\n",msg,getTime(),getTotalTime(),getAverageTime()));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
double SimpleProfiler::getTime()
|
||||
{
|
||||
stop();
|
||||
return (double)m_totalThisSession / (double)m_freq * 1000.0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
int SimpleProfiler::getNumSessions()
|
||||
{
|
||||
stop();
|
||||
return m_numSessions;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
double SimpleProfiler::getTotalTime()
|
||||
{
|
||||
stop();
|
||||
if (!m_numSessions)
|
||||
return 0.0;
|
||||
|
||||
return (double)m_totalAllSessions * 1000.0 / ((double)m_freq);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
double SimpleProfiler::getAverageTime()
|
||||
{
|
||||
stop();
|
||||
if (!m_numSessions)
|
||||
return 0.0;
|
||||
|
||||
return (double)m_totalAllSessions * 1000.0 / ((double)m_freq * (double)m_numSessions);
|
||||
}
|
||||
|
||||
#endif // ALLOW_DEBUG_UTILS
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ReleaseCrash
|
||||
// ----------------------------------------------------------------------------
|
||||
/**
|
||||
Halt the application, EVEN IN FINAL RELEASE BUILDS. This should be called
|
||||
only when a crash is guaranteed by continuing, and no meaningful continuation
|
||||
of processing is possible, even by throwing an exception.
|
||||
*/
|
||||
|
||||
#define RELEASECRASH_FILE_NAME "ReleaseCrashInfo.txt"
|
||||
#define RELEASECRASH_FILE_NAME_PREV "ReleaseCrashInfoPrev.txt"
|
||||
|
||||
static FILE *theReleaseCrashLogFile = NULL;
|
||||
|
||||
static void releaseCrashLogOutput(const char *buffer)
|
||||
{
|
||||
if (theReleaseCrashLogFile)
|
||||
{
|
||||
fprintf(theReleaseCrashLogFile, "%s", buffer); // note, no \n (should be there already)
|
||||
fflush(theReleaseCrashLogFile);
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseCrash(const char *reason)
|
||||
{
|
||||
/// do additional reporting on the crash, if possible
|
||||
|
||||
|
||||
char prevbuf[ _MAX_PATH ];
|
||||
char curbuf[ _MAX_PATH ];
|
||||
|
||||
strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
|
||||
strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
|
||||
strcpy(curbuf, TheGlobalData->getPath_UserData().str());
|
||||
strcat(curbuf, RELEASECRASH_FILE_NAME);
|
||||
|
||||
remove(prevbuf);
|
||||
rename(curbuf, prevbuf);
|
||||
|
||||
theReleaseCrashLogFile = fopen(curbuf, "w");
|
||||
if (theReleaseCrashLogFile)
|
||||
{
|
||||
fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), reason);
|
||||
fprintf(theReleaseCrashLogFile, "\nLast error:\n%s\n\nCurrent stack:\n", g_LastErrorDump.str());
|
||||
const int STACKTRACE_SIZE = 12;
|
||||
const int STACKTRACE_SKIP = 6;
|
||||
void* stacktrace[STACKTRACE_SIZE];
|
||||
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
|
||||
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
|
||||
|
||||
fflush(theReleaseCrashLogFile);
|
||||
fclose(theReleaseCrashLogFile);
|
||||
theReleaseCrashLogFile = NULL;
|
||||
}
|
||||
|
||||
if (!DX8Wrapper_IsWindowed) {
|
||||
if (ApplicationHWnd) {
|
||||
ShowWindow(ApplicationHWnd, SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
/* static */ char buff[8192]; // not so static so we can be threadsafe
|
||||
_snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason);
|
||||
buff[8191] = 0;
|
||||
::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
|
||||
#else
|
||||
// crash error messaged changed 3/6/03 BGC
|
||||
// ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
|
||||
|
||||
if (!GetRegistryLanguage().compareNoCase("german2") || !GetRegistryLanguage().compareNoCase("german") )
|
||||
{
|
||||
::MessageBox(NULL, "Es ist ein gravierender Fehler aufgetreten. Solche Fehler k<>nnen durch viele verschiedene Dinge wie Viren, <20>berhitzte Hardware und Hardware, die den Mindestanforderungen des Spiels nicht entspricht, ausgel<65>st werden. Tipps zur Vorgehensweise findest du in den Foren unter www.generals.ea.com, Informationen zum Technischen Kundendienst im Handbuch zum Spiel.", "Fehler...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
void ReleaseCrashLocalized(const AsciiString& p, const AsciiString& m)
|
||||
{
|
||||
if (!TheGameText) {
|
||||
ReleaseCrash(m.str());
|
||||
// This won't ever return
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString prompt = TheGameText->fetch(p);
|
||||
UnicodeString mesg = TheGameText->fetch(m);
|
||||
|
||||
|
||||
/// do additional reporting on the crash, if possible
|
||||
|
||||
if (!DX8Wrapper_IsWindowed) {
|
||||
if (ApplicationHWnd) {
|
||||
ShowWindow(ApplicationHWnd, SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
if (TheSystemIsUnicode)
|
||||
{
|
||||
::MessageBoxW(NULL, mesg.str(), prompt.str(), MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
// However, if we're using the default version of the message box, we need to
|
||||
// translate the string into an AsciiString
|
||||
AsciiString promptA, mesgA;
|
||||
promptA.translate(prompt);
|
||||
mesgA.translate(mesg);
|
||||
//Make sure main window is not TOP_MOST
|
||||
::SetWindowPos(ApplicationHWnd, HWND_NOTOPMOST, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE);
|
||||
::MessageBoxA(NULL, mesgA.str(), promptA.str(), MB_OK|MB_TASKMODAL|MB_ICONERROR);
|
||||
}
|
||||
|
||||
char prevbuf[ _MAX_PATH ];
|
||||
char curbuf[ _MAX_PATH ];
|
||||
|
||||
strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
|
||||
strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
|
||||
strcpy(curbuf, TheGlobalData->getPath_UserData().str());
|
||||
strcat(curbuf, RELEASECRASH_FILE_NAME);
|
||||
|
||||
remove(prevbuf);
|
||||
rename(curbuf, prevbuf);
|
||||
|
||||
theReleaseCrashLogFile = fopen(curbuf, "w");
|
||||
if (theReleaseCrashLogFile)
|
||||
{
|
||||
fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), mesg.str());
|
||||
|
||||
const int STACKTRACE_SIZE = 12;
|
||||
const int STACKTRACE_SKIP = 6;
|
||||
void* stacktrace[STACKTRACE_SIZE];
|
||||
::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
|
||||
::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
|
||||
|
||||
fflush(theReleaseCrashLogFile);
|
||||
fclose(theReleaseCrashLogFile);
|
||||
theReleaseCrashLogFile = NULL;
|
||||
}
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
135
Generals/Code/GameEngine/Source/Common/System/Directory.cpp
Normal file
135
Generals/Code/GameEngine/Source/Common/System/Directory.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
#if (0)
|
||||
|
||||
#include "Common/Directory.h"
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
static void TimetToFileTime( time_t t, FILETIME& ft )
|
||||
{
|
||||
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
|
||||
ft.dwLowDateTime = (DWORD) ll;
|
||||
ft.dwHighDateTime = ll >>32;
|
||||
}
|
||||
|
||||
static time_t FileTimeToTimet( const FILETIME& ft )
|
||||
{
|
||||
LONGLONG ll = (ft.dwHighDateTime << 32) + ft.dwLowDateTime - 116444736000000000;
|
||||
ll /= 10000000;
|
||||
return (time_t)ll;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
void FileInfo::set( const WIN32_FIND_DATA& info )
|
||||
{
|
||||
filename = info.cFileName;
|
||||
creationTime = FileTimeToTimet(info.ftCreationTime);
|
||||
accessTime = FileTimeToTimet(info.ftLastAccessTime);
|
||||
modTime = FileTimeToTimet(info.ftLastWriteTime);
|
||||
attributes = info.dwFileAttributes;
|
||||
filesize = info.nFileSizeLow;
|
||||
//DEBUG_LOG(("FileInfo::set(): fname=%s, size=%d\n", filename.str(), filesize));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
Directory::Directory( const AsciiString& dirPath ) : m_dirPath(dirPath)
|
||||
{
|
||||
WIN32_FIND_DATA item; // search item
|
||||
HANDLE hFile; // handle for search resources
|
||||
char currDir[ MAX_PATH ];
|
||||
|
||||
// sanity
|
||||
if( m_dirPath.isEmpty() )
|
||||
{
|
||||
DEBUG_LOG(( "Empty dirname\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// save the current directory
|
||||
GetCurrentDirectory( MAX_PATH, currDir );
|
||||
|
||||
// switch into the directory provided
|
||||
if( SetCurrentDirectory( m_dirPath.str() ) == 0 )
|
||||
{
|
||||
DEBUG_LOG(( "Can't set directory '%s'\n", m_dirPath.str() ));
|
||||
return;
|
||||
}
|
||||
|
||||
// go through each item in the output directory
|
||||
bool done = false;
|
||||
hFile = FindFirstFile( "*", &item);
|
||||
if( hFile == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
DEBUG_LOG(( "Can't search directory '%s'\n", m_dirPath.str() ));
|
||||
done = true;
|
||||
}
|
||||
|
||||
FileInfo info;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
// if this is a subdirectory keep the name around till the end
|
||||
if( BitTest( item.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY ) )
|
||||
{
|
||||
if ( strcmp( item.cFileName, "." ) && strcmp( item.cFileName, ".." ) )
|
||||
{
|
||||
info.set(item);
|
||||
m_subdirs.insert( info );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info.set(item);
|
||||
m_files.insert( info );
|
||||
}
|
||||
|
||||
if ( FindNextFile( hFile, &item ) == 0 )
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
// close search
|
||||
FindClose( hFile );
|
||||
|
||||
// restore the working directory to what it was when we started here
|
||||
SetCurrentDirectory( currDir );
|
||||
}
|
||||
|
||||
FileInfoSet* Directory::getFiles( void )
|
||||
{
|
||||
return &m_files;
|
||||
}
|
||||
|
||||
FileInfoSet* Directory::getSubdirs( void )
|
||||
{
|
||||
return &m_subdirs;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// DisabledTypes.cpp /////////////////////////////////////////////////////////////////////////////////////
|
||||
// Kris Morness, September 2002
|
||||
|
||||
#include "PreRTS.h"
|
||||
|
||||
#include "Common/DisabledTypes.h"
|
||||
#include "Common/BitFlagsIO.h"
|
||||
|
||||
const char* DisabledMaskType::s_bitNameList[] =
|
||||
{
|
||||
"DEFAULT",
|
||||
"DISABLED_HACKED",
|
||||
"DISABLED_EMP",
|
||||
"DISABLED_HELD",
|
||||
"DISABLED_PARALYZED",
|
||||
"DISABLED_UNMANNED",
|
||||
"DISABLED_UNDERPOWERED",
|
||||
"DISABLED_FREEFALL",
|
||||
|
||||
"DISABLED_SCRIPT_DISABLED",
|
||||
"DISABLED_SCRIPT_UNDERPOWERED",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
DisabledMaskType DISABLEDMASK_NONE; // inits to all zeroes
|
||||
DisabledMaskType DISABLEDMASK_ALL;
|
||||
|
||||
void initDisabledMasks()
|
||||
{
|
||||
SET_ALL_DISABLEDMASK_BITS( DISABLEDMASK_ALL );
|
||||
}
|
||||
259
Generals/Code/GameEngine/Source/Common/System/File.cpp
Normal file
259
Generals/Code/GameEngine/Source/Common/System/File.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: WSYS Library
|
||||
//
|
||||
// Module: IO_
|
||||
//
|
||||
// File name: IO_File.cpp
|
||||
//
|
||||
// Created: 4/23/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "Common/File.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//=================================================================
|
||||
// File::File
|
||||
//=================================================================
|
||||
|
||||
File::File()
|
||||
: m_open(FALSE),
|
||||
m_deleteOnClose(FALSE),
|
||||
m_access(NONE)
|
||||
{
|
||||
|
||||
setName("<no file>");
|
||||
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//=================================================================
|
||||
// File::~File
|
||||
//=================================================================
|
||||
|
||||
File::~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// File::open
|
||||
//=================================================================
|
||||
/**
|
||||
* Any derived open() members must first call File::open. If File::open
|
||||
* succeeds but the derived class's open failes then make sure to call
|
||||
* File::close() before returning.
|
||||
*/
|
||||
//=================================================================
|
||||
|
||||
Bool File::open( const Char *filename, Int access )
|
||||
{
|
||||
if( m_open )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
setName( filename );
|
||||
|
||||
if( (access & ( STREAMING | WRITE )) == ( STREAMING | WRITE ))
|
||||
{
|
||||
// illegal access
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( (access & ( TEXT | BINARY)) == ( TEXT | BINARY ))
|
||||
{
|
||||
// illegal access
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( (access & (READ|WRITE)) == 0 )
|
||||
{
|
||||
access |= READ;
|
||||
}
|
||||
|
||||
if ( !(access & (READ|APPEND)) )
|
||||
{
|
||||
access |= TRUNCATE;
|
||||
}
|
||||
|
||||
if ( (access & (TEXT|BINARY)) == 0 )
|
||||
{
|
||||
access |= BINARY;
|
||||
}
|
||||
|
||||
m_access = access;
|
||||
m_open = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// File::close
|
||||
//=================================================================
|
||||
/**
|
||||
* Must call File::close() for each successful File::open() call.
|
||||
*/
|
||||
//=================================================================
|
||||
|
||||
void File::close( void )
|
||||
{
|
||||
if( m_open )
|
||||
{
|
||||
setName( "<no file>" );
|
||||
m_open = FALSE;
|
||||
if ( m_deleteOnClose )
|
||||
{
|
||||
this->deleteInstance(); // on special cases File object will delete itself when closing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// File::size
|
||||
//=================================================================
|
||||
/**
|
||||
* Default implementation of File::size. Derived classes can optimize
|
||||
* this member function.
|
||||
*/
|
||||
//=================================================================
|
||||
|
||||
Int File::size( void )
|
||||
{
|
||||
Int pos = seek( 0, CURRENT );
|
||||
Int size = seek( 0, END );
|
||||
|
||||
seek( pos, START );
|
||||
|
||||
return size < 0 ? 0 : size;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// File::position
|
||||
//============================================================================
|
||||
|
||||
Int File::position( void )
|
||||
{
|
||||
return seek(0, CURRENT);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// File::print
|
||||
//============================================================================
|
||||
|
||||
Bool File::print ( const Char *format, ...)
|
||||
{
|
||||
Char buffer[10*1024];
|
||||
Int len;
|
||||
|
||||
if ( ! (m_access & TEXT ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start( args, format ); /* Initialize variable arguments. */
|
||||
len = vsprintf( buffer, format, args );
|
||||
va_end( args );
|
||||
|
||||
if ( len >= sizeof(buffer) )
|
||||
{
|
||||
// Big Problem
|
||||
assert( FALSE );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return (write ( buffer, len ) == len);
|
||||
}
|
||||
|
||||
Bool File::eof() {
|
||||
return (position() == size());
|
||||
}
|
||||
324
Generals/Code/GameEngine/Source/Common/System/FileSystem.cpp
Normal file
324
Generals/Code/GameEngine/Source/Common/System/FileSystem.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: WSYS Library
|
||||
//
|
||||
// Module: IO
|
||||
//
|
||||
// File name: IO_FileSystem.cpp
|
||||
//
|
||||
// Created: 4/23/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "Common/File.h"
|
||||
#include "Common/FileSystem.h"
|
||||
|
||||
#include "Common/ArchiveFileSystem.h"
|
||||
#include "Common/CDManager.h"
|
||||
#include "Common/GameAudio.h"
|
||||
#include "Common/LocalFileSystem.h"
|
||||
#include "Common/PerfTimer.h"
|
||||
|
||||
|
||||
DECLARE_PERF_TIMER(FileSystem)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//===============================
|
||||
// TheFileSystem
|
||||
//===============================
|
||||
/**
|
||||
* This is the FileSystem's singleton class. All file access
|
||||
* should be through TheFileSystem, unless code needs to use an explicit
|
||||
* File or FileSystem derivative.
|
||||
*
|
||||
* Using TheFileSystem->open and File exclusively for file access, particularly
|
||||
* in library or modular code, allows applications to transparently implement
|
||||
* file access as they see fit. This is particularly important for code that
|
||||
* needs to be shared between applications, such as games and tools.
|
||||
*/
|
||||
//===============================
|
||||
|
||||
FileSystem *TheFileSystem = NULL;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::FileSystem
|
||||
//============================================================================
|
||||
|
||||
FileSystem::FileSystem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::~FileSystem
|
||||
//============================================================================
|
||||
|
||||
FileSystem::~FileSystem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::init
|
||||
//============================================================================
|
||||
|
||||
void FileSystem::init( void )
|
||||
{
|
||||
TheLocalFileSystem->init();
|
||||
TheArchiveFileSystem->init();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::update
|
||||
//============================================================================
|
||||
|
||||
void FileSystem::update( void )
|
||||
{
|
||||
USE_PERF_TIMER(FileSystem)
|
||||
TheLocalFileSystem->update();
|
||||
TheArchiveFileSystem->update();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::reset
|
||||
//============================================================================
|
||||
|
||||
void FileSystem::reset( void )
|
||||
{
|
||||
USE_PERF_TIMER(FileSystem)
|
||||
TheLocalFileSystem->reset();
|
||||
TheArchiveFileSystem->reset();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::open
|
||||
//============================================================================
|
||||
|
||||
File* FileSystem::openFile( const Char *filename, Int access )
|
||||
{
|
||||
USE_PERF_TIMER(FileSystem)
|
||||
File *file = NULL;
|
||||
|
||||
if ( TheLocalFileSystem != NULL )
|
||||
{
|
||||
file = TheLocalFileSystem->openFile( filename, access );
|
||||
}
|
||||
|
||||
if ( (TheArchiveFileSystem != NULL) && (file == NULL) )
|
||||
{
|
||||
file = TheArchiveFileSystem->openFile( filename );
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::doesFileExist
|
||||
//============================================================================
|
||||
|
||||
Bool FileSystem::doesFileExist(const Char *filename) const
|
||||
{
|
||||
USE_PERF_TIMER(FileSystem)
|
||||
if (TheLocalFileSystem->doesFileExist(filename)) {
|
||||
return TRUE;
|
||||
}
|
||||
if (TheArchiveFileSystem->doesFileExist(filename)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::getFileListInDirectory
|
||||
//============================================================================
|
||||
void FileSystem::getFileListInDirectory(const AsciiString& directory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
|
||||
{
|
||||
USE_PERF_TIMER(FileSystem)
|
||||
TheLocalFileSystem->getFileListInDirectory(AsciiString(""), directory, searchName, filenameList, searchSubdirectories);
|
||||
TheArchiveFileSystem->getFileListInDirectory(AsciiString(""), directory, searchName, filenameList, searchSubdirectories);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::getFileInfo
|
||||
//============================================================================
|
||||
Bool FileSystem::getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const
|
||||
{
|
||||
USE_PERF_TIMER(FileSystem)
|
||||
if (fileInfo == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
memset(fileInfo, 0, sizeof(fileInfo));
|
||||
|
||||
if (TheLocalFileSystem->getFileInfo(filename, fileInfo)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (TheArchiveFileSystem->getFileInfo(filename, fileInfo)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::createDirectory
|
||||
//============================================================================
|
||||
Bool FileSystem::createDirectory(AsciiString directory)
|
||||
{
|
||||
USE_PERF_TIMER(FileSystem)
|
||||
if (TheLocalFileSystem != NULL) {
|
||||
return TheLocalFileSystem->createDirectory(directory);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::areMusicFilesOnCD
|
||||
//============================================================================
|
||||
Bool FileSystem::areMusicFilesOnCD()
|
||||
{
|
||||
#if 1
|
||||
return TRUE;
|
||||
#else
|
||||
if (!TheCDManager) {
|
||||
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - No CD Manager; returning false\n"));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
AsciiString cdRoot;
|
||||
Int dc = TheCDManager->driveCount();
|
||||
for (Int i = 0; i < dc; ++i) {
|
||||
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - checking drive %d\n", i));
|
||||
CDDriveInterface *cdi = TheCDManager->getDrive(i);
|
||||
if (!cdi) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cdRoot = cdi->getPath();
|
||||
if (!cdRoot.endsWith("\\"))
|
||||
cdRoot.concat("\\");
|
||||
cdRoot.concat("gensec.big");
|
||||
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - checking for %s\n", cdRoot.str()));
|
||||
File *musicBig = TheLocalFileSystem->openFile(cdRoot.str());
|
||||
if (musicBig)
|
||||
{
|
||||
DEBUG_LOG(("FileSystem::areMusicFilesOnCD() - found it!\n"));
|
||||
musicBig->close();
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
//============================================================================
|
||||
// FileSystem::loadMusicFilesFromCD
|
||||
//============================================================================
|
||||
void FileSystem::loadMusicFilesFromCD()
|
||||
{
|
||||
if (!TheCDManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
AsciiString cdRoot;
|
||||
Int dc = TheCDManager->driveCount();
|
||||
for (Int i = 0; i < dc; ++i) {
|
||||
CDDriveInterface *cdi = TheCDManager->getDrive(i);
|
||||
if (!cdi) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cdRoot = cdi->getPath();
|
||||
if (TheArchiveFileSystem->loadBigFilesFromDirectory(cdRoot, MUSIC_BIG)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// FileSystem::unloadMusicFilesFromCD
|
||||
//============================================================================
|
||||
void FileSystem::unloadMusicFilesFromCD()
|
||||
{
|
||||
if (!(TheAudio && TheAudio->isMusicPlayingFromCD())) {
|
||||
return;
|
||||
}
|
||||
|
||||
TheArchiveFileSystem->closeArchiveFile( MUSIC_BIG );
|
||||
}
|
||||
@@ -0,0 +1,711 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: FunctionLexicon.cpp //////////////////////////////////////////////////////////////////////
|
||||
// Created: Colin Day, September 2001
|
||||
// Desc: Collection of function pointers to help us in managing
|
||||
// and assign callbacks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/FunctionLexicon.h"
|
||||
#include "GameClient/GameWindow.h"
|
||||
#include "GameClient/GameWindowManager.h"
|
||||
#include "GameClient/GUICallbacks.h"
|
||||
#include "GameClient/Gadget.h"
|
||||
|
||||
// Popup Ladder Select --------------------------------------------------------------------------
|
||||
extern void PopupLadderSelectInit( WindowLayout *layout, void *userData );
|
||||
extern WindowMsgHandledType PopupLadderSelectSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
extern WindowMsgHandledType PopupLadderSelectInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
|
||||
extern WindowMsgHandledType PopupBuddyNotificationSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
|
||||
// WOL Buddy Overlay Right Click menu callbacks --------------------------------------------------------------
|
||||
extern void RCGameDetailsMenuInit( WindowLayout *layout, void *userData );
|
||||
extern WindowMsgHandledType RCGameDetailsMenuSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
|
||||
// Beacon control bar callback --------------------------------------------------------------
|
||||
extern WindowMsgHandledType BeaconWindowInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
|
||||
// Popup Replay Save Menu ----------------------------------------------------------------------------------
|
||||
extern void PopupReplayInit( WindowLayout *layout, void *userData );
|
||||
extern void PopupReplayUpdate( WindowLayout *layout, void *userData );
|
||||
extern void PopupReplayShutdown( WindowLayout *layout, void *userData );
|
||||
extern WindowMsgHandledType PopupReplaySystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
extern WindowMsgHandledType PopupReplayInput( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
|
||||
// Extended MessageBox ----------------------------------------------------------------------------------
|
||||
extern WindowMsgHandledType ExtendedMessageBoxSystem( GameWindow *window, UnsignedInt msg, WindowMsgData mData1, WindowMsgData mData2 );
|
||||
|
||||
// game window draw table -----------------------------------------------------------------------
|
||||
static FunctionLexicon::TableEntry gameWinDrawTable[] =
|
||||
{
|
||||
{ NAMEKEY_INVALID, "IMECandidateMainDraw", IMECandidateMainDraw },
|
||||
{ NAMEKEY_INVALID, "IMECandidateTextAreaDraw", IMECandidateTextAreaDraw },
|
||||
{ NAMEKEY_INVALID, NULL, NULL }
|
||||
};
|
||||
|
||||
// game window system table -----------------------------------------------------------------------
|
||||
static FunctionLexicon::TableEntry gameWinSystemTable[] =
|
||||
{
|
||||
|
||||
|
||||
{ NAMEKEY_INVALID, "PassSelectedButtonsToParentSystem", PassSelectedButtonsToParentSystem },
|
||||
{ NAMEKEY_INVALID, "PassMessagesToParentSystem", PassMessagesToParentSystem },
|
||||
|
||||
{ NAMEKEY_INVALID, "GameWinDefaultSystem", GameWinDefaultSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetPushButtonSystem", GadgetPushButtonSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetCheckBoxSystem", GadgetCheckBoxSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetRadioButtonSystem", GadgetRadioButtonSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetTabControlSystem", GadgetTabControlSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetListBoxSystem", GadgetListBoxSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetComboBoxSystem", GadgetComboBoxSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetHorizontalSliderSystem", GadgetHorizontalSliderSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetVerticalSliderSystem", GadgetVerticalSliderSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetProgressBarSystem", GadgetProgressBarSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetStaticTextSystem", GadgetStaticTextSystem },
|
||||
{ NAMEKEY_INVALID, "GadgetTextEntrySystem", GadgetTextEntrySystem },
|
||||
{ NAMEKEY_INVALID, "MessageBoxSystem", MessageBoxSystem },
|
||||
{ NAMEKEY_INVALID, "QuitMessageBoxSystem", QuitMessageBoxSystem },
|
||||
|
||||
{ NAMEKEY_INVALID, "ExtendedMessageBoxSystem", ExtendedMessageBoxSystem },
|
||||
|
||||
{ NAMEKEY_INVALID, "MOTDSystem", MOTDSystem },
|
||||
{ NAMEKEY_INVALID, "MainMenuSystem", MainMenuSystem },
|
||||
{ NAMEKEY_INVALID, "OptionsMenuSystem", OptionsMenuSystem },
|
||||
{ NAMEKEY_INVALID, "SinglePlayerMenuSystem", SinglePlayerMenuSystem },
|
||||
{ NAMEKEY_INVALID, "QuitMenuSystem", QuitMenuSystem },
|
||||
{ NAMEKEY_INVALID, "MapSelectMenuSystem", MapSelectMenuSystem },
|
||||
{ NAMEKEY_INVALID, "ReplayMenuSystem", ReplayMenuSystem },
|
||||
{ NAMEKEY_INVALID, "CreditsMenuSystem", CreditsMenuSystem },
|
||||
{ NAMEKEY_INVALID, "LanLobbyMenuSystem", LanLobbyMenuSystem },
|
||||
{ NAMEKEY_INVALID, "LanGameOptionsMenuSystem", LanGameOptionsMenuSystem },
|
||||
{ NAMEKEY_INVALID, "LanMapSelectMenuSystem", LanMapSelectMenuSystem },
|
||||
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuSystem", SkirmishGameOptionsMenuSystem },
|
||||
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuSystem", SkirmishMapSelectMenuSystem },
|
||||
{ NAMEKEY_INVALID, "SaveLoadMenuSystem", SaveLoadMenuSystem },
|
||||
{ NAMEKEY_INVALID, "PopupCommunicatorSystem", PopupCommunicatorSystem },
|
||||
{ NAMEKEY_INVALID, "PopupBuddyNotificationSystem", PopupBuddyNotificationSystem },
|
||||
{ NAMEKEY_INVALID, "PopupReplaySystem", PopupReplaySystem },
|
||||
{ NAMEKEY_INVALID, "KeyboardOptionsMenuSystem", KeyboardOptionsMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLLadderScreenSystem", WOLLadderScreenSystem },
|
||||
{ NAMEKEY_INVALID, "WOLLoginMenuSystem", WOLLoginMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLLocaleSelectSystem", WOLLocaleSelectSystem },
|
||||
{ NAMEKEY_INVALID, "WOLLobbyMenuSystem", WOLLobbyMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLGameSetupMenuSystem", WOLGameSetupMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLMapSelectMenuSystem", WOLMapSelectMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLBuddyOverlaySystem", WOLBuddyOverlaySystem },
|
||||
{ NAMEKEY_INVALID, "WOLBuddyOverlayRCMenuSystem", WOLBuddyOverlayRCMenuSystem },
|
||||
{ NAMEKEY_INVALID, "RCGameDetailsMenuSystem", RCGameDetailsMenuSystem },
|
||||
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlaySystem",GameSpyPlayerInfoOverlaySystem },
|
||||
{ NAMEKEY_INVALID, "WOLMessageWindowSystem", WOLMessageWindowSystem },
|
||||
{ NAMEKEY_INVALID, "WOLQuickMatchMenuSystem", WOLQuickMatchMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLWelcomeMenuSystem", WOLWelcomeMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLStatusMenuSystem", WOLStatusMenuSystem },
|
||||
{ NAMEKEY_INVALID, "WOLQMScoreScreenSystem", WOLQMScoreScreenSystem },
|
||||
{ NAMEKEY_INVALID, "WOLCustomScoreScreenSystem", WOLCustomScoreScreenSystem },
|
||||
{ NAMEKEY_INVALID, "NetworkDirectConnectSystem", NetworkDirectConnectSystem },
|
||||
{ NAMEKEY_INVALID, "PopupHostGameSystem", PopupHostGameSystem },
|
||||
{ NAMEKEY_INVALID, "PopupJoinGameSystem", PopupJoinGameSystem },
|
||||
{ NAMEKEY_INVALID, "PopupLadderSelectSystem", PopupLadderSelectSystem },
|
||||
{ NAMEKEY_INVALID, "InGamePopupMessageSystem", InGamePopupMessageSystem },
|
||||
{ NAMEKEY_INVALID, "ControlBarSystem", ControlBarSystem },
|
||||
{ NAMEKEY_INVALID, "ControlBarObserverSystem", ControlBarObserverSystem },
|
||||
{ NAMEKEY_INVALID, "IMECandidateWindowSystem", IMECandidateWindowSystem },
|
||||
{ NAMEKEY_INVALID, "ReplayControlSystem", ReplayControlSystem },
|
||||
{ NAMEKEY_INVALID, "InGameChatSystem", InGameChatSystem },
|
||||
{ NAMEKEY_INVALID, "DisconnectControlSystem", DisconnectControlSystem },
|
||||
{ NAMEKEY_INVALID, "DiplomacySystem", DiplomacySystem },
|
||||
{ NAMEKEY_INVALID, "GeneralsExpPointsSystem", GeneralsExpPointsSystem },
|
||||
{ NAMEKEY_INVALID, "DifficultySelectSystem", DifficultySelectSystem },
|
||||
|
||||
{ NAMEKEY_INVALID, "IdleWorkerSystem", IdleWorkerSystem },
|
||||
{ NAMEKEY_INVALID, "EstablishConnectionsControlSystem", EstablishConnectionsControlSystem },
|
||||
{ NAMEKEY_INVALID, "GameInfoWindowSystem", GameInfoWindowSystem },
|
||||
{ NAMEKEY_INVALID, "ScoreScreenSystem", ScoreScreenSystem },
|
||||
{ NAMEKEY_INVALID, "DownloadMenuSystem", DownloadMenuSystem },
|
||||
|
||||
{ NAMEKEY_INVALID, NULL, NULL }
|
||||
|
||||
};
|
||||
|
||||
// game window input table ------------------------------------------------------------------------
|
||||
static FunctionLexicon::TableEntry gameWinInputTable[] =
|
||||
{
|
||||
|
||||
{ NAMEKEY_INVALID, "GameWinDefaultInput", GameWinDefaultInput },
|
||||
{ NAMEKEY_INVALID, "GameWinBlockInput", GameWinBlockInput },
|
||||
{ NAMEKEY_INVALID, "GadgetPushButtonInput", GadgetPushButtonInput },
|
||||
{ NAMEKEY_INVALID, "GadgetCheckBoxInput", GadgetCheckBoxInput },
|
||||
{ NAMEKEY_INVALID, "GadgetRadioButtonInput", GadgetRadioButtonInput },
|
||||
{ NAMEKEY_INVALID, "GadgetTabControlInput", GadgetTabControlInput },
|
||||
{ NAMEKEY_INVALID, "GadgetListBoxInput", GadgetListBoxInput },
|
||||
{ NAMEKEY_INVALID, "GadgetListBoxMultiInput", GadgetListBoxMultiInput },
|
||||
{ NAMEKEY_INVALID, "GadgetComboBoxInput", GadgetComboBoxInput },
|
||||
{ NAMEKEY_INVALID, "GadgetHorizontalSliderInput", GadgetHorizontalSliderInput },
|
||||
{ NAMEKEY_INVALID, "GadgetVerticalSliderInput", GadgetVerticalSliderInput },
|
||||
{ NAMEKEY_INVALID, "GadgetStaticTextInput", GadgetStaticTextInput },
|
||||
{ NAMEKEY_INVALID, "GadgetTextEntryInput", GadgetTextEntryInput },
|
||||
|
||||
{ NAMEKEY_INVALID, "MainMenuInput", MainMenuInput },
|
||||
{ NAMEKEY_INVALID, "MapSelectMenuInput", MapSelectMenuInput },
|
||||
{ NAMEKEY_INVALID, "OptionsMenuInput", OptionsMenuInput },
|
||||
{ NAMEKEY_INVALID, "SinglePlayerMenuInput", SinglePlayerMenuInput },
|
||||
{ NAMEKEY_INVALID, "LanLobbyMenuInput", LanLobbyMenuInput },
|
||||
{ NAMEKEY_INVALID, "ReplayMenuInput", ReplayMenuInput },
|
||||
{ NAMEKEY_INVALID, "CreditsMenuInput", CreditsMenuInput },
|
||||
{ NAMEKEY_INVALID, "KeyboardOptionsMenuInput", KeyboardOptionsMenuInput },
|
||||
{ NAMEKEY_INVALID, "PopupCommunicatorInput", PopupCommunicatorInput },
|
||||
{ NAMEKEY_INVALID, "LanGameOptionsMenuInput", LanGameOptionsMenuInput },
|
||||
{ NAMEKEY_INVALID, "LanMapSelectMenuInput", LanMapSelectMenuInput },
|
||||
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuInput", SkirmishGameOptionsMenuInput },
|
||||
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuInput", SkirmishMapSelectMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLLadderScreenInput", WOLLadderScreenInput },
|
||||
{ NAMEKEY_INVALID, "WOLLoginMenuInput", WOLLoginMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLLocaleSelectInput", WOLLocaleSelectInput },
|
||||
{ NAMEKEY_INVALID, "WOLLobbyMenuInput", WOLLobbyMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLGameSetupMenuInput", WOLGameSetupMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLMapSelectMenuInput", WOLMapSelectMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLBuddyOverlayInput", WOLBuddyOverlayInput },
|
||||
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayInput", GameSpyPlayerInfoOverlayInput },
|
||||
{ NAMEKEY_INVALID, "WOLMessageWindowInput", WOLMessageWindowInput },
|
||||
{ NAMEKEY_INVALID, "WOLQuickMatchMenuInput", WOLQuickMatchMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLWelcomeMenuInput", WOLWelcomeMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLStatusMenuInput", WOLStatusMenuInput },
|
||||
{ NAMEKEY_INVALID, "WOLQMScoreScreenInput", WOLQMScoreScreenInput },
|
||||
{ NAMEKEY_INVALID, "WOLCustomScoreScreenInput", WOLCustomScoreScreenInput },
|
||||
{ NAMEKEY_INVALID, "NetworkDirectConnectInput", NetworkDirectConnectInput },
|
||||
{ NAMEKEY_INVALID, "PopupHostGameInput", PopupHostGameInput },
|
||||
{ NAMEKEY_INVALID, "PopupJoinGameInput", PopupJoinGameInput },
|
||||
{ NAMEKEY_INVALID, "PopupLadderSelectInput", PopupLadderSelectInput },
|
||||
{ NAMEKEY_INVALID, "InGamePopupMessageInput", InGamePopupMessageInput },
|
||||
{ NAMEKEY_INVALID, "ControlBarInput", ControlBarInput },
|
||||
{ NAMEKEY_INVALID, "ReplayControlInput", ReplayControlInput },
|
||||
{ NAMEKEY_INVALID, "InGameChatInput", InGameChatInput },
|
||||
{ NAMEKEY_INVALID, "DisconnectControlInput", DisconnectControlInput },
|
||||
{ NAMEKEY_INVALID, "DiplomacyInput", DiplomacyInput },
|
||||
{ NAMEKEY_INVALID, "EstablishConnectionsControlInput", EstablishConnectionsControlInput },
|
||||
{ NAMEKEY_INVALID, "LeftHUDInput", LeftHUDInput },
|
||||
{ NAMEKEY_INVALID, "ScoreScreenInput", ScoreScreenInput },
|
||||
{ NAMEKEY_INVALID, "SaveLoadMenuInput", SaveLoadMenuInput },
|
||||
{ NAMEKEY_INVALID, "BeaconWindowInput", BeaconWindowInput },
|
||||
{ NAMEKEY_INVALID, "DifficultySelectInput", DifficultySelectInput },
|
||||
{ NAMEKEY_INVALID, "PopupReplayInput", PopupReplayInput },
|
||||
{ NAMEKEY_INVALID, "GeneralsExpPointsInput", GeneralsExpPointsInput},
|
||||
|
||||
{ NAMEKEY_INVALID, "DownloadMenuInput", DownloadMenuInput },
|
||||
|
||||
{ NAMEKEY_INVALID, "IMECandidateWindowInput", IMECandidateWindowInput },
|
||||
{ NAMEKEY_INVALID, NULL, NULL }
|
||||
|
||||
};
|
||||
|
||||
// game window tooltip table ----------------------------------------------------------------------
|
||||
static FunctionLexicon::TableEntry gameWinTooltipTable[] =
|
||||
{
|
||||
|
||||
|
||||
{ NAMEKEY_INVALID, "GameWinDefaultTooltip", GameWinDefaultTooltip },
|
||||
|
||||
{ NAMEKEY_INVALID, NULL, NULL }
|
||||
|
||||
};
|
||||
|
||||
// window layout init table -----------------------------------------------------------------------
|
||||
static FunctionLexicon::TableEntry winLayoutInitTable[] =
|
||||
{
|
||||
|
||||
{ NAMEKEY_INVALID, "MainMenuInit", MainMenuInit },
|
||||
{ NAMEKEY_INVALID, "OptionsMenuInit", OptionsMenuInit },
|
||||
{ NAMEKEY_INVALID, "SaveLoadMenuInit", SaveLoadMenuInit },
|
||||
{ NAMEKEY_INVALID, "SaveLoadMenuFullScreenInit", SaveLoadMenuFullScreenInit },
|
||||
|
||||
{ NAMEKEY_INVALID, "PopupCommunicatorInit", PopupCommunicatorInit },
|
||||
{ NAMEKEY_INVALID, "KeyboardOptionsMenuInit", KeyboardOptionsMenuInit },
|
||||
{ NAMEKEY_INVALID, "SinglePlayerMenuInit", SinglePlayerMenuInit },
|
||||
{ NAMEKEY_INVALID, "MapSelectMenuInit", MapSelectMenuInit },
|
||||
{ NAMEKEY_INVALID, "LanLobbyMenuInit", LanLobbyMenuInit },
|
||||
{ NAMEKEY_INVALID, "ReplayMenuInit", ReplayMenuInit },
|
||||
{ NAMEKEY_INVALID, "CreditsMenuInit", CreditsMenuInit },
|
||||
{ NAMEKEY_INVALID, "LanGameOptionsMenuInit", LanGameOptionsMenuInit },
|
||||
{ NAMEKEY_INVALID, "LanMapSelectMenuInit", LanMapSelectMenuInit },
|
||||
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuInit", SkirmishGameOptionsMenuInit },
|
||||
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuInit", SkirmishMapSelectMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLLadderScreenInit", WOLLadderScreenInit },
|
||||
{ NAMEKEY_INVALID, "WOLLoginMenuInit", WOLLoginMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLLocaleSelectInit", WOLLocaleSelectInit },
|
||||
{ NAMEKEY_INVALID, "WOLLobbyMenuInit", WOLLobbyMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLGameSetupMenuInit", WOLGameSetupMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLMapSelectMenuInit", WOLMapSelectMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLBuddyOverlayInit", WOLBuddyOverlayInit },
|
||||
{ NAMEKEY_INVALID, "WOLBuddyOverlayRCMenuInit", WOLBuddyOverlayRCMenuInit },
|
||||
{ NAMEKEY_INVALID, "RCGameDetailsMenuInit", RCGameDetailsMenuInit },
|
||||
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayInit", GameSpyPlayerInfoOverlayInit },
|
||||
{ NAMEKEY_INVALID, "WOLMessageWindowInit", WOLMessageWindowInit },
|
||||
{ NAMEKEY_INVALID, "WOLQuickMatchMenuInit", WOLQuickMatchMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLWelcomeMenuInit", WOLWelcomeMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLStatusMenuInit", WOLStatusMenuInit },
|
||||
{ NAMEKEY_INVALID, "WOLQMScoreScreenInit", WOLQMScoreScreenInit },
|
||||
{ NAMEKEY_INVALID, "WOLCustomScoreScreenInit", WOLCustomScoreScreenInit },
|
||||
{ NAMEKEY_INVALID, "NetworkDirectConnectInit", NetworkDirectConnectInit },
|
||||
{ NAMEKEY_INVALID, "PopupHostGameInit", PopupHostGameInit },
|
||||
{ NAMEKEY_INVALID, "PopupJoinGameInit", PopupJoinGameInit },
|
||||
{ NAMEKEY_INVALID, "PopupLadderSelectInit", PopupLadderSelectInit },
|
||||
{ NAMEKEY_INVALID, "InGamePopupMessageInit", InGamePopupMessageInit },
|
||||
{ NAMEKEY_INVALID, "GameInfoWindowInit", GameInfoWindowInit },
|
||||
{ NAMEKEY_INVALID, "ScoreScreenInit", ScoreScreenInit },
|
||||
{ NAMEKEY_INVALID, "DownloadMenuInit", DownloadMenuInit },
|
||||
{ NAMEKEY_INVALID, "DifficultySelectInit", DifficultySelectInit },
|
||||
{ NAMEKEY_INVALID, "PopupReplayInit", PopupReplayInit },
|
||||
|
||||
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
|
||||
|
||||
};
|
||||
|
||||
// window layout update table ---------------------------------------------------------------------
|
||||
static FunctionLexicon::TableEntry winLayoutUpdateTable[] =
|
||||
{
|
||||
|
||||
{ NAMEKEY_INVALID, "MainMenuUpdate", MainMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "OptionsMenuUpdate", OptionsMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "SinglePlayerMenuUpdate", SinglePlayerMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "MapSelectMenuUpdate", MapSelectMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "LanLobbyMenuUpdate", LanLobbyMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "ReplayMenuUpdate", ReplayMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "SaveLoadMenuUpdate", SaveLoadMenuUpdate },
|
||||
|
||||
{ NAMEKEY_INVALID, "CreditsMenuUpdate", CreditsMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "LanGameOptionsMenuUpdate", LanGameOptionsMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "LanMapSelectMenuUpdate", LanMapSelectMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuUpdate", SkirmishGameOptionsMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuUpdate", SkirmishMapSelectMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLLadderScreenUpdate", WOLLadderScreenUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLLoginMenuUpdate", WOLLoginMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLLocaleSelectUpdate", WOLLocaleSelectUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLLobbyMenuUpdate", WOLLobbyMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLGameSetupMenuUpdate", WOLGameSetupMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLMapSelectMenuUpdate", WOLMapSelectMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLBuddyOverlayUpdate", WOLBuddyOverlayUpdate },
|
||||
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayUpdate",GameSpyPlayerInfoOverlayUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLMessageWindowUpdate", WOLMessageWindowUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLQuickMatchMenuUpdate", WOLQuickMatchMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLWelcomeMenuUpdate", WOLWelcomeMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLStatusMenuUpdate", WOLStatusMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLQMScoreScreenUpdate", WOLQMScoreScreenUpdate },
|
||||
{ NAMEKEY_INVALID, "WOLCustomScoreScreenUpdate", WOLCustomScoreScreenUpdate },
|
||||
{ NAMEKEY_INVALID, "NetworkDirectConnectUpdate", NetworkDirectConnectUpdate },
|
||||
{ NAMEKEY_INVALID, "ScoreScreenUpdate", ScoreScreenUpdate },
|
||||
{ NAMEKEY_INVALID, "DownloadMenuUpdate", DownloadMenuUpdate },
|
||||
{ NAMEKEY_INVALID, "PopupReplayUpdate", PopupReplayUpdate },
|
||||
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
|
||||
|
||||
};
|
||||
|
||||
// window layout shutdown table -------------------------------------------------------------------
|
||||
static FunctionLexicon::TableEntry winLayoutShutdownTable[] =
|
||||
{
|
||||
|
||||
{ NAMEKEY_INVALID, "MainMenuShutdown", MainMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "OptionsMenuShutdown", OptionsMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "SaveLoadMenuShutdown", SaveLoadMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "PopupCommunicatorShutdown", PopupCommunicatorShutdown },
|
||||
{ NAMEKEY_INVALID, "KeyboardOptionsMenuShutdown", KeyboardOptionsMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "SinglePlayerMenuShutdown", SinglePlayerMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "MapSelectMenuShutdown", MapSelectMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "LanLobbyMenuShutdown", LanLobbyMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "ReplayMenuShutdown", ReplayMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "CreditsMenuShutdown", CreditsMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "LanGameOptionsMenuShutdown", LanGameOptionsMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "LanMapSelectMenuShutdown", LanMapSelectMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "SkirmishGameOptionsMenuShutdown",SkirmishGameOptionsMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "SkirmishMapSelectMenuShutdown", SkirmishMapSelectMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLLadderScreenShutdown", WOLLadderScreenShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLLoginMenuShutdown", WOLLoginMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLLocaleSelectShutdown", WOLLocaleSelectShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLLobbyMenuShutdown", WOLLobbyMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLGameSetupMenuShutdown", WOLGameSetupMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLMapSelectMenuShutdown", WOLMapSelectMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLBuddyOverlayShutdown", WOLBuddyOverlayShutdown },
|
||||
{ NAMEKEY_INVALID, "GameSpyPlayerInfoOverlayShutdown",GameSpyPlayerInfoOverlayShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLMessageWindowShutdown", WOLMessageWindowShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLQuickMatchMenuShutdown", WOLQuickMatchMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLWelcomeMenuShutdown", WOLWelcomeMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLStatusMenuShutdown", WOLStatusMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLQMScoreScreenShutdown", WOLQMScoreScreenShutdown },
|
||||
{ NAMEKEY_INVALID, "WOLCustomScoreScreenShutdown", WOLCustomScoreScreenShutdown },
|
||||
{ NAMEKEY_INVALID, "NetworkDirectConnectShutdown", NetworkDirectConnectShutdown },
|
||||
{ NAMEKEY_INVALID, "ScoreScreenShutdown", ScoreScreenShutdown },
|
||||
{ NAMEKEY_INVALID, "DownloadMenuShutdown", DownloadMenuShutdown },
|
||||
{ NAMEKEY_INVALID, "PopupReplayShutdown", PopupReplayShutdown },
|
||||
{ NAMEKEY_INVALID, NULL, NULL } // keep this last
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC DATA
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
FunctionLexicon *TheFunctionLexicon = NULL; ///< the function dictionary
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Since we have a convenient table to organize our callbacks anyway,
|
||||
* we'll just use this same storage space to load in any run time
|
||||
* components we might want to add to the table, such as generating
|
||||
* a key based off the name supplied in the table for faster access */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FunctionLexicon::loadTable( TableEntry *table,
|
||||
TableIndex tableIndex )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( table == NULL )
|
||||
return;
|
||||
|
||||
// loop through all entries
|
||||
TableEntry *entry = table;
|
||||
while( entry->name )
|
||||
{
|
||||
|
||||
// assign key from name key based on name provided in table
|
||||
entry->key = TheNameKeyGenerator->nameToKey( AsciiString(entry->name) );
|
||||
|
||||
// next table entry please
|
||||
entry++;
|
||||
|
||||
} // end while
|
||||
|
||||
// assign table to the index specified
|
||||
m_tables[ tableIndex ] = table;
|
||||
|
||||
} // end loadTable
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Search the provided table for a function matching the key */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void *FunctionLexicon::keyToFunc( NameKeyType key, TableEntry *table )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( key == NAMEKEY_INVALID )
|
||||
return NULL;
|
||||
|
||||
// search table for key
|
||||
TableEntry *entry = table;
|
||||
while( entry && entry->key != NAMEKEY_INVALID )
|
||||
{
|
||||
|
||||
if( entry->key == key )
|
||||
return entry->func;
|
||||
entry++;
|
||||
|
||||
} // end if
|
||||
|
||||
return NULL; // not found
|
||||
|
||||
} // end keyToFunc
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Search tables for the function given this key, if the index parameter
|
||||
* is TABLE_ANY, then ALL tables will be searched. Otherwise index refers
|
||||
* to only a single table index to be searched */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void *FunctionLexicon::findFunction( NameKeyType key, TableIndex index )
|
||||
{
|
||||
void *func = NULL;
|
||||
|
||||
// sanity
|
||||
if( key == NAMEKEY_INVALID )
|
||||
return NULL;
|
||||
|
||||
// search ALL tables for function if the index paramater allows if
|
||||
if( index == TABLE_ANY )
|
||||
{
|
||||
|
||||
Int i;
|
||||
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
|
||||
{
|
||||
|
||||
func = keyToFunc( key, m_tables[ i ] );
|
||||
if( func )
|
||||
break; // exit for i
|
||||
|
||||
} // end for i
|
||||
|
||||
} // end if
|
||||
else
|
||||
{
|
||||
|
||||
// do NOT search all tables, just the one specified by the parameter
|
||||
func = keyToFunc( key, m_tables[ index ] );
|
||||
|
||||
} // end else
|
||||
|
||||
// return function, if found
|
||||
return func;
|
||||
|
||||
} // end findFunction
|
||||
|
||||
#ifdef NOT_IN_USE
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Search for the function in the specified table */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
const char *FunctionLexicon::funcToName( void *func, TableEntry *table )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( func == NULL )
|
||||
return NULL;
|
||||
|
||||
// search the table
|
||||
TableEntry *entry = table;
|
||||
while( entry && entry->key != NAMEKEY_INVALID )
|
||||
{
|
||||
|
||||
// is this it
|
||||
if( entry->func == func )
|
||||
return entry->name;
|
||||
|
||||
// not it, check next
|
||||
entry++;
|
||||
|
||||
} // end while
|
||||
|
||||
return NULL; // not found
|
||||
|
||||
} // end funcToName
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FunctionLexicon::FunctionLexicon( void )
|
||||
{
|
||||
Int i;
|
||||
|
||||
// empty the tables
|
||||
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
|
||||
m_tables[ i ] = NULL;
|
||||
|
||||
} // end FunctionLexicon
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
FunctionLexicon::~FunctionLexicon( void )
|
||||
{
|
||||
|
||||
} // end ~FunctionLexicon
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Initialize our dictionary of funtion pointers and symbols */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FunctionLexicon::init( void )
|
||||
{
|
||||
|
||||
// if you change this method, check the reset() and make sure it's OK
|
||||
|
||||
// initialize the name key identifiers for the lookup table
|
||||
loadTable( gameWinDrawTable, TABLE_GAME_WIN_DRAW );
|
||||
loadTable( gameWinSystemTable, TABLE_GAME_WIN_SYSTEM );
|
||||
loadTable( gameWinInputTable, TABLE_GAME_WIN_INPUT );
|
||||
loadTable( gameWinTooltipTable, TABLE_GAME_WIN_TOOLTIP );
|
||||
|
||||
loadTable( winLayoutInitTable, TABLE_WIN_LAYOUT_INIT );
|
||||
loadTable( winLayoutUpdateTable, TABLE_WIN_LAYOUT_UPDATE );
|
||||
loadTable( winLayoutShutdownTable, TABLE_WIN_LAYOUT_SHUTDOWN );
|
||||
|
||||
validate();
|
||||
|
||||
} // end init
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** reset */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FunctionLexicon::reset( void )
|
||||
{
|
||||
|
||||
//
|
||||
// make sure the ordering of what happens here with respect to derived classes resets is
|
||||
// all OK since we're cheating and using the init() method
|
||||
//
|
||||
|
||||
// nothing dynamically loaded, just reinit the tables
|
||||
init();
|
||||
|
||||
} // end reset
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Update */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
void FunctionLexicon::update( void )
|
||||
{
|
||||
|
||||
} // end update
|
||||
|
||||
/*
|
||||
// !NOTE! We can not have this function, see the header for
|
||||
// more information as to why
|
||||
//
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
// translate a function pointer to its symbolic name
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
char *FunctionLexicon::functionToName( void *func )
|
||||
{
|
||||
|
||||
// sanity
|
||||
if( func == NULL )
|
||||
return NULL;
|
||||
|
||||
// search ALL the tables
|
||||
Int i;
|
||||
char *name = NULL;
|
||||
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
|
||||
{
|
||||
|
||||
name = funcToName( func, m_tables[ i ] );
|
||||
if( name )
|
||||
return name;
|
||||
|
||||
} // end for i
|
||||
|
||||
return NULL; // not found
|
||||
|
||||
} // end functionToName
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
/** Scan the tables and make sure that each function address is unique.
|
||||
* We want to do this to prevent accidental entries of two identical
|
||||
* functions and because the compiler will optimize identical functions
|
||||
* to the same address (typically in empty functions with no body and the
|
||||
* same parameters) which we MUST keep separate for when we add code to
|
||||
* them */
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool FunctionLexicon::validate( void )
|
||||
{
|
||||
Bool valid = TRUE;
|
||||
Int i, j;
|
||||
TableEntry *sourceEntry, *lookAtEntry;
|
||||
|
||||
// scan all talbes
|
||||
for( i = 0; i < MAX_FUNCTION_TABLES; i++ )
|
||||
{
|
||||
|
||||
// scan through this table
|
||||
sourceEntry = m_tables[ i ];
|
||||
while( sourceEntry && sourceEntry->key != NAMEKEY_INVALID )
|
||||
{
|
||||
|
||||
//
|
||||
// scan all tables looking for the function in sourceEntry, do not bother
|
||||
// of source entry is NULL (a valid entry in the table, but not a function)
|
||||
//
|
||||
if( sourceEntry->func )
|
||||
{
|
||||
|
||||
// scan all tables
|
||||
for( j = 0; j < MAX_FUNCTION_TABLES; j++ )
|
||||
{
|
||||
|
||||
// scan all entries in this table
|
||||
lookAtEntry = m_tables[ j ];
|
||||
while( lookAtEntry && lookAtEntry->key != NAMEKEY_INVALID )
|
||||
{
|
||||
|
||||
//
|
||||
// check for match, do not match the entry source with itself
|
||||
//
|
||||
if( sourceEntry != lookAtEntry )
|
||||
if( sourceEntry->func == lookAtEntry->func )
|
||||
{
|
||||
|
||||
DEBUG_LOG(( "WARNING! Function lexicon entries match same address! '%s' and '%s'\n",
|
||||
sourceEntry->name, lookAtEntry->name ));
|
||||
valid = FALSE;
|
||||
|
||||
} // end if
|
||||
|
||||
// next entry in this target table
|
||||
lookAtEntry++;
|
||||
|
||||
} // end while
|
||||
|
||||
} // end for j
|
||||
|
||||
} // end if
|
||||
|
||||
// next source entry
|
||||
sourceEntry++;
|
||||
|
||||
} // end while
|
||||
|
||||
} // end for i
|
||||
|
||||
// return the valid state of our tables
|
||||
return valid;
|
||||
|
||||
} // end validate
|
||||
|
||||
//============================================================================
|
||||
// FunctionLexicon::gameWinDrawFunc
|
||||
//============================================================================
|
||||
|
||||
GameWinDrawFunc FunctionLexicon::gameWinDrawFunc( NameKeyType key, TableIndex index )
|
||||
{
|
||||
if ( index == TABLE_ANY )
|
||||
{
|
||||
// first search the device depended table then the device independent table
|
||||
GameWinDrawFunc func;
|
||||
|
||||
func = (GameWinDrawFunc)findFunction( key, TABLE_GAME_WIN_DEVICEDRAW );
|
||||
if ( func == NULL )
|
||||
{
|
||||
func = (GameWinDrawFunc)findFunction( key, TABLE_GAME_WIN_DRAW );
|
||||
}
|
||||
return func;
|
||||
}
|
||||
// search the specified table
|
||||
return (GameWinDrawFunc)findFunction( key, index );
|
||||
}
|
||||
|
||||
WindowLayoutInitFunc FunctionLexicon::winLayoutInitFunc( NameKeyType key, TableIndex index )
|
||||
{
|
||||
if ( index == TABLE_ANY )
|
||||
{
|
||||
// first search the device depended table then the device independent table
|
||||
WindowLayoutInitFunc func;
|
||||
|
||||
func = (WindowLayoutInitFunc)findFunction( key, TABLE_WIN_LAYOUT_DEVICEINIT );
|
||||
if ( func == NULL )
|
||||
{
|
||||
func = (WindowLayoutInitFunc)findFunction( key, TABLE_WIN_LAYOUT_INIT );
|
||||
}
|
||||
return func;
|
||||
}
|
||||
// search the specified table
|
||||
return (WindowLayoutInitFunc)findFunction( key, index );
|
||||
}
|
||||
66
Generals/Code/GameEngine/Source/Common/System/GameCommon.cpp
Normal file
66
Generals/Code/GameEngine/Source/Common/System/GameCommon.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GameCommon.h
|
||||
// Part of header detangling
|
||||
// John McDonald, Aug 2002
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/GameCommon.h"
|
||||
|
||||
const char *TheVeterancyNames[] =
|
||||
{
|
||||
"REGULAR",
|
||||
"VETERAN",
|
||||
"ELITE",
|
||||
"HEROIC",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *TheRelationshipNames[] =
|
||||
{
|
||||
"ENEMIES",
|
||||
"NEUTRAL",
|
||||
"ALLIES",
|
||||
NULL
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Real normalizeAngle(Real angle)
|
||||
{
|
||||
DEBUG_ASSERTCRASH(!_isnan(angle), ("Angle is NAN in normalizeAngle!\n"));
|
||||
|
||||
if( _isnan(angle) )
|
||||
return 0;// ARGH!!!! Don't assert and then not handle it! Error bad! Fix error!
|
||||
|
||||
while (angle > PI)
|
||||
angle -= 2*PI;
|
||||
|
||||
while (angle <= -PI)
|
||||
angle += 2*PI;
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
3594
Generals/Code/GameEngine/Source/Common/System/GameMemory.cpp
Normal file
3594
Generals/Code/GameEngine/Source/Common/System/GameMemory.cpp
Normal file
File diff suppressed because it is too large
Load Diff
46
Generals/Code/GameEngine/Source/Common/System/GameType.cpp
Normal file
46
Generals/Code/GameEngine/Source/Common/System/GameType.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GameType.cpp ///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
char *TimeOfDayNames[] =
|
||||
{
|
||||
"NONE",
|
||||
"MORNING",
|
||||
"AFTERNOON",
|
||||
"EVENING",
|
||||
"NIGHT",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
char *WeatherNames[] =
|
||||
{
|
||||
"NORMAL",
|
||||
"SNOWY",
|
||||
|
||||
NULL
|
||||
};
|
||||
558
Generals/Code/GameEngine/Source/Common/System/Geometry.cpp
Normal file
558
Generals/Code/GameEngine/Source/Common/System/Geometry.cpp
Normal file
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: Geometry.cpp /////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Steven Johnson, Aug 2002
|
||||
// Desc:
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#define DEFINE_GEOMETRY_NAMES
|
||||
|
||||
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
|
||||
#include "Common/Geometry.h"
|
||||
#include "Common/INI.h"
|
||||
#include "Common/RandomValue.h"
|
||||
#include "Common/Xfer.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//=============================================================================
|
||||
/*static*/ void GeometryInfo::parseGeometryType( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
((GeometryInfo*)store)->m_type = (GeometryType)INI::scanIndexList(ini->getNextToken(), GeometryNames);
|
||||
((GeometryInfo*)store)->calcBoundingStuff();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
/*static*/ void GeometryInfo::parseGeometryIsSmall( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
((GeometryInfo*)store)->m_isSmall = INI::scanBool(ini->getNextToken());
|
||||
((GeometryInfo*)store)->calcBoundingStuff();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
/*static*/ void GeometryInfo::parseGeometryHeight( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
((GeometryInfo*)store)->m_height = INI::scanReal(ini->getNextToken());
|
||||
((GeometryInfo*)store)->calcBoundingStuff();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
/*static*/ void GeometryInfo::parseGeometryMajorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
((GeometryInfo*)store)->m_majorRadius = INI::scanReal(ini->getNextToken());
|
||||
((GeometryInfo*)store)->calcBoundingStuff();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
/*static*/ void GeometryInfo::parseGeometryMinorRadius( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
|
||||
{
|
||||
((GeometryInfo*)store)->m_minorRadius = INI::scanReal(ini->getNextToken());
|
||||
((GeometryInfo*)store)->calcBoundingStuff();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void GeometryInfo::set(GeometryType type, Bool isSmall, Real height, Real majorRadius, Real minorRadius)
|
||||
{
|
||||
m_type = type;
|
||||
m_isSmall = isSmall;
|
||||
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
m_majorRadius = majorRadius;
|
||||
m_minorRadius = majorRadius;
|
||||
m_height = majorRadius;
|
||||
break;
|
||||
|
||||
case GEOMETRY_CYLINDER:
|
||||
m_majorRadius = majorRadius;
|
||||
m_minorRadius = majorRadius;
|
||||
m_height = height;
|
||||
break;
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
m_majorRadius = majorRadius;
|
||||
m_minorRadius = minorRadius;
|
||||
m_height = height;
|
||||
break;
|
||||
};
|
||||
|
||||
calcBoundingStuff();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Real calcDotProduct(const Coord3D& a, const Coord3D& b)
|
||||
{
|
||||
return a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Real calcDistSquared(const Coord3D& a, const Coord3D& b)
|
||||
{
|
||||
return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Real calcPointToLineDistSquared(const Coord3D& pt, const Coord3D& lineStart, const Coord3D& lineEnd)
|
||||
{
|
||||
Coord3D line, lineToPt, closest;
|
||||
|
||||
line.x = lineEnd.x - lineStart.x;
|
||||
line.y = lineEnd.y - lineStart.y;
|
||||
line.z = lineEnd.z - lineStart.z;
|
||||
|
||||
lineToPt.x = pt.x - lineStart.x;
|
||||
lineToPt.y = pt.y - lineStart.y;
|
||||
lineToPt.z = pt.z - lineStart.z;
|
||||
|
||||
Real dot = calcDotProduct(lineToPt, line);
|
||||
if (dot <= 0.0f)
|
||||
{
|
||||
// angle is obtuse
|
||||
return calcDistSquared(pt, lineStart);
|
||||
}
|
||||
|
||||
Real lineLenSqr = calcDistSquared(lineStart, lineEnd);
|
||||
DEBUG_ASSERTCRASH(lineLenSqr==calcDotProduct(line,line),("hmm"));
|
||||
if (lineLenSqr <= dot)
|
||||
{
|
||||
return calcDistSquared(pt, lineEnd);
|
||||
}
|
||||
|
||||
Real tmp = dot / lineLenSqr;
|
||||
|
||||
closest.x = lineStart.x + tmp * line.x;
|
||||
closest.y = lineStart.y + tmp * line.y;
|
||||
closest.z = lineStart.z + tmp * line.z;
|
||||
|
||||
return calcDistSquared(pt, closest);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
Bool GeometryInfo::isIntersectedByLineSegment(const Coord3D& loc, const Coord3D& from, const Coord3D& to) const
|
||||
{
|
||||
DEBUG_CRASH(("this call does not work properly for nonspheres yet. use with caution."));
|
||||
|
||||
/// @todo srj -- treats everything as a sphere for now. fix.
|
||||
Real distSquared = calcPointToLineDistSquared(loc, from, to);
|
||||
return distSquared <= sqr(getBoundingSphereRadius());
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// given an object with this geom, located at 'pos', and another obj with the given
|
||||
// pos & geom, calc the min and max pitches from this to that.
|
||||
void GeometryInfo::calcPitches(const Coord3D& thisPos, const GeometryInfo& that, const Coord3D& thatPos,
|
||||
Real& minPitch, Real& maxPitch) const
|
||||
{
|
||||
Coord3D thisCenter;
|
||||
getCenterPosition(thisPos, thisCenter);
|
||||
|
||||
Real dxy = sqrt(sqr(thatPos.x - thisCenter.x) + sqr(thatPos.y - thisCenter.y));
|
||||
|
||||
Real dz;
|
||||
|
||||
/** @todo srj -- this could be better, by calcing it for all the corners, not just top-center
|
||||
and bottom-center... oh well */
|
||||
dz = (thatPos.z + that.getMaxHeightAbovePosition()) - thisCenter.z;
|
||||
maxPitch = atan2(dz, dxy);
|
||||
|
||||
dz = (thatPos.z - that.getMaxHeightBelowPosition()) - thisCenter.z;
|
||||
minPitch = atan2(dz, dxy);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// given an object with this geom, SET how far above the object's canonical position its max z should extend.
|
||||
void GeometryInfo::setMaxHeightAbovePosition(Real z)
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
m_majorRadius = z;
|
||||
break;
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
case GEOMETRY_CYLINDER:
|
||||
m_height = z;
|
||||
break;
|
||||
};
|
||||
|
||||
calcBoundingStuff();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// given an object with this geom, how far above the object's canonical position does its max z extend?
|
||||
Real GeometryInfo::getMaxHeightAbovePosition() const
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
return m_majorRadius;
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
case GEOMETRY_CYLINDER:
|
||||
return m_height;
|
||||
};
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// given an object with this geom, how far below the object's canonical position does its max z extend?
|
||||
Real GeometryInfo::getMaxHeightBelowPosition() const
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
return m_majorRadius;
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
case GEOMETRY_CYLINDER:
|
||||
return 0.0f;
|
||||
};
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// given an object with this geom, located at 'pos', where is the "center" of the geometry?
|
||||
Real GeometryInfo::getZDeltaToCenterPosition() const
|
||||
{
|
||||
return (m_type == GEOMETRY_SPHERE) ? 0.0f : (m_height * 0.5f);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// given an object with this geom, located at 'pos', where is the "center" of the geometry?
|
||||
void GeometryInfo::getCenterPosition(const Coord3D& pos, Coord3D& center) const
|
||||
{
|
||||
center = pos;
|
||||
center.z += getZDeltaToCenterPosition();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void GeometryInfo::expandFootprint(Real radius)
|
||||
{
|
||||
m_majorRadius += radius;
|
||||
m_minorRadius += radius;
|
||||
calcBoundingStuff();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void GeometryInfo::get2DBounds(const Coord3D& geomCenter, Real angle, Region2D& bounds) const
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
case GEOMETRY_CYLINDER:
|
||||
{
|
||||
bounds.lo.x = geomCenter.x - m_majorRadius;
|
||||
bounds.lo.y = geomCenter.y - m_majorRadius;
|
||||
bounds.hi.x = geomCenter.x + m_majorRadius;
|
||||
bounds.hi.y = geomCenter.y + m_majorRadius;
|
||||
break;
|
||||
}
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
{
|
||||
Real c = (Real)cos(angle);
|
||||
Real s = (Real)sin(angle);
|
||||
Real exc = m_majorRadius*c;
|
||||
Real eyc = m_minorRadius*c;
|
||||
Real exs = m_majorRadius*s;
|
||||
Real eys = m_minorRadius*s;
|
||||
Real x,y;
|
||||
x = geomCenter.x - exc - eys;
|
||||
y = geomCenter.y + eyc - exs;
|
||||
bounds.lo.x = x;
|
||||
bounds.lo.y = y;
|
||||
bounds.hi.x = x;
|
||||
bounds.hi.y = y;
|
||||
|
||||
x = geomCenter.x + exc - eys;
|
||||
y = geomCenter.y + eyc + exs;
|
||||
if (bounds.lo.x > x) bounds.lo.x = x;
|
||||
if (bounds.lo.y > y) bounds.lo.y = y;
|
||||
if (bounds.hi.x < x) bounds.hi.x = x;
|
||||
if (bounds.hi.y < y) bounds.hi.y = y;
|
||||
|
||||
x = geomCenter.x + exc + eys;
|
||||
y = geomCenter.y - eyc + exs;
|
||||
if (bounds.lo.x > x) bounds.lo.x = x;
|
||||
if (bounds.lo.y > y) bounds.lo.y = y;
|
||||
if (bounds.hi.x < x) bounds.hi.x = x;
|
||||
if (bounds.hi.y < y) bounds.hi.y = y;
|
||||
|
||||
x = geomCenter.x - exc + eys;
|
||||
y = geomCenter.y - eyc - exs;
|
||||
if (bounds.lo.x > x) bounds.lo.x = x;
|
||||
if (bounds.lo.y > y) bounds.lo.y = y;
|
||||
if (bounds.hi.x < x) bounds.hi.x = x;
|
||||
if (bounds.hi.y < y) bounds.hi.y = y;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void GeometryInfo::clipPointToFootprint(const Coord3D& geomCenter, Coord3D& ptToClip) const
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
case GEOMETRY_CYLINDER:
|
||||
{
|
||||
Real dx = ptToClip.x - geomCenter.x;
|
||||
Real dy = ptToClip.y - geomCenter.y;
|
||||
Real radius = sqrt(sqr(dx) + sqr(dy));
|
||||
if (radius > m_majorRadius)
|
||||
{
|
||||
Real ratio = m_majorRadius / radius;
|
||||
ptToClip.x = geomCenter.x + dx * ratio;
|
||||
ptToClip.y = geomCenter.y + dy * ratio;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
{
|
||||
ptToClip.x = clamp(geomCenter.x - m_majorRadius, ptToClip.x, geomCenter.x + m_majorRadius);
|
||||
ptToClip.y = clamp(geomCenter.y - m_minorRadius, ptToClip.y, geomCenter.y + m_minorRadius);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
inline Bool isWithin(Real a, Real b, Real c) { return a<=b && b<=c; }
|
||||
|
||||
//=============================================================================
|
||||
Bool GeometryInfo::isPointInFootprint(const Coord3D& geomCenter, const Coord3D& pt) const
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
case GEOMETRY_CYLINDER:
|
||||
{
|
||||
Real dx = pt.x - geomCenter.x;
|
||||
Real dy = pt.y - geomCenter.y;
|
||||
Real radius = sqrt(sqr(dx) + sqr(dy));
|
||||
return (radius <= m_majorRadius);
|
||||
break;
|
||||
}
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
{
|
||||
return isWithin(geomCenter.x - m_majorRadius, pt.x, geomCenter.x + m_majorRadius) &&
|
||||
isWithin(geomCenter.y - m_minorRadius, pt.y, geomCenter.y + m_minorRadius);
|
||||
}
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void GeometryInfo::makeRandomOffsetWithinFootprint(Coord3D& pt) const
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
case GEOMETRY_CYLINDER:
|
||||
{
|
||||
#if 1
|
||||
// this is a better technique than the more obvious radius-and-angle
|
||||
// one, below, because the latter tends to clump more towards the center.
|
||||
Real maxDistSqr = sqr(m_majorRadius);
|
||||
Real distSqr;
|
||||
do
|
||||
{
|
||||
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
|
||||
pt.y = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
|
||||
pt.z = 0.0f;
|
||||
distSqr = sqr(pt.x) + sqr(pt.y);
|
||||
} while (distSqr > maxDistSqr);
|
||||
#else
|
||||
Real radius = GameLogicRandomValueReal(0.0f, m_boundingCircleRadius);
|
||||
Real angle = GameLogicRandomValueReal(-PI, PI);
|
||||
pt.x = radius * Cos(angle);
|
||||
pt.y = radius * Sin(angle);
|
||||
pt.z = 0.0f;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
{
|
||||
pt.x = GameLogicRandomValueReal(-m_majorRadius, m_majorRadius);
|
||||
pt.y = GameLogicRandomValueReal(-m_minorRadius, m_minorRadius);
|
||||
pt.z = 0.0f;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
Real GeometryInfo::getFootprintArea() const
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
case GEOMETRY_CYLINDER:
|
||||
{
|
||||
return PI * sqr(m_boundingCircleRadius);
|
||||
}
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
{
|
||||
return 4.0f * m_majorRadius * m_minorRadius;
|
||||
}
|
||||
};
|
||||
|
||||
DEBUG_CRASH(("should never get here"));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void GeometryInfo::calcBoundingStuff()
|
||||
{
|
||||
switch(m_type)
|
||||
{
|
||||
case GEOMETRY_SPHERE:
|
||||
{
|
||||
m_boundingSphereRadius = m_majorRadius;
|
||||
m_boundingCircleRadius = m_majorRadius;
|
||||
break;
|
||||
}
|
||||
case GEOMETRY_CYLINDER:
|
||||
{
|
||||
m_boundingCircleRadius = m_majorRadius;
|
||||
|
||||
m_boundingSphereRadius = m_height*0.5;
|
||||
if (m_boundingSphereRadius < m_majorRadius)
|
||||
m_boundingSphereRadius = m_majorRadius;
|
||||
break;
|
||||
}
|
||||
|
||||
case GEOMETRY_BOX:
|
||||
{
|
||||
m_boundingCircleRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius));
|
||||
m_boundingSphereRadius = sqrt(sqr(m_majorRadius) + sqr(m_minorRadius) + sqr(m_height*0.5));
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
//=============================================================================
|
||||
void GeometryInfo::tweakExtents(ExtentModType extentModType, Real extentModAmount)
|
||||
{
|
||||
switch(extentModType)
|
||||
{
|
||||
case EXTENTMOD_HEIGHT:
|
||||
m_height += extentModAmount;
|
||||
break;
|
||||
case EXTENTMOD_MAJOR:
|
||||
m_majorRadius += extentModAmount;
|
||||
break;
|
||||
case EXTENTMOD_MINOR:
|
||||
m_minorRadius += extentModAmount;
|
||||
break;
|
||||
case EXTENTMOD_TYPE:
|
||||
m_type = (GeometryType)((m_type + ((extentModType == EXTENTMOD_TYPE)?1:0)) % GEOMETRY_NUM_TYPES);
|
||||
break;
|
||||
}
|
||||
m_isSmall = false;
|
||||
calcBoundingStuff();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG) || defined(_INTERNAL)
|
||||
//=============================================================================
|
||||
AsciiString GeometryInfo::getDescriptiveString() const
|
||||
{
|
||||
AsciiString msg;
|
||||
msg.format("%d/%d(%g %g %g)", (Int)m_type, (Int)m_isSmall, m_majorRadius, m_minorRadius, m_height);
|
||||
return msg;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** CRC */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GeometryInfo::crc( Xfer *xfer )
|
||||
{
|
||||
|
||||
} // end crc
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Xfer method
|
||||
* Version Info:
|
||||
* 1: Initial version */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GeometryInfo::xfer( Xfer *xfer )
|
||||
{
|
||||
|
||||
// version
|
||||
XferVersion currentVersion = 1;
|
||||
XferVersion version = currentVersion;
|
||||
xfer->xferVersion( &version, currentVersion );
|
||||
|
||||
// type
|
||||
xfer->xferUser( &m_type, sizeof( GeometryType ) );
|
||||
|
||||
// is small
|
||||
xfer->xferBool( &m_isSmall );
|
||||
|
||||
// height
|
||||
xfer->xferReal( &m_height );
|
||||
|
||||
// major radius
|
||||
xfer->xferReal( &m_majorRadius );
|
||||
|
||||
// minor radius
|
||||
xfer->xferReal( &m_minorRadius );
|
||||
|
||||
// bouncing circle radius
|
||||
xfer->xferReal( &m_boundingCircleRadius );
|
||||
|
||||
// bounding sphere radius
|
||||
xfer->xferReal( &m_boundingSphereRadius );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void GeometryInfo::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
||||
137
Generals/Code/GameEngine/Source/Common/System/KindOf.cpp
Normal file
137
Generals/Code/GameEngine/Source/Common/System/KindOf.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Kindof.cpp /////////////////////////////////////////////////////////////////////////////////////
|
||||
// Part of header detangling
|
||||
// John McDonald, Aug 2002
|
||||
|
||||
#include "PreRTS.h"
|
||||
|
||||
#include "Common/KindOf.h"
|
||||
#include "Common/BitFlagsIO.h"
|
||||
|
||||
const char* KindOfMaskType::s_bitNameList[] =
|
||||
{
|
||||
"OBSTACLE",
|
||||
"SELECTABLE",
|
||||
"IMMOBILE",
|
||||
"CAN_ATTACK",
|
||||
"STICK_TO_TERRAIN_SLOPE",
|
||||
"CAN_CAST_REFLECTIONS",
|
||||
"SHRUBBERY",
|
||||
"STRUCTURE",
|
||||
"INFANTRY",
|
||||
"VEHICLE",
|
||||
"AIRCRAFT",
|
||||
"HUGE_VEHICLE",
|
||||
"DOZER",
|
||||
"HARVESTER",
|
||||
"COMMANDCENTER",
|
||||
#ifdef ALLOW_SURRENDER
|
||||
"PRISON",
|
||||
"COLLECTS_PRISON_BOUNTY",
|
||||
"POW_TRUCK",
|
||||
#endif
|
||||
"LINEBUILD",
|
||||
"SALVAGER",
|
||||
"WEAPON_SALVAGER",
|
||||
"TRANSPORT",
|
||||
"BRIDGE",
|
||||
"LANDMARK_BRIDGE",
|
||||
"BRIDGE_TOWER",
|
||||
"PROJECTILE",
|
||||
"PRELOAD",
|
||||
"NO_GARRISON",
|
||||
"WAVEGUIDE",
|
||||
"WAVE_EFFECT",
|
||||
"NO_COLLIDE",
|
||||
"REPAIR_PAD",
|
||||
"HEAL_PAD",
|
||||
"STEALTH_GARRISON",
|
||||
"CASH_GENERATOR",
|
||||
"AIRFIELD",
|
||||
"DRAWABLE_ONLY",
|
||||
"MP_COUNT_FOR_VICTORY",
|
||||
"REBUILD_HOLE",
|
||||
"SCORE",
|
||||
"SCORE_CREATE",
|
||||
"SCORE_DESTROY",
|
||||
"NO_HEAL_ICON",
|
||||
"CAN_RAPPEL",
|
||||
"PARACHUTABLE",
|
||||
#ifdef ALLOW_SURRENDER
|
||||
"CAN_SURRENDER",
|
||||
#endif
|
||||
"CAN_BE_REPULSED",
|
||||
"MOB_NEXUS",
|
||||
"IGNORED_IN_GUI",
|
||||
"CRATE",
|
||||
"CAPTURABLE",
|
||||
"CLEARED_BY_BUILD",
|
||||
"SMALL_MISSILE",
|
||||
"ALWAYS_VISIBLE",
|
||||
"UNATTACKABLE",
|
||||
"MINE",
|
||||
"CLEANUP_HAZARD",
|
||||
"PORTABLE_STRUCTURE",
|
||||
"ALWAYS_SELECTABLE",
|
||||
"ATTACK_NEEDS_LINE_OF_SIGHT",
|
||||
"WALK_ON_TOP_OF_WALL",
|
||||
"DEFENSIVE_WALL",
|
||||
"FS_POWER",
|
||||
"FS_FACTORY",
|
||||
"FS_BASE_DEFENSE",
|
||||
"FS_TECHNOLOGY",
|
||||
"AIRCRAFT_PATH_AROUND",
|
||||
"LOW_OVERLAPPABLE",
|
||||
"FORCEATTACKABLE",
|
||||
"AUTO_RALLYPOINT",
|
||||
"TECH_BUILDING",
|
||||
"POWERED",
|
||||
"PRODUCED_AT_HELIPAD",
|
||||
"DRONE",
|
||||
"CAN_SEE_THROUGH_STRUCTURE",
|
||||
"BALLISTIC_MISSILE",
|
||||
"CLICK_THROUGH",
|
||||
"SUPPLY_SOURCE_ON_PREVIEW",
|
||||
"PARACHUTE",
|
||||
"GARRISONABLE_UNTIL_DESTROYED",
|
||||
"BOAT",
|
||||
"IMMUNE_TO_CAPTURE",
|
||||
"HULK",
|
||||
"SHOW_PORTRAIT_WHEN_CONTROLLED",
|
||||
"SPAWNS_ARE_THE_WEAPONS",
|
||||
"CANNOT_BUILD_NEAR_SUPPLIES",
|
||||
"SUPPLY_SOURCE",
|
||||
"REVEAL_TO_ALL",
|
||||
"DISGUISER",
|
||||
"INERT",
|
||||
"HERO",
|
||||
"IGNORES_SELECT_ALL",
|
||||
"DONT_AUTO_CRUSH_INFANTRY",
|
||||
NULL
|
||||
};
|
||||
|
||||
KindOfMaskType KINDOFMASK_NONE; // inits to all zeroes
|
||||
|
||||
451
Generals/Code/GameEngine/Source/Common/System/List.cpp
Normal file
451
Generals/Code/GameEngine/Source/Common/System/List.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: WSYS Library
|
||||
//
|
||||
// Module: List
|
||||
//
|
||||
// File name: WSYS_List.cpp
|
||||
//
|
||||
// Created: 10/31/01 TR
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h"
|
||||
|
||||
#include "Common/List.h"
|
||||
|
||||
// 'assignment within condition expression'.
|
||||
#pragma warning(disable : 4706)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//============================================================================
|
||||
// LList::LList
|
||||
//============================================================================
|
||||
|
||||
LList::LList( )
|
||||
: m_sortMode(DESCENDING)
|
||||
{
|
||||
m_head.setItem( &m_head.m_item);
|
||||
};
|
||||
|
||||
//=================================================================
|
||||
// LList::add
|
||||
//=================================================================
|
||||
|
||||
void LList::add( LListNode* new_node )
|
||||
{
|
||||
LListNode* node;
|
||||
Int pri;
|
||||
|
||||
if ( m_addToEndOfGroup )
|
||||
{
|
||||
pri = new_node->priority();
|
||||
node = &m_head;
|
||||
while( (node = node->prev() ))
|
||||
{
|
||||
if( (m_sortMode == ASCENDING && node->priority() >= pri)
|
||||
|| (m_sortMode == DESCENDING && node->priority() <= pri) )
|
||||
{
|
||||
node->append( new_node );
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_head.append( new_node );
|
||||
}
|
||||
else
|
||||
{
|
||||
pri = new_node->priority();
|
||||
node = &m_head;
|
||||
while( (node = node->next() ))
|
||||
{
|
||||
if( (m_sortMode == ASCENDING && node->priority() <= pri)
|
||||
|| (m_sortMode == DESCENDING && node->priority() >= pri) )
|
||||
{
|
||||
node->insert( new_node );
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_head.insert( new_node );
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::addGDFNode
|
||||
//============================================================================
|
||||
|
||||
void LList::addItem( Int pri, void* item )
|
||||
{
|
||||
LListNode *node = NEW LListNode(); // poolify
|
||||
|
||||
if ( node )
|
||||
{
|
||||
node->setPriority( pri );
|
||||
node->setItem( item );
|
||||
node->autoDelete();
|
||||
add( node );
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::addGDFNodeToHead
|
||||
//============================================================================
|
||||
|
||||
void LList::addItemToHead( void *item )
|
||||
{
|
||||
LListNode *node = NEW LListNode();
|
||||
|
||||
if ( node )
|
||||
{
|
||||
node->setItem( item );
|
||||
node->autoDelete();
|
||||
addToHead( node );
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::addGDFNodeToTail
|
||||
//============================================================================
|
||||
|
||||
void LList::addItemToTail( void *item )
|
||||
{
|
||||
LListNode *node = NEW LListNode();
|
||||
|
||||
if ( node )
|
||||
{
|
||||
node->setItem( item );
|
||||
node->autoDelete();
|
||||
addToTail( node );
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::Clear
|
||||
//============================================================================
|
||||
|
||||
void LList::clear( void )
|
||||
{
|
||||
LListNode *node;
|
||||
|
||||
while ( (node = firstNode()) != NULL )
|
||||
{
|
||||
node->remove();
|
||||
node->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LList::nodeCount
|
||||
//=================================================================
|
||||
|
||||
Int LList::nodeCount( void )
|
||||
{
|
||||
LListNode* node;
|
||||
Int count = 0;
|
||||
|
||||
node = firstNode();
|
||||
|
||||
while(node)
|
||||
{
|
||||
count++;
|
||||
node = node->next();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LList::getNode
|
||||
//=================================================================
|
||||
|
||||
LListNode* LList::getNode( Int index )
|
||||
{
|
||||
LListNode* node;
|
||||
|
||||
node = firstNode();
|
||||
|
||||
while( node && index >= 0 )
|
||||
{
|
||||
if( index-- == 0 )
|
||||
{
|
||||
return node;
|
||||
}
|
||||
node = node->next();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::merge
|
||||
//============================================================================
|
||||
|
||||
void LList::merge( LList *list )
|
||||
{
|
||||
|
||||
if ( list == NULL || list->isEmpty() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_head.m_prev->m_next = list->m_head.m_next;
|
||||
list->m_head.m_next->m_prev = m_head.m_prev;
|
||||
list->m_head.m_prev->m_next = &m_head;
|
||||
m_head.m_prev = list->m_head.m_prev;
|
||||
list->m_head.m_next = &list->m_head;
|
||||
list->m_head.m_prev = &list->m_head;
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::hasReference
|
||||
//============================================================================
|
||||
|
||||
Bool LList::hasItem( void *item )
|
||||
{
|
||||
return findItem( item ) != NULL;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::findReference
|
||||
//============================================================================
|
||||
|
||||
LListNode* LList::findItem( void *item )
|
||||
{
|
||||
LListNode* node;
|
||||
|
||||
node = firstNode();
|
||||
|
||||
while( node )
|
||||
{
|
||||
if( node->item() == item )
|
||||
{
|
||||
return node;
|
||||
}
|
||||
node = node->next();
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LListNode::LListNode
|
||||
//============================================================================
|
||||
|
||||
LListNode::LListNode()
|
||||
: m_pri(0),
|
||||
m_item(NULL),
|
||||
m_autoDelete(FALSE)
|
||||
{
|
||||
m_next = m_prev = this;
|
||||
};
|
||||
|
||||
//=================================================================
|
||||
// LListNode::insert
|
||||
//=================================================================
|
||||
|
||||
void LListNode::insert( LListNode* new_node )
|
||||
{
|
||||
new_node->m_prev = m_prev;
|
||||
new_node->m_next = this;
|
||||
m_prev = new_node;
|
||||
new_node->m_prev->m_next = new_node;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LListNode::append
|
||||
//=================================================================
|
||||
|
||||
void LListNode::append( LListNode* new_node )
|
||||
{
|
||||
new_node->m_prev = this;
|
||||
new_node->m_next = m_next;
|
||||
this->m_next = new_node;
|
||||
new_node->m_next->m_prev = new_node;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LListNode::remove
|
||||
//=================================================================
|
||||
|
||||
void LListNode::remove( void )
|
||||
{
|
||||
m_prev->m_next = m_next;
|
||||
m_next->m_prev = m_prev;
|
||||
m_prev = m_next = this; // so we know that the node is not in a list
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LListNode::next
|
||||
//=================================================================
|
||||
|
||||
LListNode* LListNode::next( void )
|
||||
{
|
||||
|
||||
if( m_next->isHead( ))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_next;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LListNode::prev
|
||||
//=================================================================
|
||||
|
||||
LListNode* LListNode::prev( void )
|
||||
{
|
||||
if( m_prev->isHead())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_prev;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LListNode::loopNext
|
||||
//=================================================================
|
||||
|
||||
LListNode* LListNode::loopNext( void )
|
||||
{
|
||||
LListNode* next;
|
||||
|
||||
next = m_next;
|
||||
|
||||
if( next->isHead( ))
|
||||
{
|
||||
// skip head node
|
||||
next = next->m_next;
|
||||
if( next->isHead( ))
|
||||
{
|
||||
return NULL; // it is an empty list
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LListNode::loopPrev
|
||||
//=================================================================
|
||||
|
||||
LListNode* LListNode::loopPrev( void )
|
||||
{
|
||||
LListNode* prev;
|
||||
|
||||
prev = m_prev;
|
||||
|
||||
if( prev->isHead())
|
||||
{
|
||||
// skip head node
|
||||
prev = prev->m_prev;
|
||||
if( prev->isHead())
|
||||
{
|
||||
return NULL; // it is an empty list
|
||||
}
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LListNode::destroy
|
||||
//============================================================================
|
||||
|
||||
void LListNode::destroy( void )
|
||||
{
|
||||
if ( m_autoDelete )
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// LList::addToEndOfGroup
|
||||
//============================================================================
|
||||
|
||||
void LList::addToEndOfGroup( Bool yes )
|
||||
{
|
||||
m_addToEndOfGroup = yes;
|
||||
}
|
||||
627
Generals/Code/GameEngine/Source/Common/System/LocalFile.cpp
Normal file
627
Generals/Code/GameEngine/Source/Common/System/LocalFile.cpp
Normal file
@@ -0,0 +1,627 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: WSYS Library
|
||||
//
|
||||
// Module: IO_
|
||||
//
|
||||
// File name: IO_LocalFile.cpp
|
||||
//
|
||||
// Created: 4/23/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "Common/LocalFile.h"
|
||||
#include "Common/RAMFile.h"
|
||||
#include "Lib/BaseType.h"
|
||||
#include "Common/PerfTimer.h"
|
||||
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static Int s_totalOpen = 0;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::LocalFile
|
||||
//=================================================================
|
||||
|
||||
LocalFile::LocalFile()
|
||||
#ifdef USE_BUFFERED_IO
|
||||
: m_file(NULL)
|
||||
#else
|
||||
: m_handle(-1)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::~LocalFile
|
||||
//=================================================================
|
||||
|
||||
LocalFile::~LocalFile()
|
||||
{
|
||||
#ifdef USE_BUFFERED_IO
|
||||
if (m_file)
|
||||
{
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
--s_totalOpen;
|
||||
}
|
||||
#else
|
||||
if( m_handle != -1 )
|
||||
{
|
||||
_close( m_handle );
|
||||
m_handle = -1;
|
||||
--s_totalOpen;
|
||||
}
|
||||
#endif
|
||||
|
||||
File::close();
|
||||
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::open
|
||||
//=================================================================
|
||||
/**
|
||||
* This function opens a file using the standard C open() call. Access flags
|
||||
* are mapped to the appropriate open flags. Returns true if file was opened
|
||||
* successfully.
|
||||
*/
|
||||
//=================================================================
|
||||
|
||||
//DECLARE_PERF_TIMER(LocalFile)
|
||||
Bool LocalFile::open( const Char *filename, Int access )
|
||||
{
|
||||
//USE_PERF_TIMER(LocalFile)
|
||||
if( !File::open( filename, access) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* here we translate WSYS file access to the std C equivalent */
|
||||
#ifdef USE_BUFFERED_IO
|
||||
char mode[32];
|
||||
char* m = mode;
|
||||
|
||||
if (m_access & APPEND)
|
||||
{
|
||||
DEBUG_CRASH(("not yet supported by buffered mode"));
|
||||
}
|
||||
|
||||
if (m_access & TRUNCATE)
|
||||
{
|
||||
DEBUG_CRASH(("not yet supported by buffered mode"));
|
||||
}
|
||||
|
||||
if((m_access & READWRITE ) == READWRITE )
|
||||
{
|
||||
if (m_access & CREATE)
|
||||
{
|
||||
*m++ = 'w';
|
||||
*m++ = '+';
|
||||
}
|
||||
else
|
||||
{
|
||||
*m++ = 'r';
|
||||
*m++ = '+';
|
||||
}
|
||||
}
|
||||
else if(m_access & WRITE)
|
||||
{
|
||||
*m++ = 'w';
|
||||
}
|
||||
else
|
||||
{
|
||||
*m++ = 'r';
|
||||
DEBUG_ASSERTCRASH(!(m_access & TRUNCATE), ("cannot truncate with read-only"));
|
||||
}
|
||||
|
||||
if (m_access & TEXT)
|
||||
{
|
||||
*m++ = 't';
|
||||
}
|
||||
if (m_access & BINARY)
|
||||
{
|
||||
*m++ = 'b';
|
||||
}
|
||||
|
||||
*m++ = 0;
|
||||
|
||||
m_file = fopen(filename, mode);
|
||||
if (m_file == NULL)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
//setvbuf(m_file, m_vbuf, _IOFBF, sizeof(BUF_SIZE));
|
||||
|
||||
#else
|
||||
|
||||
int flags = 0;
|
||||
|
||||
if (m_access & CREATE)
|
||||
{
|
||||
flags |= _O_CREAT;
|
||||
}
|
||||
if (m_access & TRUNCATE)
|
||||
{
|
||||
flags |= _O_TRUNC;
|
||||
}
|
||||
if (m_access & APPEND)
|
||||
{
|
||||
flags |= _O_APPEND;
|
||||
}
|
||||
if (m_access & TEXT)
|
||||
{
|
||||
flags |= _O_TEXT;
|
||||
}
|
||||
if (m_access & BINARY)
|
||||
{
|
||||
flags |= _O_BINARY;
|
||||
}
|
||||
|
||||
if((m_access & READWRITE )== READWRITE )
|
||||
{
|
||||
flags |= _O_RDWR;
|
||||
}
|
||||
else if(m_access & WRITE)
|
||||
{
|
||||
flags |= _O_WRONLY;
|
||||
flags |= _O_CREAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags |= _O_RDONLY;
|
||||
}
|
||||
|
||||
m_handle = _open( filename, flags , _S_IREAD | _S_IWRITE);
|
||||
|
||||
if( m_handle == -1 )
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
++s_totalOpen;
|
||||
/// DEBUG_LOG(("LocalFile::open %s (total %d)\n",filename,s_totalOpen));
|
||||
if ( m_access & APPEND )
|
||||
{
|
||||
if ( seek ( 0, END ) < 0 )
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
|
||||
close();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::close
|
||||
//=================================================================
|
||||
/**
|
||||
* Closes the current file if it is open.
|
||||
* Must call LocalFile::close() for each successful LocalFile::open() call.
|
||||
*/
|
||||
//=================================================================
|
||||
|
||||
void LocalFile::close( void )
|
||||
{
|
||||
File::close();
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::read
|
||||
//=================================================================
|
||||
|
||||
Int LocalFile::read( void *buffer, Int bytes )
|
||||
{
|
||||
//USE_PERF_TIMER(LocalFile)
|
||||
if( !m_open )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buffer == NULL)
|
||||
{
|
||||
#ifdef USE_BUFFERED_IO
|
||||
fseek(m_file, bytes, SEEK_CUR);
|
||||
#else
|
||||
_lseek(m_handle, bytes, SEEK_CUR);
|
||||
#endif
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#ifdef USE_BUFFERED_IO
|
||||
Int ret = fread(buffer, 1, bytes, m_file);
|
||||
#else
|
||||
Int ret = _read( m_handle, buffer, bytes );
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::write
|
||||
//=================================================================
|
||||
|
||||
Int LocalFile::write( const void *buffer, Int bytes )
|
||||
{
|
||||
|
||||
if( !m_open || !buffer )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef USE_BUFFERED_IO
|
||||
Int ret = fwrite(buffer, 1, bytes, m_file);
|
||||
#else
|
||||
Int ret = _write( m_handle, buffer, bytes );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::seek
|
||||
//=================================================================
|
||||
|
||||
Int LocalFile::seek( Int pos, seekMode mode)
|
||||
{
|
||||
int lmode;
|
||||
|
||||
switch( mode )
|
||||
{
|
||||
case START:
|
||||
lmode = SEEK_SET;
|
||||
break;
|
||||
case CURRENT:
|
||||
lmode = SEEK_CUR;
|
||||
break;
|
||||
case END:
|
||||
DEBUG_ASSERTCRASH(pos <= 0, ("LocalFile::seek - pos should be <= 0 for a seek starting at the end of the file"));
|
||||
lmode = SEEK_END;
|
||||
break;
|
||||
default:
|
||||
// bad seek mode
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef USE_BUFFERED_IO
|
||||
Int ret = fseek(m_file, pos, lmode);
|
||||
if (ret == 0)
|
||||
return ftell(m_file);
|
||||
else
|
||||
return -1;
|
||||
#else
|
||||
Int ret = _lseek( m_handle, pos, lmode );
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::scanInt
|
||||
//=================================================================
|
||||
// skips preceding whitespace and stops at the first non-number
|
||||
// or at EOF
|
||||
Bool LocalFile::scanInt(Int &newInt)
|
||||
{
|
||||
newInt = 0;
|
||||
AsciiString tempstr;
|
||||
Char c;
|
||||
Int val;
|
||||
|
||||
// skip preceding non-numeric characters
|
||||
do {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(&c, 1, 1, m_file);
|
||||
#else
|
||||
val = _read( m_handle, &c, 1);
|
||||
#endif
|
||||
} while ((val != 0) && (((c < '0') || (c > '9')) && (c != '-')));
|
||||
|
||||
if (val == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do {
|
||||
tempstr.concat(c);
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(&c, 1, 1, m_file);
|
||||
#else
|
||||
val = _read( m_handle, &c, 1);
|
||||
#endif
|
||||
} while ((val != 0) && ((c >= '0') && (c <= '9')));
|
||||
|
||||
// put the last read char back, since we didn't use it.
|
||||
if (val != 0) {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
fseek(m_file, -1, SEEK_CUR);
|
||||
#else
|
||||
_lseek(m_handle, -1, SEEK_CUR);
|
||||
#endif
|
||||
}
|
||||
|
||||
newInt = atoi(tempstr.str());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::scanReal
|
||||
//=================================================================
|
||||
// skips preceding whitespace and stops at the first non-number
|
||||
// or at EOF
|
||||
Bool LocalFile::scanReal(Real &newReal)
|
||||
{
|
||||
newReal = 0.0;
|
||||
AsciiString tempstr;
|
||||
Char c;
|
||||
Int val;
|
||||
Bool sawDec = FALSE;
|
||||
|
||||
// skip the preceding white space
|
||||
do {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(&c, 1, 1, m_file);
|
||||
#else
|
||||
val = _read( m_handle, &c, 1);
|
||||
#endif
|
||||
} while ((val != 0) && (((c < '0') || (c > '9')) && (c != '-') && (c != '.')));
|
||||
|
||||
if (val == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do {
|
||||
tempstr.concat(c);
|
||||
if (c == '.') {
|
||||
sawDec = TRUE;
|
||||
}
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(&c, 1, 1, m_file);
|
||||
#else
|
||||
val = _read(m_handle, &c, 1);
|
||||
#endif
|
||||
} while ((val != 0) && (((c >= '0') && (c <= '9')) || ((c == '.') && !sawDec)));
|
||||
|
||||
if (val != 0) {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
fseek(m_file, -1, SEEK_CUR);
|
||||
#else
|
||||
_lseek(m_handle, -1, SEEK_CUR);
|
||||
#endif
|
||||
}
|
||||
|
||||
newReal = atof(tempstr.str());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::scanString
|
||||
//=================================================================
|
||||
// skips preceding whitespace and stops at the first whitespace
|
||||
// or at EOF
|
||||
Bool LocalFile::scanString(AsciiString &newString)
|
||||
{
|
||||
Char c;
|
||||
Int val;
|
||||
|
||||
newString.clear();
|
||||
|
||||
// skip the preceding whitespace
|
||||
do {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(&c, 1, 1, m_file);
|
||||
#else
|
||||
val = _read(m_handle, &c, 1);
|
||||
#endif
|
||||
} while ((val != 0) && (isspace(c)));
|
||||
|
||||
if (val == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do {
|
||||
newString.concat(c);
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(&c, 1, 1, m_file);
|
||||
#else
|
||||
val = _read(m_handle, &c, 1);
|
||||
#endif
|
||||
} while ((val != 0) && (!isspace(c)));
|
||||
|
||||
if (val != 0) {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
fseek(m_file, -1, SEEK_CUR);
|
||||
#else
|
||||
_lseek(m_handle, -1, SEEK_CUR);
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::nextLine
|
||||
//=================================================================
|
||||
// scans to the first character after a new-line or at EOF
|
||||
void LocalFile::nextLine(Char *buf, Int bufSize)
|
||||
{
|
||||
Char c = 0;
|
||||
Int val;
|
||||
Int i = 0;
|
||||
|
||||
// seek to the next new-line.
|
||||
do {
|
||||
if ((buf == NULL) || (i >= (bufSize-1))) {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(&c, 1, 1, m_file);
|
||||
#else
|
||||
val = _read(m_handle, &c, 1);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef USE_BUFFERED_IO
|
||||
val = fread(buf + i, 1, 1, m_file);
|
||||
#else
|
||||
val = _read(m_handle, buf + i, 1);
|
||||
#endif
|
||||
c = buf[i];
|
||||
}
|
||||
++i;
|
||||
} while ((val != 0) && (c != '\n'));
|
||||
|
||||
if (buf != NULL) {
|
||||
if (i < bufSize) {
|
||||
buf[i] = 0;
|
||||
} else {
|
||||
buf[bufSize] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
//=================================================================
|
||||
File* LocalFile::convertToRAMFile()
|
||||
{
|
||||
RAMFile *ramFile = newInstance( RAMFile );
|
||||
if (ramFile->open(this))
|
||||
{
|
||||
if (this->m_deleteOnClose)
|
||||
{
|
||||
ramFile->deleteOnClose();
|
||||
this->close(); // is deleteonclose, so should delete.
|
||||
}
|
||||
else
|
||||
{
|
||||
this->close();
|
||||
this->deleteInstance();
|
||||
}
|
||||
return ramFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
ramFile->close();
|
||||
ramFile->deleteInstance();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================
|
||||
// LocalFile::readEntireAndClose
|
||||
//=================================================================
|
||||
/**
|
||||
Allocate a buffer large enough to hold entire file, read
|
||||
the entire file into the buffer, then close the file.
|
||||
the buffer is owned by the caller, who is responsible
|
||||
for freeing is (via delete[]). This is a Good Thing to
|
||||
use because it minimizes memory copies for BIG files.
|
||||
*/
|
||||
char* LocalFile::readEntireAndClose()
|
||||
{
|
||||
UnsignedInt fileSize = size();
|
||||
char* buffer = NEW char[fileSize];
|
||||
|
||||
read(buffer, fileSize);
|
||||
|
||||
close();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright(C) 2001 - All Rights Reserved
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: Game Engine
|
||||
//
|
||||
// Module: IO
|
||||
//
|
||||
// File name: LocalFileSystem.cpp
|
||||
//
|
||||
// Created: 4/23/01
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Includes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#include "PreRTS.h"
|
||||
#include "Common/LocalFileSystem.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externals
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Defines
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Types
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Data
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
LocalFileSystem *TheLocalFileSystem = NULL;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Prototypes
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void LocalFileSystem::init() {
|
||||
}
|
||||
|
||||
void LocalFileSystem::reset() {
|
||||
}
|
||||
|
||||
void LocalFileSystem::update() {
|
||||
}
|
||||
754
Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp
Normal file
754
Generals/Code/GameEngine/Source/Common/System/MemoryInit.cpp
Normal file
@@ -0,0 +1,754 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: MemoryInit.cpp
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Westwood Studios Pacific.
|
||||
//
|
||||
// Confidential Information
|
||||
// Copyright (C) 2001 - All Rights Reserved
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Project: RTS3
|
||||
//
|
||||
// File name: MemoryInit.cpp
|
||||
//
|
||||
// Created: Steven Johnson, August 2001
|
||||
//
|
||||
// Desc: Memory manager
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
// SYSTEM INCLUDES
|
||||
|
||||
// USER INCLUDES
|
||||
#include "Lib/BaseType.h"
|
||||
#include "Common/GameMemory.h"
|
||||
|
||||
#ifdef _INTERNAL
|
||||
// for occasional debugging...
|
||||
//#pragma optimize("", off)
|
||||
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void userMemoryManagerGetDmaParms(Int *numSubPools, const PoolInitRec **pParms)
|
||||
{
|
||||
static const PoolInitRec defaultDMA[7] =
|
||||
{
|
||||
// name, allocsize, initialcount, overflowcount
|
||||
{ "dmaPool_16", 16, 65536, 1024 },
|
||||
{ "dmaPool_32", 32, 150000, 1024 },
|
||||
{ "dmaPool_64", 64, 60000, 1024 },
|
||||
{ "dmaPool_128", 128, 32768, 1024 },
|
||||
{ "dmaPool_256", 256, 8192, 1024 },
|
||||
{ "dmaPool_512", 512, 8192, 1024 },
|
||||
{ "dmaPool_1024", 1024, 24000, 1024 }
|
||||
};
|
||||
|
||||
*numSubPools = 7;
|
||||
*pParms = defaultDMA;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
struct PoolSizeRec
|
||||
{
|
||||
const char* name;
|
||||
Int initial;
|
||||
Int overflow;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// And please be careful of duplicates. They are not rejected.
|
||||
// not const -- we might override from INI
|
||||
static PoolSizeRec sizes[] =
|
||||
{
|
||||
{ "PartitionContactListNode", 2048, 512 },
|
||||
{ "BattleshipUpdate", 32, 32 },
|
||||
{ "FlyToDestAndDestroyUpdate", 32, 32 },
|
||||
{ "MusicTrack", 32, 32 },
|
||||
{ "PositionalSoundPool", 32, 32 },
|
||||
{ "GameMessage", 2048, 32 },
|
||||
{ "NameKeyBucketPool", 4096, 32 },
|
||||
{ "ObjectSellInfo", 16, 16 },
|
||||
{ "ProductionPrerequisitePool", 1024, 32 },
|
||||
{ "RadarObject", 512, 32 },
|
||||
{ "ResourceGatheringManager", 16, 16 },
|
||||
{ "SightingInfo", 8192, 2048 },// Looks big, but all 3000 objects used to have 4 just built in.
|
||||
{ "SpecialPowerTemplate", 64, 32 },
|
||||
{ "StateMachinePool", 32, 32 },
|
||||
{ "TeamPool", 128, 32 }, // if you increase this, increase player/team relation map pools
|
||||
{ "PlayerRelationMapPool", 128, 32 },
|
||||
{ "TeamRelationMapPool", 128, 32 },
|
||||
{ "TeamPrototypePool", 256, 32 },
|
||||
{ "TerrainType", 256, 32 },
|
||||
{ "ThingTemplatePool", 1200, 32 },
|
||||
{ "TunnelTracker", 16, 16 },
|
||||
{ "Upgrade", 16, 16 },
|
||||
{ "UpgradeTemplate", 128, 16 },
|
||||
{ "Anim2D", 32, 32 },
|
||||
{ "CommandButton", 300, 16 },
|
||||
{ "CommandSet", 256, 16 },
|
||||
{ "DisplayString", 32, 32 },
|
||||
{ "WebBrowserURL", 16, 16 },
|
||||
{ "Drawable", 4096, 32 },
|
||||
{ "Image", 2048, 32 },
|
||||
{ "ParticlePool", 4096, 256 },
|
||||
{ "ParticleSystemTemplatePool", 768, 32 },
|
||||
{ "ParticleSystemPool", 1024, 32 },
|
||||
{ "TerrainRoadType", 64, 64, },
|
||||
{ "WindowLayoutPool", 32, 32 },
|
||||
{ "AnimatedParticleSysBoneClientUpdate", 16, 16 },
|
||||
{ "SwayClientUpdate", 4096, 32 },
|
||||
{ "BeaconClientUpdate", 64, 32 },
|
||||
{ "AIGroupPool", 64, 32 },
|
||||
{ "AIDockMachinePool", 256, 32 },
|
||||
{ "AIGuardMachinePool", 32, 32 },
|
||||
{ "AITNGuardMachinePool", 32, 32 },
|
||||
{ "PathNodePool", 8192, 1024 },
|
||||
{ "PathPool", 256, 16 },
|
||||
{ "WorkOrder", 32, 32 },
|
||||
{ "TeamInQueue", 32, 32 },
|
||||
{ "AIPlayer", 8, 8 },
|
||||
{ "AISkirmishPlayer", 8, 8 },
|
||||
{ "AIStateMachine", 600, 32 },
|
||||
{ "JetAIStateMachine", 64, 32 },
|
||||
{ "HeliAIStateMachine", 64, 32 },
|
||||
{ "AIAttackMoveStateMachine", 2048, 32 },
|
||||
{ "AIAttackThenIdleStateMachine", 512, 32 },
|
||||
{ "AttackStateMachine", 512, 32 },
|
||||
{ "CrateTemplate", 32, 32 },
|
||||
{ "ExperienceTrackerPool", 4096, 256 },
|
||||
{ "FiringTrackerPool", 4096, 256 },
|
||||
{ "ObjectRepulsorHelper", 1024, 256 },
|
||||
{ "ObjectSMCHelperPool", 4096, 256 },
|
||||
{ "ObjectWeaponStatusHelperPool", 4096, 256 },
|
||||
{ "ObjectDefectionHelperPool", 4096, 256 },
|
||||
{ "Locomotor", 2048, 32 },
|
||||
{ "LocomotorTemplate", 128, 32 },
|
||||
{ "ObjectPool", 4096, 32 },
|
||||
{ "SimpleObjectIteratorPool", 32, 32 },
|
||||
{ "SimpleObjectIteratorClumpPool", 4096, 32 },
|
||||
{ "PartitionDataPool", 4096, 32 },
|
||||
{ "BuildEntry", 32, 32 },
|
||||
{ "Weapon", 4096, 32 },
|
||||
{ "WeaponTemplate", 192, 32 },
|
||||
{ "AIUpdateInterface", 600, 32 },
|
||||
{ "ActiveBody", 1024, 32 },
|
||||
{ "ActiveShroudUpgrade", 32, 32 },
|
||||
{ "AssistedTargetingUpdate", 32, 32 },
|
||||
{ "AudioEventInfo", 1200, 64 },
|
||||
{ "AudioRequest", 256, 8 },
|
||||
{ "AutoHealBehavior", 4096, 32 },
|
||||
{ "BaseRegenerateUpdate", 64, 32 },
|
||||
{ "BoneFXDamage", 64, 32 },
|
||||
{ "BoneFXUpdate", 64, 32 },
|
||||
{ "BridgeBehavior", 32, 32 },
|
||||
{ "BridgeTowerBehavior", 32, 32 },
|
||||
{ "BridgeScaffoldBehavior", 32, 32 },
|
||||
{ "CaveContain", 16, 16 },
|
||||
{ "HealContain", 32, 32 },
|
||||
{ "CreateCrateDie", 256, 128 },
|
||||
{ "CreateObjectDie", 1024, 32 },
|
||||
{ "EjectPilotDie", 1024, 32 },
|
||||
{ "CrushDie", 1024, 32 },
|
||||
{ "DamDie", 8, 8 },
|
||||
{ "DelayedUpgrade", 32, 32 },
|
||||
{ "DelayedWeaponSetUpgradeUpdate", 32, 32 },
|
||||
{ "DeliverPayloadStateMachine", 32, 32 },
|
||||
{ "DeliverPayloadAIUpdate", 32, 32 },
|
||||
{ "DeletionUpdate", 128, 32 },
|
||||
{ "HackInternetStateMachine", 32, 32 },
|
||||
{ "HackInternetAIUpdate", 32, 32 },
|
||||
{ "MissileAIUpdate", 512, 32 },
|
||||
{ "DumbProjectileBehavior", 64, 32 },
|
||||
{ "DestroyDie", 1024, 32 },
|
||||
{ "UpgradeDie", 128, 32 },
|
||||
{ "KeepObjectDie", 128, 32 },
|
||||
{ "DozerAIUpdate", 32, 32 },
|
||||
{ "DynamicGeometryInfoUpdate", 16, 16 },
|
||||
{ "DynamicShroudClearingRangeUpdate", 128, 16 },
|
||||
{ "FXListDie", 1024, 32 },
|
||||
{ "FireSpreadUpdate", 2048, 128 },
|
||||
{ "FirestormDynamicGeometryInfoUpdate", 16, 16 },
|
||||
{ "FireWeaponCollide", 2048, 32 },
|
||||
{ "FireWeaponUpdate", 32, 32 },
|
||||
{ "FlammableUpdate", 4096, 256 },
|
||||
{ "FloatUpdate", 512, 128 },
|
||||
{ "TensileFormationUpdate", 256, 32 },
|
||||
{ "GarrisonContain", 256, 32 },
|
||||
{ "HealCrateCollide", 32, 32 },
|
||||
{ "HeightDieUpdate", 32, 32 },
|
||||
{ "FireWeaponWhenDamagedBehavior", 32, 32 },
|
||||
{ "FireWeaponWhenDeadBehavior", 64, 32 },
|
||||
{ "GenerateMinefieldBehavior", 32, 32 },
|
||||
{ "HelicopterSlowDeathBehavior", 64, 32 },
|
||||
{ "ParkingPlaceBehavior", 32, 32 },
|
||||
#ifdef ALLOW_SURRENDER
|
||||
{ "POWTruckAIUpdate", 32, 32, },
|
||||
{ "POWTruckBehavior", 32, 32, },
|
||||
{ "PrisonBehavior", 32, 32 },
|
||||
{ "PrisonVisual", 32, 32 },
|
||||
{ "PropagandaCenterBehavior", 16, 16 },
|
||||
#endif
|
||||
{ "PropagandaTowerBehavior", 16, 16 },
|
||||
{ "ObjectTracker", 128, 32 },
|
||||
{ "OCLUpdate", 16, 16 },
|
||||
{ "BodyParticleSystem", 128, 64 },
|
||||
{ "HighlanderBody", 2048, 128 },
|
||||
{ "HordeUpdate", 128, 32 },
|
||||
{ "ImmortalBody", 2048, 128 },
|
||||
{ "InactiveBody", 2048, 32 },
|
||||
{ "InstantDeathBehavior", 512, 32 },
|
||||
{ "LaserUpdate", 32, 32 },
|
||||
{ "PointDefenseLaserUpdate", 32, 32 },
|
||||
{ "CleanupHazardUpdate", 32, 32 },
|
||||
{ "AutoFindHealingUpdate", 256, 32 },
|
||||
{ "CommandButtonHuntUpdate", 512, 8 },
|
||||
{ "PilotFindVehicleUpdate", 256, 32 },
|
||||
{ "DemoTrapUpdate", 32, 32 },
|
||||
{ "ParticleUplinkCannonUpdate", 16, 16 },
|
||||
{ "BaikonurLaunchPower", 4, 4 },
|
||||
{ "RadiusDecalUpdate", 16, 16 },
|
||||
{ "BattlePlanUpdate", 32, 32 },
|
||||
{ "LifetimeUpdate", 256, 32 },
|
||||
{ "LocomotorSetUpgrade", 512, 128 },
|
||||
{ "AutoDepositUpdate", 256, 32 },
|
||||
{ "NeutronMissileUpdate", 512, 32 },
|
||||
{ "MoneyCrateCollide", 32, 32 },
|
||||
{ "NeutronMissileSlowDeathBehavior", 8, 8 },
|
||||
{ "OpenContain", 128, 32 },
|
||||
{ "OverchargeBehavior", 32, 32 },
|
||||
{ "OverlordContain", 32, 32 },
|
||||
{ "ParachuteContain", 128, 32 },
|
||||
{ "PhysicsBehavior", 600, 32 },
|
||||
{ "PoisonedBehavior", 512, 64 },
|
||||
{ "ProductionEntry", 32, 32 },
|
||||
{ "ProductionUpdate", 256, 32 },
|
||||
{ "ProjectileStreamUpdate", 32, 32 },
|
||||
{ "ProneUpdate", 128, 32 },
|
||||
{ "QueueProductionExitUpdate", 32, 32 },
|
||||
{ "RadarUpdate", 16, 16 },
|
||||
{ "RadarUpgrade", 16, 16 },
|
||||
{ "SupplyWarehouseCripplingBehavior", 16, 16 },
|
||||
{ "CostModifierUpgrade", 32, 32 },
|
||||
{ "CashBountyPower", 32, 32 },
|
||||
{ "CleanupAreaPower", 32, 32 },
|
||||
{ "ObjectCreationUpgrade", 128, 32 },
|
||||
{ "MinefieldBehavior", 256, 32 },
|
||||
{ "JetSlowDeathBehavior", 64, 32 },
|
||||
{ "RebuildHoleBehavior", 64, 32 },
|
||||
{ "RebuildHoleExposeDie", 64, 32 },
|
||||
{ "RepairDockUpdate", 32, 32 },
|
||||
#ifdef ALLOW_SURRENDER
|
||||
{ "PrisonDockUpdate", 32, 32 },
|
||||
#endif
|
||||
{ "RailedTransportDockUpdate", 16, 16 },
|
||||
{ "RailedTransportAIUpdate", 16, 16 },
|
||||
{ "RailedTransportContain", 16, 16 },
|
||||
{ "RailroadBehavior", 16, 16 },
|
||||
{ "SalvageCrateCollide", 32, 32 },
|
||||
{ "ShroudCrateCollide", 32, 32 },
|
||||
{ "SlavedUpdate", 64, 32 },
|
||||
{ "SlowDeathBehavior", 4096, 32 },
|
||||
{ "SpyVisionUpdate", 16, 16 },
|
||||
{ "DefaultProductionExitUpdate", 32, 32 },
|
||||
{ "SpawnPointProductionExitUpdate", 32, 32 },
|
||||
{ "SpawnBehavior", 32, 32 },
|
||||
{ "SpecialPowerCompletionDie", 32, 32 },
|
||||
{ "SpecialPowerCreate", 32, 32 },
|
||||
{ "PreorderCreate", 32, 32 },
|
||||
{ "SpecialAbility", 512, 32 },
|
||||
{ "SpecialAbilityUpdate", 512, 32 },
|
||||
{ "MissileLauncherBuildingUpdate", 32, 32 },
|
||||
{ "SquishCollide", 512, 32 },
|
||||
{ "StructureBody", 512, 64 },
|
||||
{ "HiveStructureBody", 64, 32 }, //Stinger sites
|
||||
{ "StructureCollapseUpdate", 32, 32 },
|
||||
{ "StructureToppleUpdate", 32, 32 },
|
||||
{ "SupplyCenterCreate", 32, 32 },
|
||||
{ "SupplyCenterDockUpdate", 32, 32 },
|
||||
{ "SupplyCenterProductionExitUpdate", 32, 32 },
|
||||
{ "SupplyTruckStateMachine", 256, 32 },
|
||||
{ "SupplyTruckAIUpdate", 32, 32 },
|
||||
{ "SupplyWarehouseCreate", 32, 32 },
|
||||
{ "SupplyWarehouseDockUpdate", 32, 32 },
|
||||
{ "EnemyNearUpdate", 1024, 32 },
|
||||
{ "TechBuildingBehavior", 32, 32 },
|
||||
{ "ToppleUpdate", 2048, 32 },
|
||||
{ "TransitionDamageFX", 256, 32 },
|
||||
{ "TransportAIUpdate", 64, 32 },
|
||||
{ "TransportContain", 128, 32 },
|
||||
{ "TunnelContain", 16, 16 },
|
||||
{ "TunnelContainDie", 32, 32 },
|
||||
{ "TunnelCreate", 32, 32 },
|
||||
{ "TurretAI", 256, 32 },
|
||||
{ "TurretStateMachine", 128, 32 },
|
||||
{ "TurretSwapUpgrade", 512, 128 },
|
||||
{ "UnitCrateCollide", 32, 32 },
|
||||
{ "UnpauseSpecialPowerUpgrade", 32, 32 },
|
||||
{ "VeterancyCrateCollide", 32, 32 },
|
||||
{ "VeterancyGainCreate", 512, 128 },
|
||||
{ "ConvertToCarBombCrateCollide", 32, 32 },
|
||||
{ "ConvertToHijackedVehicleCrateCollide", 32, 32 },
|
||||
{ "JetAIUpdate", 64, 32 },
|
||||
{ "ChinookAIUpdate", 32, 32 },
|
||||
{ "WanderAIUpdate", 32, 32 },
|
||||
{ "WaveGuideUpdate", 16, 16 },
|
||||
{ "WeaponBonusUpgrade", 512, 128 },
|
||||
{ "WeaponSetUpgrade", 512, 128 },
|
||||
{ "ArmorUpgrade", 512, 128 },
|
||||
{ "WorkerAIUpdate", 128, 128 },
|
||||
{ "WorkerStateMachine", 128, 128 },
|
||||
{ "ChinookAIStateMachine", 32, 32 },
|
||||
{ "DeployStyleAIUpdate", 32, 32 },
|
||||
{ "AssaultTransportAIUpdate", 64, 32 },
|
||||
{ "StreamingArchiveFile", 8, 8 },
|
||||
|
||||
{ "DozerActionStateMachine", 256, 32 },
|
||||
{ "DozerPrimaryStateMachine", 256, 32 },
|
||||
{ "W3DDisplayString", 1024, 128 },
|
||||
{ "W3DDefaultDraw", 1024, 128 },
|
||||
{ "W3DDebrisDraw", 1024, 128 },
|
||||
{ "W3DDependencyModelDraw", 64, 64 },
|
||||
{ "W3DLaserDraw", 32, 32 },
|
||||
{ "W3DModelDraw", 4096, 128 },
|
||||
{ "W3DOverlordTankDraw", 64, 64 },
|
||||
{ "W3DPoliceCarDraw", 32, 32 },
|
||||
{ "W3DProjectileStreamDraw", 32, 32 },
|
||||
{ "W3DRopeDraw", 32, 32 },
|
||||
{ "W3DScienceModelDraw", 32, 32 },
|
||||
{ "W3DSupplyDraw", 32, 32 },
|
||||
{ "W3DTankDraw", 256, 32 },
|
||||
{ "W3DTracerDraw", 64, 32 },
|
||||
{ "W3DTruckDraw", 128, 32 },
|
||||
{ "W3DTankTruckDraw", 32, 16 },
|
||||
{ "DefaultSpecialPower", 32, 32 },
|
||||
{ "OCLSpecialPower", 32, 32 },
|
||||
#ifdef ALLOW_DEMORALIZE
|
||||
{ "DemoralizeSpecialPower", 16, 16, },
|
||||
#endif
|
||||
{ "CashHackSpecialPower", 32, 32 },
|
||||
{ "CommandSetUpgrade", 32, 32 },
|
||||
{ "GrantUpgradeCreate", 256, 32 },
|
||||
{ "SpyVisionSpecialPower", 256, 32 },
|
||||
{ "StealthDetectorUpdate", 256, 32 },
|
||||
{ "StealthUpdate", 256, 32 },
|
||||
{ "StealthUpgrade", 256, 32 },
|
||||
{ "StatusBitsUpgrade", 128, 128 },
|
||||
{ "SubObjectsUpgrade", 128, 128 },
|
||||
{ "ExperienceScalarUpgrade", 256, 128 },
|
||||
{ "MaxHealthUpgrade", 128, 128 },
|
||||
{ "WeaponBonusUpgrade", 128, 64 },
|
||||
{ "StickyBombUpdate", 64, 32 },
|
||||
{ "FireOCLAfterWeaponCooldownUpdate", 64, 32 },
|
||||
{ "HijackerUpdate", 64, 32 },
|
||||
{ "ChinaMinesUpgrade", 64, 32 },
|
||||
{ "PowerPlantUpdate", 16, 16 },
|
||||
{ "PowerPlantUpgrade", 16, 16 },
|
||||
{ "DefectorSpecialPower", 16, 16 },
|
||||
{ "CheckpointUpdate", 16, 16 },
|
||||
{ "MobNexusContain", 128, 32 },
|
||||
{ "MobMemberSlavedUpdate", 64, 32 },
|
||||
{ "EMPUpdate", 64, 32 },
|
||||
{ "Overridable", 32, 32 },
|
||||
|
||||
{ "W3DGameWindow", 1024, 32 },
|
||||
{ "SuccessState", 32, 32 },
|
||||
{ "FailureState", 32, 32 },
|
||||
{ "ContinueState", 32, 32 },
|
||||
{ "SleepState", 32, 32 },
|
||||
|
||||
{ "AIDockWaitForClearanceState", 256, 32 },
|
||||
{ "AIDockProcessDockState", 256, 32 },
|
||||
{ "AIGuardInnerState", 32, 32 },
|
||||
{ "AIGuardIdleState", 32, 32 },
|
||||
{ "AIGuardOuterState", 32, 32 },
|
||||
{ "AIGuardReturnState", 32, 32 },
|
||||
{ "AIGuardPickUpCrateState", 32, 32 },
|
||||
{ "AIGuardAttackAggressorState", 32, 32 },
|
||||
{ "AITNGuardInnerState", 32, 32 },
|
||||
{ "AITNGuardIdleState", 32, 32 },
|
||||
{ "AITNGuardOuterState", 32, 32 },
|
||||
{ "AITNGuardReturnState", 32, 32 },
|
||||
{ "AITNGuardPickUpCrateState", 32, 32 },
|
||||
{ "AITNGuardAttackAggressorState", 32, 32 },
|
||||
{ "AIIdleState", 2400, 32 },
|
||||
{ "AIRappelState", 600, 32 },
|
||||
{ "AIBusyState", 600, 32 },
|
||||
{ "AIWaitState", 600, 32 },
|
||||
{ "AIAttackState", 4096, 32 },
|
||||
{ "AIAttackSquadState", 600, 32 },
|
||||
{ "AIDeadState", 600, 32 },
|
||||
{ "AIDockState", 600, 32 },
|
||||
{ "AIExitState", 600, 32 },
|
||||
{ "AIGuardState", 600, 32 },
|
||||
{ "AITunnelNetworkGuardState", 600, 32 },
|
||||
{ "AIHuntState", 600, 32 },
|
||||
{ "AIAttackAreaState", 600, 32 },
|
||||
{ "AIFaceState", 1200, 32 },
|
||||
{ "ApproachState", 600, 32 },
|
||||
{ "DeliveringState", 600, 32 },
|
||||
{ "ConsiderNewApproachState", 600, 32 },
|
||||
{ "RecoverFromOffMapState", 600, 32 },
|
||||
{ "HeadOffMapState", 600, 32 },
|
||||
{ "CleanUpState", 600, 32 },
|
||||
{ "HackInternetState", 600, 32 },
|
||||
{ "PackingState", 600, 32 },
|
||||
{ "UnpackingState", 600, 32 },
|
||||
{ "SupplyTruckWantsToPickUpOrDeliverBoxesState", 600, 32 },
|
||||
{ "RegroupingState", 600, 32 },
|
||||
{ "DockingState", 600, 32 },
|
||||
{ "ChinookEvacuateState", 32, 32 },
|
||||
{ "ChinookHeadOffMapState", 32, 32 },
|
||||
{ "ChinookTakeoffOrLandingState", 32, 32 },
|
||||
{ "ChinookCombatDropState", 32, 32 },
|
||||
{ "DozerActionPickActionPosState", 256, 32 },
|
||||
{ "DozerActionMoveToActionPosState", 256, 32 },
|
||||
{ "DozerActionDoActionState", 256, 32 },
|
||||
{ "DozerPrimaryIdleState", 256, 32 },
|
||||
{ "DozerActionState", 256, 32 },
|
||||
{ "DozerPrimaryGoingHomeState", 256, 32 },
|
||||
{ "JetAwaitingRunwayState", 64, 32 },
|
||||
{ "JetOrHeliCirclingDeadAirfieldState", 64, 32 },
|
||||
{ "HeliTakeoffOrLandingState", 64, 32 },
|
||||
{ "JetOrHeliParkOrientState", 64, 32 },
|
||||
{ "JetOrHeliReloadAmmoState", 64, 32 },
|
||||
{ "SupplyTruckBusyState", 600, 32 },
|
||||
{ "SupplyTruckIdleState", 600, 32 },
|
||||
{ "ActAsDozerState", 600, 32 },
|
||||
{ "ActAsSupplyTruckState", 600, 32 },
|
||||
{ "AIDockApproachState", 256, 32 },
|
||||
{ "AIDockAdvancePositionState", 256, 32 },
|
||||
{ "AIDockMoveToEntryState", 256, 32 },
|
||||
{ "AIDockMoveToDockState", 256, 32 },
|
||||
{ "AIDockMoveToExitState", 256, 32 },
|
||||
{ "AIDockMoveToRallyState", 256, 32 },
|
||||
{ "AIMoveToState", 600, 32 },
|
||||
{ "AIMoveOutOfTheWayState", 600, 32 },
|
||||
{ "AIMoveAndTightenState", 600, 32 },
|
||||
{ "AIMoveAwayFromRepulsorsState", 600, 32 },
|
||||
{ "AIAttackApproachTargetState", 96, 32 },
|
||||
{ "AIAttackPursueTargetState", 96, 32 },
|
||||
{ "AIAttackAimAtTargetState", 96, 32 },
|
||||
{ "AIAttackFireWeaponState", 256, 32 },
|
||||
{ "AIPickUpCrateState", 4096, 32 },
|
||||
{ "AIFollowWaypointPathState", 1200, 32 },
|
||||
{ "AIFollowWaypointPathExactState", 1200, 32 },
|
||||
{ "AIWanderInPlaceState", 600, 32 },
|
||||
{ "AIFollowPathState", 1200, 32 },
|
||||
{ "AIMoveAndEvacuateState", 1200, 32 },
|
||||
{ "AIMoveAndDeleteState", 600, 32 },
|
||||
{ "AIEnterState", 600, 32 },
|
||||
{ "JetOrHeliReturningToDeadAirfieldState", 64, 32 },
|
||||
{ "JetOrHeliReturnForLandingState", 64, 32 },
|
||||
{ "TurretAIIdleState", 600, 32 },
|
||||
{ "TurretAIIdleScanState", 600, 32 },
|
||||
{ "TurretAIAimTurretState", 600, 32 },
|
||||
{ "TurretAIRecenterTurretState", 600, 32 },
|
||||
{ "TurretAIHoldTurretState", 600, 32 },
|
||||
{ "JetOrHeliTaxiState", 64, 32 },
|
||||
{ "JetTakeoffOrLandingState", 64, 32 },
|
||||
{ "JetPauseBeforeTakeoffState", 64, 32 },
|
||||
{ "AIAttackMoveToState", 600, 32 },
|
||||
{ "AIAttackFollowWaypointPathState", 1200, 32 },
|
||||
{ "AIWanderState", 600, 32 },
|
||||
{ "AIPanicState", 600, 32 },
|
||||
{ "ChinookMoveToBldgState", 32, 32 },
|
||||
{ "ScienceInfo", 64, 32 },
|
||||
{ "RankInfo", 32, 32 },
|
||||
|
||||
{ "FireWeaponNugget", 32, 32 },
|
||||
{ "AttackNugget", 32, 32 },
|
||||
{ "DeliverPayloadNugget", 32, 32 },
|
||||
{ "ApplyRandomForceNugget", 32, 32 },
|
||||
{ "GenericObjectCreationNugget", 512, 32 },
|
||||
{ "SoundFXNugget", 256, 32 },
|
||||
{ "TracerFXNugget", 32, 32 },
|
||||
{ "RayEffectFXNugget", 32, 32 },
|
||||
{ "LightPulseFXNugget", 64, 32 },
|
||||
{ "ViewShakeFXNugget", 128, 32 },
|
||||
{ "TerrainScorchFXNugget", 32, 32 },
|
||||
{ "ParticleSystemFXNugget", 600, 32 },
|
||||
{ "FXListAtBonePosFXNugget", 32, 32 },
|
||||
{ "Squad", 256, 32 },
|
||||
{ "BuildListInfo", 256, 32 },
|
||||
|
||||
{ "ScriptGroup", 128, 32 },
|
||||
{ "OrCondition", 1024, 256 },
|
||||
{ "ScriptAction", 2048, 512 },
|
||||
{ "Script", 1024, 256 },
|
||||
{ "Parameter", 8192, 1024 },
|
||||
{ "Condition", 2048, 256 },
|
||||
{ "Template", 32, 32 },
|
||||
{ "ScriptList", 32, 32 },
|
||||
{ "AttackPriorityInfo", 32, 32 },
|
||||
{ "SequentialScript", 32, 32 },
|
||||
{ "Win32LocalFile", 1024, 256 },
|
||||
{ "RAMFile", 32, 32 },
|
||||
{ "BattlePlanBonuses", 32, 32 },
|
||||
{ "KindOfPercentProductionChange", 32, 32 },
|
||||
{ "UserParser", 4096, 256 },
|
||||
{ "XferBlockData", 32, 32 },
|
||||
{ "EvaCheckInfo", 32, 32 },
|
||||
{ "SuperweaponInfo", 32, 32 },
|
||||
{ "NamedTimerInfo", 32, 32 },
|
||||
{ "PopupMessageData", 32, 32 },
|
||||
{ "FloatingTextData", 32, 32 },
|
||||
{ "MapObject", 4096, 32 },
|
||||
{ "Waypoint", 1024, 32 },
|
||||
{ "PolygonTrigger", 128, 32 },
|
||||
{ "Bridge", 32, 32 },
|
||||
{ "Mapping", 128, 32 },
|
||||
{ "OutputChunk", 32, 32 },
|
||||
{ "InputChunk", 32, 32 },
|
||||
{ "AnimateWindow", 32, 32 },
|
||||
{ "GameFont", 32, 32 },
|
||||
{ "NetCommandRef", 256, 32 },
|
||||
{ "GameMessageArgument", 128, 32 },
|
||||
{ "GameMessageParserArgumentType", 32, 32 },
|
||||
{ "GameMessageParser", 32, 32 },
|
||||
{ "WeaponBonusSet", 32, 32 },
|
||||
{ "Campaign", 32, 32 },
|
||||
{ "Mission", 32, 32 },
|
||||
{ "ModalWindow", 32, 32 },
|
||||
{ "NetPacket", 32, 32 },
|
||||
{ "AISideInfo", 32, 32 },
|
||||
{ "AISideBuildList", 32, 32 },
|
||||
{ "MetaMapRec", 256, 32 },
|
||||
{ "TransportStatus", 32, 32 },
|
||||
{ "Anim2DTemplate", 32, 32 },
|
||||
{ "ObjectTypes", 32, 32 },
|
||||
{ "NetCommandList", 512, 32 },
|
||||
{ "TurretAIData", 256, 32 },
|
||||
{ "NetCommandMsg", 32, 32 },
|
||||
{ "NetGameCommandMsg", 64, 32 },
|
||||
{ "NetAckBothCommandMsg", 32, 32 },
|
||||
{ "NetAckStage1CommandMsg", 32, 32 },
|
||||
{ "NetAckStage2CommandMsg", 32, 32 },
|
||||
{ "NetFrameCommandMsg", 32, 32 },
|
||||
{ "NetPlayerLeaveCommandMsg", 32, 32 },
|
||||
{ "NetRunAheadMetricsCommandMsg", 32, 32 },
|
||||
{ "NetRunAheadCommandMsg", 32, 32 },
|
||||
{ "NetDestroyPlayerCommandMsg", 32, 32 },
|
||||
{ "NetDisconnectFrameCommandMsg", 32, 32 },
|
||||
{ "NetDisconnectScreenOffCommandMsg", 32, 32 },
|
||||
{ "NetFrameResendRequestCommandMsg", 32, 32 },
|
||||
{ "NetKeepAliveCommandMsg", 32, 32 },
|
||||
{ "NetDisconnectKeepAliveCommandMsg", 32, 32 },
|
||||
{ "NetDisconnectPlayerCommandMsg", 32, 32 },
|
||||
{ "NetPacketRouterQueryCommandMsg", 32, 32 },
|
||||
{ "NetPacketRouterAckCommandMsg", 32, 32 },
|
||||
{ "NetDisconnectChatCommandMsg", 32, 32 },
|
||||
{ "NetChatCommandMsg", 32, 32 },
|
||||
{ "NetDisconnectVoteCommandMsg", 32, 32 },
|
||||
{ "NetProgressCommandMsg", 32, 32 },
|
||||
{ "NetWrapperCommandMsg", 32, 32 },
|
||||
{ "NetFileCommandMsg", 32, 32 },
|
||||
{ "NetFileAnnounceCommandMsg", 32, 32 },
|
||||
{ "NetFileProgressCommandMsg", 32, 32 },
|
||||
{ "NetCommandWrapperListNode", 32, 32 },
|
||||
{ "NetCommandWrapperList", 32, 32 },
|
||||
{ "Connection", 32, 32 },
|
||||
{ "User", 32, 32 },
|
||||
{ "FrameDataManager", 32, 32 },
|
||||
{ "DrawableIconInfo", 32, 32 },
|
||||
{ "TintEnvelope", 128, 32 },
|
||||
{ "DynamicAudioEventRTS", 1024, 256 },
|
||||
{ "DrawableLocoInfo", 128, 32 },
|
||||
{ "W3DPrototypeClass", 2048, 32 },
|
||||
{ "EnumeratedIP", 32, 32 },
|
||||
{ "WaterTransparencySetting", 4, 4 },
|
||||
|
||||
|
||||
// W3D pools!
|
||||
{ "BoxPrototypeClass", 512, 32 },
|
||||
{ "SpherePrototypeClass", 32, 32 },
|
||||
{ "SoundRenderObjPrototypeClass", 32, 32 },
|
||||
{ "RingPrototypeClass", 32, 32 },
|
||||
{ "PrimitivePrototypeClass", 8192, 32 },
|
||||
{ "HModelPrototypeClass", 256, 32 },
|
||||
{ "ParticleEmitterPrototypeClass", 32, 32 },
|
||||
{ "NullPrototypeClass", 32, 32 },
|
||||
{ "HLodPrototypeClass", 512, 32 },
|
||||
{ "DistLODPrototypeClass", 32, 32 },
|
||||
{ "DazzlePrototypeClass", 32, 32 },
|
||||
{ "CollectionPrototypeClass", 32, 32 },
|
||||
{ "BoxPrototypeClass", 256, 32 },
|
||||
{ "AggregatePrototypeClass", 32, 32 },
|
||||
{ "OBBoxRenderObjClass", 16384, 32 },
|
||||
{ "AABoxRenderObjClass", 32, 32 },
|
||||
{ "VertexMaterialClass", 16384, 32 },
|
||||
{ "TextureClass", 1024, 32 },
|
||||
{ "CloudMapTerrainTextureClass", 32, 32 },
|
||||
{ "ScorchTextureClass", 32, 32 },
|
||||
{ "LightMapTerrainTextureClass", 32, 32 },
|
||||
{ "AlphaEdgeTextureClass", 32, 32 },
|
||||
{ "AlphaTerrainTextureClass", 32, 32 },
|
||||
{ "TerrainTextureClass", 32, 32 },
|
||||
{ "MeshClass", 16384, 1024 },
|
||||
{ "HTreeClass", 8192, 32 },
|
||||
{ "HLodDefClass", 512, 32 },
|
||||
{ "HLodClass", 4096, 32 },
|
||||
{ "MeshModelClass", 8192, 32 },
|
||||
{ "ShareBufferClass", 32768, 1024 },
|
||||
{ "AABTreeClass", 32, 32 },
|
||||
{ "MotionChannelClass", 16384, 32 },
|
||||
{ "BitChannelClass", 64, 32 },
|
||||
{ "TimeCodedMotionChannelClass", 32, 32 },
|
||||
{ "AdaptiveDeltaMotionChannelClass", 32, 32 },
|
||||
{ "TimeCodedBitChannelClass", 32, 32 },
|
||||
{ "UVBufferClass", 8192, 32 },
|
||||
{ "TexBufferClass", 512, 32 },
|
||||
{ "MatBufferClass", 512, 32 },
|
||||
{ "MatrixMapperClass", 32, 32 },
|
||||
{ "ScaleTextureMapperClass", 32, 32 },
|
||||
{ "LinearOffsetTextureMapperClass", 32, 32 },
|
||||
{ "GridTextureMapperClass", 32, 32 },
|
||||
{ "RotateTextureMapperClass", 32, 32 },
|
||||
{ "SineLinearOffsetTextureMapperClass", 32, 32 },
|
||||
{ "StepLinearOffsetTextureMapperClass", 32, 32 },
|
||||
{ "ZigZagLinearOffsetTextureMapperClass", 32, 32 },
|
||||
{ "ClassicEnvironmentMapperClass", 32, 32 },
|
||||
{ "EnvironmentMapperClass", 256, 32 },
|
||||
{ "EdgeMapperClass", 32, 32 },
|
||||
{ "WSClassicEnvironmentMapperClass", 32, 32 },
|
||||
{ "WSEnvironmentMapperClass", 32, 32 },
|
||||
{ "GridClassicEnvironmentMapperClass", 32, 32 },
|
||||
{ "GridEnvironmentMapperClass", 32, 32 },
|
||||
{ "ScreenMapperClass", 32, 32 },
|
||||
{ "RandomTextureMapperClass", 32, 32 },
|
||||
{ "BumpEnvTextureMapperClass", 32, 32 },
|
||||
{ "MeshLoadContextClass", 32, 32 },
|
||||
{ "MaterialInfoClass", 8192, 32 },
|
||||
{ "MeshMatDescClass", 8192, 32 },
|
||||
{ "TextureLoadTaskClass", 256, 32 },
|
||||
{ "SortingNodeStruct", 256, 32 },
|
||||
{ "ProxyArrayClass", 32, 32 },
|
||||
{ "Line3DClass", 128, 32 },
|
||||
{ "Render2DClass", 64, 32 },
|
||||
{ "SurfaceClass", 128, 32 },
|
||||
{ "FontCharsClassCharDataStruct", 1024, 32 },
|
||||
{ "FontCharsBuffer", 16, 4 },
|
||||
{ "FVFInfoClass", 128, 32 },
|
||||
{ "TerrainTracksRenderObjClass", 128, 32 },
|
||||
{ "DynamicIBAccessClass", 32, 32 },
|
||||
{ "DX8IndexBufferClass", 128, 32 },
|
||||
{ "SortingIndexBufferClass", 32, 32 },
|
||||
{ "DX8VertexBufferClass", 128, 32 },
|
||||
{ "SortingVertexBufferClass", 32, 32 },
|
||||
{ "DynD3DMATERIAL8", 8192, 32 },
|
||||
{ "DynamicMatrix3D", 512, 32 },
|
||||
{ "MeshGeometryClass", 32, 32 },
|
||||
{ "DynamicMeshModel", 32, 32 },
|
||||
{ "GapFillerClass", 32, 32 },
|
||||
{ "FontCharsClass", 64, 32 },
|
||||
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void userMemoryAdjustPoolSize(const char *poolName, Int& initialAllocationCount, Int& overflowAllocationCount)
|
||||
{
|
||||
if (initialAllocationCount > 0)
|
||||
return;
|
||||
|
||||
for (const PoolSizeRec* p = sizes; p->name != NULL; ++p)
|
||||
{
|
||||
if (strcmp(p->name, poolName) == 0)
|
||||
{
|
||||
initialAllocationCount = p->initial;
|
||||
overflowAllocationCount = p->overflow;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_CRASH(("Initial size for pool %s not found -- you should add it to MemoryInit.cpp\n",poolName));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static Int roundUpMemBound(Int i)
|
||||
{
|
||||
const int MEM_BOUND_ALIGNMENT = 4;
|
||||
|
||||
if (i < MEM_BOUND_ALIGNMENT)
|
||||
return MEM_BOUND_ALIGNMENT;
|
||||
else
|
||||
return (i + (MEM_BOUND_ALIGNMENT-1)) & ~(MEM_BOUND_ALIGNMENT-1);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void userMemoryManagerInitPools()
|
||||
{
|
||||
// note that we MUST use stdio stuff here, and not the normal game file system
|
||||
// (with bigfile support, etc), because that relies on memory pools, which
|
||||
// aren't yet initialized properly! so rely ONLY on straight stdio stuff here.
|
||||
// (not even AsciiString. thanks.)
|
||||
|
||||
// since we're called prior to main, the cur dir might not be what
|
||||
// we expect. so do it the hard way.
|
||||
char buf[_MAX_PATH];
|
||||
::GetModuleFileName(NULL, buf, sizeof(buf));
|
||||
char* pEnd = buf + strlen(buf);
|
||||
while (pEnd != buf)
|
||||
{
|
||||
if (*pEnd == '\\')
|
||||
{
|
||||
*pEnd = 0;
|
||||
break;
|
||||
}
|
||||
--pEnd;
|
||||
}
|
||||
strcat(buf, "\\Data\\INI\\MemoryPools.ini");
|
||||
|
||||
FILE* fp = fopen(buf, "r");
|
||||
if (fp)
|
||||
{
|
||||
char poolName[256];
|
||||
int initial, overflow;
|
||||
while (fgets(buf, _MAX_PATH, fp))
|
||||
{
|
||||
if (buf[0] == ';')
|
||||
continue;
|
||||
if (sscanf(buf, "%s %d %d", poolName, &initial, &overflow ) == 3)
|
||||
{
|
||||
for (PoolSizeRec* p = sizes; p->name != NULL; ++p)
|
||||
{
|
||||
if (stricmp(p->name, poolName) == 0)
|
||||
{
|
||||
// currently, these must be multiples of 4. so round up.
|
||||
p->initial = roundUpMemBound(initial);
|
||||
p->overflow = roundUpMemBound(overflow);
|
||||
break; // from for-p
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
82
Generals/Code/GameEngine/Source/Common/System/QuickTrig.cpp
Normal file
82
Generals/Code/GameEngine/Source/Common/System/QuickTrig.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// (c) 2001-2003 Electronic Arts Inc. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FILE: QuickTrig.cpp ////////////////////////////////////////////////////////////////////////////////
|
||||
// Author: Mark Lorenzen (adapted by srj)
|
||||
// Desc: fast trig
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
|
||||
|
||||
#include "Common/QuickTrig.h"
|
||||
|
||||
// GLOBALS ////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Real TheQuickSinTable[] =
|
||||
{
|
||||
0.00000f, 0.01237f, 0.02473f, 0.03710f, 0.04945f, 0.06180f, 0.07414f, 0.08647f,
|
||||
0.09879f, 0.11109f, 0.12337f, 0.13563f, 0.14788f, 0.16010f, 0.17229f, 0.18446f,
|
||||
0.19661f, 0.20872f, 0.22080f, 0.23284f, 0.24485f, 0.25683f, 0.26876f, 0.28065f,
|
||||
0.29250f, 0.30431f, 0.31607f, 0.32778f, 0.33944f, 0.35104f, 0.36260f, 0.37410f,
|
||||
0.38554f, 0.39692f, 0.40824f, 0.41950f, 0.43070f, 0.44183f, 0.45289f, 0.46388f,
|
||||
0.47480f, 0.48565f, 0.49643f, 0.50712f, 0.51774f, 0.52829f, 0.53875f, 0.54913f,
|
||||
0.55942f, 0.56963f, 0.57975f, 0.58978f, 0.59973f, 0.60958f, 0.61934f, 0.62900f,
|
||||
0.63857f, 0.64804f, 0.65741f, 0.66668f, 0.67584f, 0.68491f, 0.69387f, 0.70272f,
|
||||
0.71147f, 0.72010f, 0.72863f, 0.73705f, 0.74535f, 0.75354f, 0.76161f, 0.76957f,
|
||||
0.77741f, 0.78513f, 0.79273f, 0.80020f, 0.80756f, 0.81479f, 0.82190f, 0.82888f,
|
||||
0.83574f, 0.84247f, 0.84907f, 0.85554f, 0.86187f, 0.86808f, 0.87415f, 0.88009f,
|
||||
0.88590f, 0.89157f, 0.89710f, 0.90250f, 0.90775f, 0.91287f, 0.91785f, 0.92269f,
|
||||
0.92739f, 0.93195f, 0.93636f, 0.94063f, 0.94476f, 0.94874f, 0.95257f, 0.95626f,
|
||||
0.95981f, 0.96321f, 0.96646f, 0.96956f, 0.97251f, 0.97532f, 0.97798f, 0.98048f,
|
||||
0.98284f, 0.98505f, 0.98710f, 0.98901f, 0.99076f, 0.99236f, 0.99381f, 0.99511f,
|
||||
0.99625f, 0.99725f, 0.99809f, 0.99878f, 0.99931f, 0.99969f, 0.99992f, 1.00000f,
|
||||
0.99992f
|
||||
};
|
||||
|
||||
Real TheQuickTanTable[] =
|
||||
{
|
||||
0.00000f, 0.00787f, 0.01575f, 0.02363f, 0.03151f, 0.03939f, 0.04728f, 0.05517f,
|
||||
0.06308f, 0.07099f, 0.07890f, 0.08683f, 0.09477f, 0.10272f, 0.11068f, 0.11866f,
|
||||
0.12666f, 0.13466f, 0.14269f, 0.15073f, 0.15880f, 0.16688f, 0.17498f, 0.18311f,
|
||||
0.19126f, 0.19943f, 0.20763f, 0.21586f, 0.22412f, 0.23240f, 0.24071f, 0.24906f,
|
||||
0.25744f, 0.26585f, 0.27430f, 0.28279f, 0.29131f, 0.29987f, 0.30847f, 0.31712f,
|
||||
0.32581f, 0.33454f, 0.34332f, 0.35214f, 0.36102f, 0.36994f, 0.37892f, 0.38795f,
|
||||
0.39704f, 0.40618f, 0.41539f, 0.42465f, 0.43398f, 0.44337f, 0.45282f, 0.46234f,
|
||||
0.47194f, 0.48160f, 0.49134f, 0.50115f, 0.51104f, 0.52101f, 0.53106f, 0.54120f,
|
||||
0.55143f, 0.56174f, 0.57214f, 0.58264f, 0.59324f, 0.60393f, 0.61473f, 0.62563f,
|
||||
0.63664f, 0.64777f, 0.65900f, 0.67035f, 0.68183f, 0.69342f, 0.70515f, 0.71700f,
|
||||
0.72899f, 0.74112f, 0.75339f, 0.76581f, 0.77838f, 0.79110f, 0.80398f, 0.81703f,
|
||||
0.83025f, 0.84363f, 0.85720f, 0.87096f, 0.88490f, 0.89904f, 0.91338f, 0.92793f,
|
||||
0.94269f, 0.95767f, 0.97288f, 0.98833f, 1.00401f, 1.01995f, 1.03615f, 1.05261f,
|
||||
1.06935f, 1.08637f, 1.10368f, 1.12130f, 1.13924f, 1.15749f, 1.17609f, 1.19503f,
|
||||
1.21433f, 1.23400f, 1.25406f, 1.27452f, 1.29540f, 1.31670f, 1.33845f, 1.36067f,
|
||||
1.38336f, 1.40656f, 1.43027f, 1.45453f, 1.47935f, 1.50475f, 1.53076f, 1.55741f,
|
||||
1.58471f
|
||||
};
|
||||
|
||||
// yes, Real. No, really.
|
||||
Real TheQuickSinTableCount = sizeof(TheQuickSinTable) / sizeof(Real);
|
||||
Real TheQuickTanTableCount = sizeof(TheQuickTanTable) / sizeof(Real);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user