Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA 2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

View file

@ -0,0 +1,883 @@
/*
** 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: Anim2D.cpp ///////////////////////////////////////////////////////////////////////////////
// Author: Colin Day, July 2002
// Desc: A collection of 2D images to make animation
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_ANIM_2D_MODE_NAMES
#include "Common/RandomValue.h"
#include "Common/Xfer.h"
#include "GameClient/Anim2D.h"
#include "GameClient/Display.h"
#include "GameClient/Image.h"
#include "GameLogic/GameLogic.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// GLOBAL /////////////////////////////////////////////////////////////////////////////////////////
Anim2DCollection *TheAnim2DCollection = NULL;
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Anim2DTemplate::Anim2DTemplate( AsciiString name )
{
m_name = name;
m_images = NULL;
m_numFrames = NUM_FRAMES_INVALID;
m_framesBetweenUpdates = 0;
m_animMode = ANIM_2D_LOOP;
m_randomizeStartFrame = FALSE;
m_nextTemplate = NULL;
} // end Anim2DTemplate
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Anim2DTemplate::~Anim2DTemplate( void )
{
// delete the images
if( m_images )
delete [] m_images;
} // end ~Anim2DTemplate
// ------------------------------------------------------------------------------------------------
/** Field parse table for 2D animation templates */
// ---------i---------------------------------------------------------------------------------------
const FieldParse Anim2DTemplate::s_anim2DFieldParseTable[] =
{
{ "NumberImages", Anim2DTemplate::parseNumImages, NULL, 0 },
{ "Image", Anim2DTemplate::parseImage, NULL, 0 },
{ "ImageSequence", Anim2DTemplate::parseImageSequence, NULL, 0 },
{ "AnimationMode", INI::parseIndexList, Anim2DModeNames, offsetof( Anim2DTemplate, m_animMode ) },
{ "AnimationDelay", INI::parseDurationUnsignedShort, NULL, offsetof( Anim2DTemplate, m_framesBetweenUpdates ) },
{ "RandomizeStartFrame", INI::parseBool, NULL, offsetof( Anim2DTemplate, m_randomizeStartFrame ) },
{ NULL, NULL, NULL, 0 }
};
// ------------------------------------------------------------------------------------------------
/** Parse the number of images we will have in this animation and allocate the array for them */
// ------------------------------------------------------------------------------------------------
void Anim2DTemplate::parseNumImages( INI *ini, void *instance, void *store, const void *userData )
{
// parse the integer data from the file
UnsignedInt numFrames;
ini->parseUnsignedInt( ini, instance, &numFrames, userData );
// get the template we are to store into
Anim2DTemplate *animTemplate = (Anim2DTemplate *)instance;
// animations must have a minimum # of frames
Int minimumFrames = 1;
if( numFrames < minimumFrames )
{
DEBUG_CRASH(( "Anim2DTemplate::parseNumImages - Invalid animation '%s', animations must have '%d' or more frames defined\n",
animTemplate->getName().str(), minimumFrames ));
throw INI_INVALID_DATA;
} // end if
// allocate the image array
animTemplate->allocateImages( (UnsignedShort)numFrames );
} // end parseNumImages
// ------------------------------------------------------------------------------------------------
/** Allocate the image array for an animation template and store the number of frames we have */
// ------------------------------------------------------------------------------------------------
void Anim2DTemplate::allocateImages( UnsignedShort numFrames )
{
// store the number of frames
m_numFrames = numFrames;
// allocate an array to hold the image pointers
m_images = NEW const Image *[ m_numFrames ]; // pool[]ify
// set all the images to NULL;
for( Int i = 0; i < m_numFrames; ++i )
m_images[ i ] = NULL;
} // end allocateImages
// ------------------------------------------------------------------------------------------------
/** Parsing a single image definition for an animation */
// ------------------------------------------------------------------------------------------------
void Anim2DTemplate::parseImage( INI *ini, void *instance, void *store, const void *userData )
{
// parse the image name from the file and store as an image pointer
const Image *image;
ini->parseMappedImage( ini, instance, &image, userData );
// sanity
if( image == NULL )
{
//We don't care if we're in the builder
//DEBUG_CRASH(( "Anim2DTemplate::parseImage - Image not found\n" ));
//throw INI_INVALID_DATA;
} // end if
//
// assign the image to the animation template list of images ... note since we've pre-allocated
// the array of images and the index an image is loaded into depends on its order specified
// in INI, we need to get the number of images currently loaded into this animation template
// so that we can put it at the next free image spot ... and then tell the animation
// template we've loaded one more
//
Anim2DTemplate *animTemplate = (Anim2DTemplate *)instance;
animTemplate->storeImage( image );
} // end parseImage
// ------------------------------------------------------------------------------------------------
/** This will parse the image sequence of an animation. You can use this as a shortcut to
* specifying a series of images for an animation instead of having to specify them all
* individually. Image names will be assumed to start with an appended "000" to the end
* end of the first image name and incremented up to the number of images for the
* animation. NOTE: That the number images *must* have already been specified before
* we can parse this entry so we know how many images to allocate and look for */
// ------------------------------------------------------------------------------------------------
/*static*/ void Anim2DTemplate::parseImageSequence( INI *ini, void *instance,
void *store, const void *userData )
{
// get the animation template
Anim2DTemplate *animTemplate = (Anim2DTemplate *)instance;
//
// before we can read, allocate, and find all the images for the sequence ... we must
// know how many total images are in this animation. Verify that now
//
if( animTemplate->getNumFrames() == NUM_FRAMES_INVALID )
{
DEBUG_CRASH(( "Anim2DTemplate::parseImageSequence - You must specify the number of animation frames for animation '%s' *BEFORE* specifying the image sequence name\n",
animTemplate->getName().str() ));
throw INI_INVALID_DATA;
} // end if
//
// the image storage has already been allocated, all we have to do now is count from
// 0 to numImages - 1 and add those images to the template
//
AsciiString imageBaseName = ini->getNextAsciiString();
AsciiString imageName;
const Image *image;
for( Int i = 0; i < animTemplate->getNumFrames(); ++i )
{
// construct this name
imageName.format( "%s%03d", imageBaseName.str(), i );
// search for this image
image = TheMappedImageCollection->findImageByName( imageName );
// sanity
if( image == NULL )
{
DEBUG_CRASH(( "Anim2DTemplate::parseImageSequence - Image '%s' not found for animation '%s'. Check the number of images specified in INI and also make sure all the actual images exist.\n",
imageName.str(), animTemplate->getName().str() ));
throw INI_INVALID_DATA;
} // end if
// store the image in the next free frame
animTemplate->storeImage( image );
} // end if
} // end parseImageSequence
// ------------------------------------------------------------------------------------------------
/** Store the image at the next open image slot for the animation */
// ------------------------------------------------------------------------------------------------
void Anim2DTemplate::storeImage( const Image *image )
{
// sanity
if( image == NULL )
return;
// search through the image list and store at the next free spot
for( Int i = 0; i < m_numFrames; ++i )
{
if( m_images[ i ] == NULL )
{
m_images[ i ] = image;
return;
} // end if
} // end for i
// if we got here we tried to store an image in an array that was too small
DEBUG_CRASH(( "Anim2DTemplate::storeImage - Unable to store image '%s' into animation '%s' because the animation is setup to only support '%d' image frames\n",
image->getName().str(), getName().str(), m_numFrames ));
throw INI_INVALID_DATA;
} // end storeImage
// ------------------------------------------------------------------------------------------------
/** Return the Image* for the frame number requested */
// ------------------------------------------------------------------------------------------------
const Image* Anim2DTemplate::getFrame( UnsignedShort frameNumber ) const
{
// sanity
DEBUG_ASSERTCRASH( m_images != NULL,
("Anim2DTemplate::getFrame - Image data is NULL for animation '%s'\n",
getName().str()) );
// sanity
if( frameNumber < 0 || frameNumber >= m_numFrames )
{
DEBUG_CRASH(( "Anim2DTemplate::getFrame - Illegal frame number '%d' for animation '%s'\n",
frameNumber, getName().str() ));
return NULL;
} // end if
else
{
// return the image frame
return m_images[ frameNumber ];
} // end else
} // end getFrame
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Anim2D::Anim2D( Anim2DTemplate *animTemplate, Anim2DCollection *collectionSystem )
{
// sanity
DEBUG_ASSERTCRASH( animTemplate != NULL, ("Anim2D::Anim2D - NULL template\n") );
//Added By Sadullah Nader
//Initialization
m_currentFrame = 0;
//
// set the template
m_template = animTemplate;
// initialize members
m_status = ANIM_2D_STATUS_NONE;
m_alpha = 1.0f;
// set the initial frame for the animation based on the type of animation mode or randomize
if( m_template->isRandomizedStartFrame() )
randomizeCurrentFrame();
else
reset();
m_minFrame = 0;
m_maxFrame = m_template->getNumFrames() - 1;
m_framesBetweenUpdates = m_template->getNumFramesBetweenUpdates();
//added by Sadullah Nader
// initializing pointers to NULL, and clearing Frame counters before
// we register ourselves to the System
m_collectionSystemNext = NULL;
m_collectionSystemPrev = NULL;
m_lastUpdateFrame = 0;
// if a system is present, register ourselves with that system
m_collectionSystem = collectionSystem;
if( m_collectionSystem )
m_collectionSystem->registerAnimation( this );
} // end Anim2D
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Anim2D::~Anim2D( void )
{
// if we were registered with a system, un-register ourselves
if( m_collectionSystem )
m_collectionSystem->unRegisterAnimation( this );
} // end ~Anim2D
// ------------------------------------------------------------------------------------------------
/** Set the current animation frame */
// ------------------------------------------------------------------------------------------------
void Anim2D::setCurrentFrame( UnsignedShort frame )
{
// sanity
DEBUG_ASSERTCRASH( m_template != NULL, ("Anim2D::reset - No template for animation\n") );
// sanity
DEBUG_ASSERTCRASH( TheGameLogic != NULL,
("Anim2D::setCurrentFrame - TheGameLogic must exist to use animation instances (%s)\n",
m_template->getName().str()) );
// sanity
DEBUG_ASSERTCRASH( frame >= 0 && frame < m_template->getNumFrames(),
("Anim2D::setCurrentFrame - Illegal frame number '%d' in animation\n",
frame, m_template->getName().str()) );
// set the frame
m_currentFrame = frame;
// record the frame of this update to our current frame
m_lastUpdateFrame = TheGameLogic->getFrame();
} // end setCurrentFrame
// ------------------------------------------------------------------------------------------------
/** Randomize the current frame */
// ------------------------------------------------------------------------------------------------
void Anim2D::randomizeCurrentFrame( void )
{
// sanity
DEBUG_ASSERTCRASH( m_template != NULL, ("Anim2D::reset - No template for animation\n") );
// set the current frame to a random frame
setCurrentFrame( GameClientRandomValue( 0, m_template->getNumFrames() - 1 ) );
} // end randomizeCurrentFrame
// ------------------------------------------------------------------------------------------------
/** Reset this animation instance to the "start" of the animation */
// ------------------------------------------------------------------------------------------------
void Anim2D::reset( void )
{
// sanity
DEBUG_ASSERTCRASH( m_template != NULL, ("Anim2D::reset - No template for animation\n") );
switch( m_template->getAnimMode() )
{
// --------------------------------------------------------------------------------------------
case ANIM_2D_ONCE:
case ANIM_2D_LOOP:
case ANIM_2D_PING_PONG:
setCurrentFrame( m_minFrame );
break;
// --------------------------------------------------------------------------------------------
case ANIM_2D_ONCE_BACKWARDS:
case ANIM_2D_LOOP_BACKWARDS:
case ANIM_2D_PING_PONG_BACKWARDS:
setCurrentFrame( m_maxFrame );
break;
// --------------------------------------------------------------------------------------------
default:
DEBUG_CRASH(( "Anim2D::reset - Unknown animation mode '%d' for '%s'\n",
m_template->getAnimMode(), m_template->getName().str() ));
break;
} // end switch, animation mode
} // end reset
// ------------------------------------------------------------------------------------------------
/** This is called after we are drawn ... if sufficient time has passed since our last
* frame update we will update our current frame */
// ------------------------------------------------------------------------------------------------
void Anim2D::tryNextFrame( void )
{
// sanity
DEBUG_ASSERTCRASH( TheGameLogic != NULL,
("Anim2D::tryNextFrame - TheGameLogic must exist to use animation instances (%s)\n",
m_template->getName().str()) );
// how many frames have passed since our last update
if( TheGameLogic->getFrame() - m_lastUpdateFrame >= m_framesBetweenUpdates )
{
switch( m_template->getAnimMode() )
{
// ------------------------------------------------------------------------------------------
case ANIM_2D_ONCE:
{
if( m_currentFrame < m_maxFrame )
setCurrentFrame( m_currentFrame + 1 );
else
setStatus( ANIM_2D_STATUS_COMPLETE );
break;
} // end once
// -------------------------------------------------------------------------------------------
case ANIM_2D_ONCE_BACKWARDS:
{
if( m_currentFrame > m_minFrame )
setCurrentFrame( m_currentFrame - 1 );
else
setStatus( ANIM_2D_STATUS_COMPLETE );
break;
} // end once backwards
// -------------------------------------------------------------------------------------------
case ANIM_2D_LOOP:
{
if( m_currentFrame == m_maxFrame )
setCurrentFrame( m_minFrame );
else
setCurrentFrame( m_currentFrame + 1 );
break;
} // end loop
// -------------------------------------------------------------------------------------------
case ANIM_2D_LOOP_BACKWARDS:
{
if( m_currentFrame > m_minFrame )
setCurrentFrame( m_currentFrame - 1 );
else
setCurrentFrame( m_maxFrame );
break;
} // end loop backwards
// -------------------------------------------------------------------------------------------
case ANIM_2D_PING_PONG:
case ANIM_2D_PING_PONG_BACKWARDS:
{
if( BitTest( m_status, ANIM_2D_STATUS_REVERSED ) )
{
//
// decrement frame, unless we're at frame 0 in which case we
// increment and reverse directions
//
if( m_currentFrame == m_minFrame )
{
setCurrentFrame( m_currentFrame + 1 );
clearStatus( ANIM_2D_STATUS_REVERSED );
} // end if
else
{
setCurrentFrame( m_currentFrame - 1 );
} // end else
} // end if
else
{
//
// increment frame, unless we're at the end in which case we decrement
// and reverse directions
//
if( m_currentFrame == m_maxFrame )
{
setCurrentFrame( m_currentFrame - 1 );
setStatus( ANIM_2D_STATUS_REVERSED );
} // end if
else
{
setCurrentFrame( m_currentFrame + 1 );
} // end else
} // end else
break;
} // end ping pong / ping pong backwards
// -------------------------------------------------------------------------------------------
default:
{
DEBUG_CRASH(( "Anim2D::tryNextFrame - Unknown animation mode '%d' for '%s'\n",
m_template->getAnimMode(), m_template->getName().str() ));
break;
} // end default
} // end switch
} // end if
} // end tryNextFrame
// ------------------------------------------------------------------------------------------------
/** Set status bit */
// ------------------------------------------------------------------------------------------------
void Anim2D::setStatus( UnsignedByte statusBits )
{
// set the bits
BitSet( m_status, statusBits );
} // end setStatus
// ------------------------------------------------------------------------------------------------
/** Clear status bit */
// ------------------------------------------------------------------------------------------------
void Anim2D::clearStatus( UnsignedByte statusBits )
{
// clear bits
BitClear( m_status, statusBits );
} // end clearStatus
// ------------------------------------------------------------------------------------------------
/** Return the "natural" width of the image for our current frame */
// ------------------------------------------------------------------------------------------------
UnsignedInt Anim2D::getCurrentFrameWidth( void ) const
{
const Image *currentFrameImage = m_template->getFrame( m_currentFrame );
if( currentFrameImage )
return currentFrameImage->getImageWidth();
return 0;
} // end getCurrentFrameWidth
// ------------------------------------------------------------------------------------------------
/** Return the "natural" height of the image for our current frame */
// ------------------------------------------------------------------------------------------------
UnsignedInt Anim2D::getCurrentFrameHeight( void ) const
{
const Image *currentFrameImage = m_template->getFrame( m_currentFrame );
if( currentFrameImage )
return currentFrameImage->getImageHeight();
return 0;
} // end getCurrentFrameHeight
// ------------------------------------------------------------------------------------------------
/** Draw an Anim2D using the natural width and height of the image data */
// ------------------------------------------------------------------------------------------------
void Anim2D::draw( Int x, Int y )
{
// get the current image
const Image *image = m_template->getFrame( m_currentFrame );
// sanity
DEBUG_ASSERTCRASH( image != NULL, ("Anim2D::draw - Image not found for frame '%d' on animation '%s'\n",
m_currentFrame, m_template->getName().str()) );
// get the natural width and height of this image
const ICoord2D *imageSize = image->getImageSize();
// draw the image
Color color = GameMakeColor( 255, 255, 255, 255 * m_alpha );
TheDisplay->drawImage( image, x, y, x + imageSize->x, y + imageSize->y, color );
//
// see if it's time for us to go to the next frame in the sequence, we do not update
// frame numbers for animation instances that are registered with a system as the
// system will update them during its update phase
//
if( m_collectionSystem == NULL && BitTest( m_status, ANIM_2D_STATUS_FROZEN ) == FALSE )
tryNextFrame();
} // end draw
// ------------------------------------------------------------------------------------------------
/** Drawing an Anim2D using a forced width and height */
// ------------------------------------------------------------------------------------------------
void Anim2D::draw( Int x, Int y, Int width, Int height )
{
// get the current image
const Image *image = m_template->getFrame( m_currentFrame );
// sanity
DEBUG_ASSERTCRASH( image != NULL, ("Anim2D::draw - Image not found for frame '%d' on animation '%s'\n",
m_currentFrame, m_template->getName().str()) );
// draw image to the display
Color color = GameMakeColor( 255, 255, 255, 255 * m_alpha );
TheDisplay->drawImage( image, x, y, x + width, y + height, color );
//
// see if it's time for us to go to the next frame in the sequence, we do not update
// frame numbers for animation instances that are registered with a system as the
// system will update them during its update phase
//
if( m_collectionSystem == NULL && BitTest( m_status, ANIM_2D_STATUS_FROZEN ) == FALSE )
tryNextFrame();
} // end draw
// ------------------------------------------------------------------------------------------------
/** Xfer Method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Anim2D::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// current frame
xfer->xferUnsignedShort( &m_currentFrame );
// last update frame
xfer->xferUnsignedInt( &m_lastUpdateFrame );
// status
xfer->xferUnsignedByte( &m_status );
// min frame
xfer->xferUnsignedShort( &m_minFrame );
// max frame
xfer->xferUnsignedShort( &m_maxFrame );
// frames between updates
xfer->xferUnsignedInt( &m_framesBetweenUpdates );
// alpha
xfer->xferReal( &m_alpha );
} // end xfer
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Anim2DCollection::Anim2DCollection( void )
{
m_templateList = NULL;
m_instanceList = NULL;
} // end Anim2DCollection
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Anim2DCollection::~Anim2DCollection( void )
{
// there should not be any animation instances registered with us since we're being destroyed
DEBUG_ASSERTCRASH( m_instanceList == NULL, ("Anim2DCollection - instance list is not NULL\n") );
// delete all the templates
Anim2DTemplate *nextTemplate;
while( m_templateList )
{
// get next template
nextTemplate = m_templateList->friend_getNextTemplate();
// delete this template
m_templateList->deleteInstance();
// set the head of our list to the next template
m_templateList = nextTemplate;
} // end while
} // end ~Anim2DCollection
// ------------------------------------------------------------------------------------------------
/** Initialize 2D animation collection */
// ------------------------------------------------------------------------------------------------
void Anim2DCollection::init( void )
{
INI ini;
ini.load( "Data\\INI\\Animation2D.ini", INI_LOAD_OVERWRITE, NULL );
} // end init
// ------------------------------------------------------------------------------------------------
/** System update phase */
// ------------------------------------------------------------------------------------------------
void Anim2DCollection::update( void )
{
Anim2D *anim;
// go through all our animations
for( anim = m_instanceList; anim; anim = anim->m_collectionSystemNext )
{
// try to update the frame
if( BitTest( anim->getStatus(), ANIM_2D_STATUS_FROZEN ) == FALSE )
anim->tryNextFrame();
} // end for, anim
} // end update
// ------------------------------------------------------------------------------------------------
/** Search the template list for a template with a matching name */
// ------------------------------------------------------------------------------------------------
Anim2DTemplate *Anim2DCollection::findTemplate( const AsciiString& name )
{
// search the list
for( Anim2DTemplate *animTemplate = m_templateList;
animTemplate;
animTemplate = animTemplate->friend_getNextTemplate() )
{
if( animTemplate->getName() == name )
return animTemplate;
} // end for
return NULL; // template not found
} // end findTemplate
//-------------------------------------------------------------------------------------------------
Anim2DTemplate* Anim2DCollection::getNextTemplate( Anim2DTemplate *animTemplate ) const
{
if( animTemplate )
{
return animTemplate->friend_getNextTemplate();
}
return NULL;
}
// ------------------------------------------------------------------------------------------------
/** Allocate a new template, assign name, and link to our internal list */
// ------------------------------------------------------------------------------------------------
Anim2DTemplate *Anim2DCollection::newTemplate( const AsciiString& name )
{
// allocate a new template
Anim2DTemplate *animTemplate = newInstance(Anim2DTemplate)( name );
// link to our template list
animTemplate->friend_setNextTemplate( m_templateList );
m_templateList = animTemplate;
// return the new template
return animTemplate;
} // end newTemplate
// ------------------------------------------------------------------------------------------------
/** Register animation instance with us. When an animation instance is registered it can
* be updated even when it's not drawn */
// ------------------------------------------------------------------------------------------------
void Anim2DCollection::registerAnimation( Anim2D *anim )
{
// sanity
if( anim == NULL )
return;
// sanity
DEBUG_ASSERTCRASH( anim->m_collectionSystemNext == NULL &&
anim->m_collectionSystemPrev == NULL,
("Registering animation instance, instance '%s' is already in a system\n",
anim->getAnimTemplate()->getName().str()) );
// tie to our list
anim->m_collectionSystemPrev = NULL;
anim->m_collectionSystemNext = m_instanceList;
if( m_instanceList )
m_instanceList->m_collectionSystemPrev = anim;
m_instanceList = anim;
} // end registerAnimation
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void Anim2DCollection::unRegisterAnimation( Anim2D *anim )
{
// sanity
if( anim == NULL )
return;
// if animation is not registered with us do nothing
if( anim->m_collectionSystem != this )
return;
// unlink from our instnace list
if( anim->m_collectionSystemNext )
anim->m_collectionSystemNext->m_collectionSystemPrev = anim->m_collectionSystemPrev;
if( anim->m_collectionSystemPrev )
anim->m_collectionSystemPrev->m_collectionSystemNext = anim->m_collectionSystemNext;
else
m_instanceList = anim->m_collectionSystemNext;
} // end unRegisterAnimation

View file

@ -0,0 +1,459 @@
/*
** 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: CampaignManager.cpp /////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Electronic Arts Pacific.
//
// Confidential Information
// Copyright (C) 2002 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// created: Jul 2002
//
// Filename: CampaignManager.cpp
//
// author: Chris Huybregts
//
// purpose: The flow of the campaigns are stored up in here!
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// USER INCLUDES //////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/Xfer.h"
#include "GameClient/CampaignManager.h"
#include "GameClient/GameClient.h"
//-----------------------------------------------------------------------------
// DEFINES ////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
CampaignManager *TheCampaignManager = NULL;
const FieldParse CampaignManager::m_campaignFieldParseTable[] =
{
{ "Mission", CampaignManager::parseMissionPart, NULL, NULL },
{ "FirstMission", INI::parseAsciiString, NULL, offsetof( Campaign, m_firstMission ) },
{ "CampaignNameLabel", INI::parseAsciiString, NULL, offsetof( Campaign, m_campaignNameLabel ) },
{ "FinalVictoryMovie", INI::parseAsciiString, NULL, offsetof( Campaign, m_finalMovieName ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
//-----------------------------------------------------------------------------
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void INI::parseCampaignDefinition( INI *ini )
{
AsciiString name;
Campaign *campaign;
// read the name
const char* c = ini->getNextToken();
name.set( c );
// find existing item if present
DEBUG_ASSERTCRASH( TheCampaignManager, ("parseCampaignDefinition: Unable to Get TheCampaignManager\n") );
if( !TheCampaignManager )
return;
// If we have a previously allocated Campaign
campaign = TheCampaignManager->newCampaign( name );
// sanity
DEBUG_ASSERTCRASH( campaign, ("parseCampaignDefinition: Unable to allocate campaign '%s'\n", name.str()) );
// parse the ini definition
ini->initFromINI( campaign, TheCampaignManager->getFieldParse() );
} // end parseCampaignDefinition
//-----------------------------------------------------------------------------
Campaign::Campaign( void )
{
m_missions.clear();
m_firstMission.clear();
m_name.clear();
m_finalMovieName.clear();
}
//-----------------------------------------------------------------------------
Campaign::~Campaign( void )
{
MissionListIt it = m_missions.begin();
while(it != m_missions.end())
{
Mission *mission = *it;
it = m_missions.erase( it );
if(mission)
mission->deleteInstance();
}
}
AsciiString Campaign::getFinalVictoryMovie( void )
{
return m_finalMovieName;
}
//-----------------------------------------------------------------------------
Mission *Campaign::newMission( AsciiString name )
{
MissionListIt it;
it = m_missions.begin();
name.toLower();
while(it != m_missions.end())
{
Mission *mission = *it;
if(mission->m_name.compare(name) == 0)
{
m_missions.erase( it );
mission->deleteInstance();
break;
}
else
++it;
}
Mission *newMission = newInstance(Mission);
newMission->m_name.set(name);
m_missions.push_back(newMission);
return newMission;
}
//-----------------------------------------------------------------------------
Mission *Campaign::getMission( AsciiString missionName )
{
if(missionName.isEmpty())
return NULL;
MissionListIt it;
it = m_missions.begin();
// we've reached the end of the campaign
while(it != m_missions.end())
{
Mission *mission = *it;
if(mission->m_name.compare(missionName) == 0)
return mission;
++it;
}
DEBUG_ASSERTCRASH(FALSE, ("getMission couldn't find %s", missionName.str()));
return NULL;
}
//-----------------------------------------------------------------------------
Mission *Campaign::getNextMission( Mission *current)
{
AsciiString name;
//if passed a Null pointer, load the first mission
if(!current)
{
name = m_firstMission;
}
else
name = current->m_nextMission;
name.toLower();
MissionListIt it;
it = m_missions.begin();
// we've reached the end of the campaign
if(name.isEmpty())
return NULL;
while(it != m_missions.end())
{
Mission *mission = *it;
if(mission->m_name.compare(name) == 0)
return mission;
++it;
}
// DEBUG_ASSERTCRASH(FALSE, ("GetNextMission couldn't find %s", current->m_nextMission.str()));
return NULL;
}
//-----------------------------------------------------------------------------
CampaignManager::CampaignManager( void )
{
m_campaignList.clear();
m_currentCampaign = NULL;
m_currentMission = NULL;
m_victorious = FALSE;
m_currentRankPoints = 0;
m_difficulty = DIFFICULTY_NORMAL;
}
//-----------------------------------------------------------------------------
CampaignManager::~CampaignManager( void )
{
m_currentCampaign = NULL;
m_currentMission = NULL;
CampaignListIt it = m_campaignList.begin();
while(it != m_campaignList.end())
{
Campaign *campaign = *it;
it = m_campaignList.erase( it );
if(campaign)
campaign->deleteInstance();
}
}
//-----------------------------------------------------------------------------
void CampaignManager::init( void )
{
INI ini;
// Read from INI all the CampaignManager
ini.load( AsciiString( "Data\\INI\\Campaign.ini" ), INI_LOAD_OVERWRITE, NULL );
}
//-----------------------------------------------------------------------------
Campaign *CampaignManager::getCurrentCampaign( void )
{
return m_currentCampaign;
}
//-----------------------------------------------------------------------------
Mission *CampaignManager::getCurrentMission( void )
{
return m_currentMission;
}
//-----------------------------------------------------------------------------
Mission *CampaignManager::gotoNextMission( void )
{
if (!m_currentCampaign || !m_currentMission)
return NULL;
m_currentMission = m_currentCampaign->getNextMission(m_currentMission);
return m_currentMission;
}
//-----------------------------------------------------------------------------
void CampaignManager::setCampaignAndMission( AsciiString campaign, AsciiString mission )
{
if(mission.isEmpty())
{
setCampaign(campaign);
return;
}
CampaignListIt it;
it = m_campaignList.begin();
campaign.toLower();
while ( it != m_campaignList.end())
{
Campaign *camp = *it;
if(camp->m_name.compare(campaign) == 0)
{
m_currentCampaign = camp;
m_currentMission = camp->getMission( mission );
return;
}
++it;
}
}
//-----------------------------------------------------------------------------
void CampaignManager::setCampaign( AsciiString campaign )
{
CampaignListIt it;
it = m_campaignList.begin();
campaign.toLower();
while ( it != m_campaignList.end())
{
Campaign *camp = *it;
if(camp->m_name.compare(campaign) == 0)
{
m_currentCampaign = camp;
m_currentMission = camp->getNextMission( NULL );
return;
}
++it;
}
// could not find the mission. we are resetting the missions to nothing.
m_currentCampaign = NULL;
m_currentMission = NULL;
m_currentRankPoints = 0;
m_difficulty = DIFFICULTY_NORMAL;
}
//-----------------------------------------------------------------------------
AsciiString CampaignManager::getCurrentMap( void )
{
if(!m_currentMission)
return AsciiString::TheEmptyString;
return m_currentMission->m_mapName;
}
// ------------------------------------------------------------------------------------------------
/** Return the 0 based mission number */
// ------------------------------------------------------------------------------------------------
Int CampaignManager::getCurrentMissionNumber( void )
{
Int number = INVALID_MISSION_NUMBER;
if( m_currentCampaign )
{
Campaign::MissionListIt it;
for( it = m_currentCampaign->m_missions.begin();
it != m_currentCampaign->m_missions.end();
++it )
{
number++;
if( *it == m_currentMission )
return number;
}
}
return number;
}
//-----------------------------------------------------------------------------
void CampaignManager::parseMissionPart( INI* ini, void *instance, void *store, const void *userData )
{
static const FieldParse myFieldParse[] =
{
{ "Map", INI::parseAsciiString, NULL, offsetof( Mission, m_mapName ) },
{ "NextMission", INI::parseAsciiString, NULL, offsetof( Mission, m_nextMission ) },
{ "IntroMovie", INI::parseAsciiString, NULL, offsetof( Mission, m_movieLabel ) },
{ "ObjectiveLine0", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[0] ) },
{ "ObjectiveLine1", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[1] ) },
{ "ObjectiveLine2", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[2] ) },
{ "ObjectiveLine3", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[3] ) },
{ "ObjectiveLine4", INI::parseAsciiString, NULL, offsetof( Mission, m_missionObjectivesLabel[4] ) },
{ "BriefingVoice", INI::parseAudioEventRTS, NULL, offsetof( Mission, m_briefingVoice ) },
{ "UnitNames0", INI::parseAsciiString, NULL, offsetof( Mission, m_unitNames[0] ) },
{ "UnitNames1", INI::parseAsciiString, NULL, offsetof( Mission, m_unitNames[1] ) },
{ "UnitNames2", INI::parseAsciiString, NULL, offsetof( Mission, m_unitNames[2] ) },
{ "LocationNameLabel",INI::parseAsciiString, NULL, offsetof( Mission, m_locationNameLabel ) },
{ "VoiceLength", INI::parseInt , NULL, offsetof( Mission, m_voiceLength ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
AsciiString name;
const char* c = ini->getNextToken();
name.set( c );
Mission *mission = ((Campaign*)instance)->newMission(name );
ini->initFromINI(mission, myFieldParse);
}
//-----------------------------------------------------------------------------
Campaign *CampaignManager::newCampaign(AsciiString name)
{
CampaignListIt it;
it = m_campaignList.begin();
name.toLower();
while(it != m_campaignList.end())
{
Campaign *campaign = *it;
if(campaign->m_name.compare(name) == 0)
{
m_campaignList.erase( it );
campaign->deleteInstance();
break;
}
else
++it;
}
Campaign *newCampaign = newInstance(Campaign);
newCampaign->m_name.set(name);
m_campaignList.push_back(newCampaign);
return newCampaign;
}
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info
* 1: Initial version
* 2: Added RankPoints Saving*/
// ------------------------------------------------------------------------------------------------
void CampaignManager::xfer( Xfer *xfer )
{
// version
const XferVersion currentVersion = 3;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// current campaign
AsciiString currentCampaign;
if( m_currentCampaign )
currentCampaign = m_currentCampaign->m_name;
xfer->xferAsciiString( &currentCampaign );
// current mission
AsciiString currentMission;
if( m_currentMission )
currentMission = m_currentMission->m_name;
xfer->xferAsciiString( &currentMission );
// version 2 and above has rank points!
if(version >= 2)
xfer->xferInt( &m_currentRankPoints );
if(version >= 3)
xfer->xferUser( &m_difficulty, sizeof(m_difficulty) );
// when loading, need to set the current campaign and mission
if( xfer->getXferMode() == XFER_LOAD )
setCampaignAndMission( currentCampaign, currentMission );
} // end xfer
//-----------------------------------------------------------------------------
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
Mission::Mission( void )
{
m_voiceLength = 0;
}
//-----------------------------------------------------------------------------
Mission::~Mission( void )
{
}

View 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. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Generals
//
// Module: Debug
//
// File name: AudiDebugDisplay.cpp
//
// Created: 11/13/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "GameClient/DebugDisplay.h"
#include "Common/GameAudio.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
static DebugDisplayInterface *debugDisplay;
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
static void printFunc( char *text )
{
debugDisplay->printf( text );
}
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// AudioDebugDisplay
//============================================================================
#if defined(_DEBUG) || defined(_INTERNAL)
void AudioDebugDisplay ( DebugDisplayInterface *dd, void *, FILE *fp = NULL )
{
TheAudio->audioDebugDisplay( dd, NULL, fp );
}
#endif

View file

@ -0,0 +1,253 @@
/*
** 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: Debug
//
// File name: DebugDisplay.cpp
//
// Created: 11/13/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "GameClient/DebugDisplay.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// DebugDisplay::DebugDisplay
//============================================================================
DebugDisplay::DebugDisplay()
: m_width(0),
m_height(0)
{
reset();
}
//============================================================================
// DebugDisplay::reset
//============================================================================
void DebugDisplay::reset( void )
{
setCursorPos( 0, 0 );
setTextColor( WHITE );
setRightMargin( 0 );
setLeftMargin( getWidth() );
}
//============================================================================
// DebugDisplay::setCursorPos
//============================================================================
void DebugDisplay::setCursorPos( Int x, Int y )
{
m_xPos = x;
m_yPos = y;
}
//============================================================================
// DebugDisplay::getCursorXPos
//============================================================================
Int DebugDisplay::getCursorXPos( void )
{
return m_xPos;
}
//============================================================================
// DebugDisplay::getCursorYPos
//============================================================================
Int DebugDisplay::getCursorYPos( void )
{
return m_yPos;
}
//============================================================================
// DebugDisplay::getWidth
//============================================================================
Int DebugDisplay::getWidth( void )
{
return m_width;
}
//============================================================================
// DebugDisplay::getHeight
//============================================================================
Int DebugDisplay::getHeight( void )
{
return m_height;
}
//============================================================================
// DebugDisplay::setTextColor
//============================================================================
void DebugDisplay::setTextColor( Color color )
{
m_textColor = color;
}
//============================================================================
// DebugDisplay::setRightMargin
//============================================================================
void DebugDisplay::setRightMargin( Int rightPos )
{
m_rightMargin = rightPos;
}
//============================================================================
// DebugDisplay::setLeftMargin
//============================================================================
void DebugDisplay::setLeftMargin( Int leftPos )
{
m_leftMargin = leftPos;
}
//============================================================================
// DebugDisplay::printf
//============================================================================
void DebugDisplay::printf( Char *format, ...)
{
va_list args;
int result;
static char text[5*1024];
va_start( args, format );
result = vsprintf( text, format, args );
va_end( args );
if ( result < 0 )
{
// error while printing string
return;
}
DEBUG_ASSERTCRASH( result < sizeof(text), ("text overflow in DebugDisplay::printf() - string too long"));
// find every line and print it
Char *ptr = text;;
Char *lineStart = ptr;
Int lineLen = 0;
Char ch;
while ( (ch = *ptr++) != 0 )
{
switch ( ch )
{
case '\n':
{
if ( lineLen > 0 )
{
*(ptr -1) = 0; // replace '/n' with null
drawText( m_rightMargin + m_xPos, m_yPos, lineStart );
lineLen = 0;
}
lineStart = ptr;
m_yPos++;
m_xPos = 0;
break;
}
default:
{
lineLen++;
break;
}
}
}
if ( lineLen > 0 )
{
drawText( m_rightMargin + m_xPos, m_yPos, lineStart );
m_xPos += lineLen;
}
}

View file

@ -0,0 +1,342 @@
/*
** 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: Image.cpp ////////////////////////////////////////////////////////////////////////////////
// Created: Colin Day, June 2001
// Desc: High level representation of images, this is currently being
// written so we have a way to refer to images in the windows
// GUI, this system should be replaced with something that can
// handle real image management or written to accomodate
// all parts of the engine that need images.
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_IMAGE_STATUS_NAMES
#include "Lib/BaseType.h"
#include "Common/Debug.h"
#include "Common/INI.h"
#include "Common/GlobalData.h"
#include "GameClient/Image.h"
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
const FieldParse Image::m_imageFieldParseTable[] =
{
{ "Texture", INI::parseAsciiString, NULL, offsetof( Image, m_filename ) },
{ "TextureWidth", INI::parseInt, NULL, offsetof( Image, m_textureSize.x ) },
{ "TextureHeight", INI::parseInt, NULL, offsetof( Image, m_textureSize.y ) },
{ "Coords", Image::parseImageCoords, NULL, offsetof( Image, m_UVCoords ) },
{ "Status", Image::parseImageStatus, NULL, offsetof( Image, m_status ) },
{ NULL, NULL, NULL, 0 }
};
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Parse an image coordinates in the form of
*
* COORDS = Left:AAA Top:BBB Right:CCC Bottom:DDD */
//-------------------------------------------------------------------------------------------------
void Image::parseImageCoords( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
Int left = INI::scanInt(ini->getNextSubToken("Left"));
Int top = INI::scanInt(ini->getNextSubToken("Top"));
Int right = INI::scanInt(ini->getNextSubToken("Right"));
Int bottom = INI::scanInt(ini->getNextSubToken("Bottom"));
// get the image we're storing in
Image *theImage = (Image *)instance;
//
// store the UV coords based on what we've read in and the texture size
// defined for this image
//
Region2D uvCoords;
uvCoords.lo.x = (Real)left;
uvCoords.lo.y = (Real)top;
uvCoords.hi.x = (Real)right;
uvCoords.hi.y = (Real)bottom;
// adjust the coords by texture size
const ICoord2D *textureSize = theImage->getTextureSize();
if( textureSize->x )
{
uvCoords.lo.x /= (Real)textureSize->x;
uvCoords.hi.x /= (Real)textureSize->x;
} // end if
if( textureSize->y )
{
uvCoords.lo.y /= (Real)textureSize->y;
uvCoords.hi.y /= (Real)textureSize->y;
} // end if
// store the uv coords
theImage->setUV( &uvCoords );
// compute the image size based on the coords we read and store
ICoord2D imageSize;
imageSize.x = right - left;
imageSize.y = bottom - top;
theImage->setImageSize( &imageSize );
} // end parseImageCoord
//-------------------------------------------------------------------------------------------------
/** Parse the image status line */
//-------------------------------------------------------------------------------------------------
void Image::parseImageStatus( INI* ini, void *instance, void *store, const void* /*userData*/)
{
// use existing INI parsing for the bit strings
INI::parseBitString32(ini, instance, store, imageStatusNames);
//
// if we are rotated 90 degrees clockwise we need to swap our width and height as
// they were computed from the page location rect, which was for the rotated image
// (see ImagePacker tool for more details)
//
UnsignedInt *theStatusBits = (UnsignedInt *)store;
if( BitTest( *theStatusBits, IMAGE_STATUS_ROTATED_90_CLOCKWISE ) )
{
Image *theImage = (Image *)instance;
ICoord2D imageSize;
imageSize.x = theImage->getImageHeight(); // note it's height not width
imageSize.y = theImage->getImageWidth(); // note it's width not height
theImage->setImageSize( &imageSize );
} // end if
} // end parseImageStatus
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
ImageCollection *TheMappedImageCollection = NULL; ///< mapped images
// PUBLIC FUNCTIONS////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Image::Image( void )
{
m_name.clear();
m_filename.clear();
m_textureSize.x = 0;
m_textureSize.y = 0;
m_UVCoords.lo.x = 0.0f;
m_UVCoords.lo.y = 0.0f;
m_UVCoords.hi.x = 1.0f;
m_UVCoords.hi.y = 1.0f;
m_imageSize.x = 0;
m_imageSize.y = 0;
m_rawTextureData = NULL;
m_status = IMAGE_STATUS_NONE;
m_next = NULL;
} // end Image
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Image::~Image( void )
{
} // end ~Image
//-------------------------------------------------------------------------------------------------
/** Set a status bit into the existing status, return the previous status
* bit collection from before the set */
//-------------------------------------------------------------------------------------------------
UnsignedInt Image::setStatus( UnsignedInt bit )
{
UnsignedInt prevStatus = m_status;
BitSet( m_status, bit );
return prevStatus;
} // end setStatus
//-------------------------------------------------------------------------------------------------
/** Clear a status bit from the existing status, return the previous
* status bit collection from before the clear */
//-------------------------------------------------------------------------------------------------
UnsignedInt Image::clearStatus( UnsignedInt bit )
{
UnsignedInt prevStatus = m_status;
BitClear( m_status, bit );
return prevStatus;
} // end clearStatus
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
ImageCollection::ImageCollection( void )
{
m_imageList = NULL;
} // end ImageCollection
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
ImageCollection::~ImageCollection( void )
{
Image *image, *next;
// delete the images
image = m_imageList;
while( image )
{
next = image->m_next;
image->deleteInstance();
image = next;
} // end while
m_imageList = NULL;
} // end ~ImageCollection
//-------------------------------------------------------------------------------------------------
/** Return the next image in the collection */
//-------------------------------------------------------------------------------------------------
Image *ImageCollection::nextImage( Image *image )
{
if( image )
return image->m_next;
return NULL;
} // end nextImage
//-------------------------------------------------------------------------------------------------
/** Allocate a new image, tie to the image list and return it */
//-------------------------------------------------------------------------------------------------
Image *ImageCollection::newImage( void )
{
Image *image = newInstance(Image);
// attach to collection list
image->m_next = m_imageList;
m_imageList = image;
return image;
} // end newImage
//-------------------------------------------------------------------------------------------------
/** Find an image given the image name */
//-------------------------------------------------------------------------------------------------
const Image *ImageCollection::findImageByName( const AsciiString& name )
{
Image *image;
/** @todo this needs to be more intelligent if this image collection
becomes a real system we use a lot */
// search the images
image = m_imageList;
while( image )
{
//
// want to do a case insensitive compare here cause image INI files are
// autogenerated from filenames using the image packer tool
//
if( image->getName().compareNoCase( name.str() ) == 0 )
return image;
image = image->m_next;
} // end while
// not found
return NULL;
} // end findImageByName
//-------------------------------------------------------------------------------------------------
/** Find image given image filename */
//-------------------------------------------------------------------------------------------------
const Image *ImageCollection::findImageByFilename( const AsciiString& filename )
{
Image *image;
/** @todo this needs to be more intelligent if this image collection
becomes a real system we use a lot */
// search the images
image = m_imageList;
while( image )
{
if( image->getFilename() == filename )
return image;
image = image->m_next;
} // end while
// not found
return NULL;
} // end findImageByFilename
//-------------------------------------------------------------------------------------------------
/** Load this image collection with all the images specified in the INI files
* for the proper texture size directory */
//-------------------------------------------------------------------------------------------------
void ImageCollection::load( Int textureSize )
{
char buffer[ _MAX_PATH ];
INI ini;
// first load in the user created mapped image files if we have them.
WIN32_FIND_DATA findData;
AsciiString userDataPath;
if(TheGlobalData)
{
userDataPath.format("%sINI\\MappedImages\\*.ini",TheGlobalData->getPath_UserData().str());
if(FindFirstFile(userDataPath.str(), &findData) !=INVALID_HANDLE_VALUE)
{
userDataPath.format("%sINI\\MappedImages",TheGlobalData->getPath_UserData().str());
ini.loadDirectory(userDataPath, TRUE, INI_LOAD_OVERWRITE, NULL );
}
}
// construct path to the mapped images folder of the correct texture size
sprintf( buffer, "Data\\INI\\MappedImages\\TextureSize_%d", textureSize );
// load all the ine files in that directory
ini.loadDirectory( AsciiString( buffer ), TRUE, INI_LOAD_OVERWRITE, NULL );
ini.loadDirectory("Data\\INI\\MappedImages\\HandCreated", TRUE, INI_LOAD_OVERWRITE, NULL );
} // end load

File diff suppressed because it is too large Load diff

View 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: RayEffect.cpp ////////////////////////////////////////////////////////////////////////////
// Created: Colin Day, May 2001
// Desc: Ray effect system manager
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "GameClient/RayEffect.h"
#include "GameClient/Drawable.h"
// PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
class RayEffectSystem *TheRayEffects = NULL;
// PRIVATE METHODS ////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Find an effect entry given a drawable */
//-------------------------------------------------------------------------------------------------
RayEffectData *RayEffectSystem::findEntry( const Drawable *draw )
{
Int i;
RayEffectData *effectData = NULL;
// find the matching effect data entry
for( i = 0; i < MAX_RAY_EFFECTS; i++ )
{
if( m_effectData[ i ].draw == draw )
{
effectData = &m_effectData[ i ];
break; // exit for i
} // end if
} // end for i
return effectData;
} // end findEntry
// PUBLIC METHODS /////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
RayEffectSystem::RayEffectSystem( void )
{
init();
} // end RayEffectSystem
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
RayEffectSystem::~RayEffectSystem( void )
{
} // end ~RayEffectSystem
//-------------------------------------------------------------------------------------------------
/** initialize the system */
//-------------------------------------------------------------------------------------------------
void RayEffectSystem::init( void )
{
Int i;
for( i = 0; i < MAX_RAY_EFFECTS; i++ )
{
m_effectData[ i ].draw = NULL;
m_effectData[ i ].startLoc.zero();
m_effectData[ i ].endLoc.zero();
} // end for i
} // end init
//-------------------------------------------------------------------------------------------------
/** Reset */
//-------------------------------------------------------------------------------------------------
void RayEffectSystem::reset( void )
{
// nothing dynamic going on here, just initialize it
init();
} // end reset
//-------------------------------------------------------------------------------------------------
/** add a ray effect entry for this drawable */
//-------------------------------------------------------------------------------------------------
void RayEffectSystem::addRayEffect( const Drawable *draw,
const Coord3D *startLoc,
const Coord3D *endLoc )
{
Int i;
RayEffectData *effectData = NULL;
// sanity
if( draw == NULL || startLoc == NULL || endLoc == NULL )
return;
/** @todo this should be more intelligent and should not be limited
to any kind of max ray effects, this is all a temporary hack system for
the demo anyway right now though */
// search for a free effect slot
for( i = 0; i < MAX_RAY_EFFECTS; i++ )
{
if( m_effectData[ i ].draw == NULL )
{
effectData = &m_effectData[ i ];
break; // exit for
} // end if
} // end for i
// if no free slots we can't do it
if( effectData == NULL )
return;
// add the data to the entry
effectData->draw = draw;
effectData->startLoc = *startLoc;
effectData->endLoc = *endLoc;
}
//-------------------------------------------------------------------------------------------------
/** given a drawable, remove its effect from the system */
//-------------------------------------------------------------------------------------------------
void RayEffectSystem::deleteRayEffect( const Drawable *draw )
{
RayEffectData *effectData = NULL;
// sanity
if( draw == NULL )
return;
// find the effect entry
effectData = findEntry( draw );
if( effectData )
{
// remove the data for this entry
effectData->draw = NULL;
} // end if
} // end deleteRayEffect
//-------------------------------------------------------------------------------------------------
/** given a drawable, if it is in the ray effect system list retrieve
* the ray effect data for its entry */
//-------------------------------------------------------------------------------------------------
void RayEffectSystem::getRayEffectData( const Drawable *draw,
RayEffectData *effectData )
{
RayEffectData *entry = NULL;
// sanity
if( draw == NULL || effectData == NULL )
return;
// find the effect data entry
entry = findEntry( draw );
if( entry )
{
// data has been found, copy to parameter
*effectData = *entry;
} // end effectData
} // end getRayEffectData