Files
itgmania212121/src/BGAnimationLayer.cpp
T
Jason Felds dbdab4759e [Xcode4] May as well fix switch warnings.
Still need some assistance for the linker error.
2011-07-20 11:11:04 -04:00

672 lines
20 KiB
C++

#include "global.h"
#include "BGAnimationLayer.h"
#include "GameState.h"
#include "XmlFile.h"
#include "RageMath.h"
#include "RageLog.h"
#include "Song.h"
#include "ScreenDimensions.h"
#include "Sprite.h"
#include "RageDisplay.h"
#include "ActorUtil.h"
#include "arch/ArchHooks/ArchHooks.h"
#include "LuaManager.h"
#include "AutoActor.h"
#include "ThemeManager.h"
const float PARTICLE_SPEED = 300;
const float SPIRAL_MAX_ZOOM = 2;
const float SPIRAL_MIN_ZOOM = 0.3f;
#define MAX_TILES_WIDE int(SCREEN_WIDTH/32+2)
#define MAX_TILES_HIGH int(SCREEN_HEIGHT/32+2)
#define MAX_SPRITES (MAX_TILES_WIDE*MAX_TILES_HIGH)
inline float GetOffScreenLeft( Actor* pActor ) { return SCREEN_LEFT - pActor->GetZoomedWidth()/2; }
inline float GetOffScreenRight( Actor* pActor ) { return SCREEN_RIGHT + pActor->GetZoomedWidth()/2; }
inline float GetOffScreenTop( Actor* pActor ) { return SCREEN_TOP - pActor->GetZoomedHeight()/2; }
inline float GetOffScreenBottom(Actor* pActor ) { return SCREEN_BOTTOM+ pActor->GetZoomedHeight()/2; }
inline bool IsOffScreenLeft( Actor* pActor ) { return pActor->GetX() < GetOffScreenLeft(pActor); }
inline bool IsOffScreenRight( Actor* pActor ) { return pActor->GetX() > GetOffScreenRight(pActor); }
inline bool IsOffScreenTop( Actor* pActor ) { return pActor->GetY() < GetOffScreenTop(pActor); }
inline bool IsOffScreenBottom(Actor* pActor ) { return pActor->GetY() > GetOffScreenBottom(pActor); }
inline bool IsOffScreen( Actor* pActor ) { return IsOffScreenLeft(pActor) || IsOffScreenRight(pActor) || IsOffScreenTop(pActor) || IsOffScreenBottom(pActor); }
// guard rail is the area that keeps particles from going off screen
inline float GetGuardRailLeft( Actor* pActor ) { return SCREEN_LEFT + pActor->GetZoomedWidth()/2; }
inline float GetGuardRailRight( Actor* pActor ) { return SCREEN_RIGHT - pActor->GetZoomedWidth()/2; }
inline float GetGuardRailTop( Actor* pActor ) { return SCREEN_TOP + pActor->GetZoomedHeight()/2; }
inline float GetGuardRailBottom(Actor* pActor ) { return SCREEN_BOTTOM- pActor->GetZoomedHeight()/2; }
inline bool HitGuardRailLeft( Actor* pActor ) { return pActor->GetX() < GetGuardRailLeft(pActor); }
inline bool HitGuardRailRight( Actor* pActor ) { return pActor->GetX() > GetGuardRailRight(pActor); }
inline bool HitGuardRailTop( Actor* pActor ) { return pActor->GetY() < GetGuardRailTop(pActor); }
inline bool HitGuardRailBottom(Actor* pActor ) { return pActor->GetY() > GetGuardRailBottom(pActor); }
BGAnimationLayer::BGAnimationLayer()
{
m_vParticleVelocity.clear();
m_Type = TYPE_SPRITE;
m_fTexCoordVelocityX = 0;
m_fTexCoordVelocityY = 0;
m_bParticlesBounce = false;
m_iNumTilesWide = -1;
m_iNumTilesHigh = -1;
m_fTilesStartX = 0;
m_fTilesStartY = 0;
m_fTilesSpacingX = -1;
m_fTilesSpacingY = -1;
m_fTileVelocityX = 0;
m_fTileVelocityY = 0;
}
BGAnimationLayer::~BGAnimationLayer()
{
ActorFrame::DeleteAllChildren();
}
void BGAnimationLayer::LoadFromAniLayerFile( const RString& sPath )
{
/* Generic BGAs are new. Animation directories with no INI are old and obsolete.
* Don't combine them. */
RString lcPath = sPath;
lcPath.MakeLower();
if( lcPath.find("usesongbg") != RString::npos )
{
const Song* pSong = GAMESTATE->m_pCurSong;
RString sSongBGPath;
if( pSong && pSong->HasBackground() )
sSongBGPath = pSong->GetBackgroundPath();
else
sSongBGPath = THEME->GetPathG("Common","fallback background");
Sprite* pSprite = new Sprite;
pSprite->Load( Sprite::SongBGTexture(sSongBGPath) );
pSprite->StretchTo( FullScreenRectF );
this->AddChild( pSprite );
return; // this will ignore other effects in the file name
}
enum Effect {
EFFECT_CENTER,
EFFECT_STRETCH_STILL,
EFFECT_STRETCH_SCROLL_LEFT,
EFFECT_STRETCH_SCROLL_RIGHT,
EFFECT_STRETCH_SCROLL_UP,
EFFECT_STRETCH_SCROLL_DOWN,
EFFECT_STRETCH_WATER,
EFFECT_STRETCH_BUBBLE,
EFFECT_STRETCH_TWIST,
EFFECT_STRETCH_SPIN,
EFFECT_PARTICLES_SPIRAL_OUT,
EFFECT_PARTICLES_SPIRAL_IN,
EFFECT_PARTICLES_FLOAT_UP,
EFFECT_PARTICLES_FLOAT_DOWN,
EFFECT_PARTICLES_FLOAT_LEFT,
EFFECT_PARTICLES_FLOAT_RIGHT,
EFFECT_PARTICLES_BOUNCE,
EFFECT_TILE_STILL,
EFFECT_TILE_SCROLL_LEFT,
EFFECT_TILE_SCROLL_RIGHT,
EFFECT_TILE_SCROLL_UP,
EFFECT_TILE_SCROLL_DOWN,
EFFECT_TILE_FLIP_X,
EFFECT_TILE_FLIP_Y,
EFFECT_TILE_PULSE,
NUM_EFFECTS, // leave this at the end
EFFECT_INVALID
};
const RString EFFECT_STRING[NUM_EFFECTS] = {
"center",
"stretchstill",
"stretchscrollleft",
"stretchscrollright",
"stretchscrollup",
"stretchscrolldown",
"stretchwater",
"stretchbubble",
"stretchtwist",
"stretchspin",
"particlesspiralout",
"particlesspiralin",
"particlesfloatup",
"particlesfloatdown",
"particlesfloatleft",
"particlesfloatright",
"particlesbounce",
"tilestill",
"tilescrollleft",
"tilescrollright",
"tilescrollup",
"tilescrolldown",
"tileflipx",
"tileflipy",
"tilepulse",
};
Effect effect = EFFECT_CENTER;
for( int i=0; i<NUM_EFFECTS; i++ )
if( lcPath.find(EFFECT_STRING[i]) != string::npos )
effect = (Effect)i;
switch( effect )
{
case EFFECT_CENTER:
{
m_Type = TYPE_SPRITE;
Sprite* pSprite = new Sprite;
this->AddChild( pSprite );
pSprite->Load( sPath );
pSprite->SetXY( SCREEN_CENTER_X, SCREEN_CENTER_Y );
}
break;
case EFFECT_STRETCH_STILL:
case EFFECT_STRETCH_SCROLL_LEFT:
case EFFECT_STRETCH_SCROLL_RIGHT:
case EFFECT_STRETCH_SCROLL_UP:
case EFFECT_STRETCH_SCROLL_DOWN:
case EFFECT_STRETCH_WATER:
case EFFECT_STRETCH_BUBBLE:
case EFFECT_STRETCH_TWIST:
{
m_Type = TYPE_SPRITE;
Sprite* pSprite = new Sprite;
this->AddChild( pSprite );
RageTextureID ID(sPath);
ID.bStretch = true;
pSprite->Load( Sprite::SongBGTexture(ID) );
pSprite->StretchTo( FullScreenRectF );
pSprite->SetCustomTextureRect( RectF(0,0,1,1) );
switch( effect )
{
case EFFECT_STRETCH_SCROLL_LEFT: m_fTexCoordVelocityX = +0.5f; m_fTexCoordVelocityY = 0; break;
case EFFECT_STRETCH_SCROLL_RIGHT: m_fTexCoordVelocityX = -0.5f; m_fTexCoordVelocityY = 0; break;
case EFFECT_STRETCH_SCROLL_UP: m_fTexCoordVelocityX = 0; m_fTexCoordVelocityY = +0.5f; break;
case EFFECT_STRETCH_SCROLL_DOWN: m_fTexCoordVelocityX = 0; m_fTexCoordVelocityY = -0.5f; break;
default: break;
}
}
break;
case EFFECT_STRETCH_SPIN:
{
m_Type = TYPE_SPRITE;
Sprite* pSprite = new Sprite;
this->AddChild( pSprite );
pSprite->Load( Sprite::SongBGTexture(sPath) );
const RectF StretchedFullScreenRectF(
FullScreenRectF.left-200,
FullScreenRectF.top-200,
FullScreenRectF.right+200,
FullScreenRectF.bottom+200 );
pSprite->ScaleToCover( StretchedFullScreenRectF );
pSprite->SetEffectSpin( RageVector3(0,0,60) );
}
break;
case EFFECT_PARTICLES_SPIRAL_OUT:
case EFFECT_PARTICLES_SPIRAL_IN:
case EFFECT_PARTICLES_FLOAT_UP:
case EFFECT_PARTICLES_FLOAT_DOWN:
case EFFECT_PARTICLES_FLOAT_LEFT:
case EFFECT_PARTICLES_FLOAT_RIGHT:
case EFFECT_PARTICLES_BOUNCE:
{
m_Type = TYPE_PARTICLES;
Sprite s;
s.Load( sPath );
int iSpriteArea = int( s.GetUnzoomedWidth()*s.GetUnzoomedHeight() );
const int iMaxArea = int(SCREEN_WIDTH*SCREEN_HEIGHT);
int iNumParticles = iMaxArea / iSpriteArea;
iNumParticles = min( iNumParticles, MAX_SPRITES );
for( int i=0; i<iNumParticles; i++ )
{
Sprite* pSprite = new Sprite;
this->AddChild( pSprite );
pSprite->Load( sPath );
pSprite->SetZoom( 0.7f + 0.6f*i/(float)iNumParticles );
switch( effect )
{
case EFFECT_PARTICLES_BOUNCE:
pSprite->SetX( randomf( GetGuardRailLeft(pSprite), GetGuardRailRight(pSprite) ) );
pSprite->SetY( randomf( GetGuardRailTop(pSprite), GetGuardRailBottom(pSprite) ) );
break;
default:
pSprite->SetX( randomf( GetOffScreenLeft(pSprite), GetOffScreenRight(pSprite) ) );
pSprite->SetY( randomf( GetOffScreenTop(pSprite), GetOffScreenBottom(pSprite) ) );
break;
}
switch( effect )
{
case EFFECT_PARTICLES_FLOAT_UP:
case EFFECT_PARTICLES_SPIRAL_OUT:
m_vParticleVelocity.push_back( RageVector3( 0, -PARTICLE_SPEED*pSprite->GetZoom(), 0 ) );
break;
case EFFECT_PARTICLES_FLOAT_DOWN:
case EFFECT_PARTICLES_SPIRAL_IN:
m_vParticleVelocity.push_back( RageVector3( 0, PARTICLE_SPEED*pSprite->GetZoom(), 0 ) );
break;
case EFFECT_PARTICLES_FLOAT_LEFT:
m_vParticleVelocity.push_back( RageVector3( -PARTICLE_SPEED*pSprite->GetZoom(), 0, 0 ) );
break;
case EFFECT_PARTICLES_FLOAT_RIGHT:
m_vParticleVelocity.push_back( RageVector3( +PARTICLE_SPEED*pSprite->GetZoom(), 0, 0 ) );
break;
case EFFECT_PARTICLES_BOUNCE:
m_bParticlesBounce = true;
pSprite->SetZoom( 1 );
m_vParticleVelocity.push_back( RageVector3( randomf(), randomf(), 0 ) );
RageVec3Normalize( &m_vParticleVelocity[i], &m_vParticleVelocity[i] );
m_vParticleVelocity[i].x *= PARTICLE_SPEED;
m_vParticleVelocity[i].y *= PARTICLE_SPEED;
break;
default:
ASSERT(0);
}
}
}
break;
case EFFECT_TILE_STILL:
case EFFECT_TILE_SCROLL_LEFT:
case EFFECT_TILE_SCROLL_RIGHT:
case EFFECT_TILE_SCROLL_UP:
case EFFECT_TILE_SCROLL_DOWN:
case EFFECT_TILE_FLIP_X:
case EFFECT_TILE_FLIP_Y:
case EFFECT_TILE_PULSE:
{
m_Type = TYPE_TILES;
RageTextureID ID(sPath);
ID.bStretch = true;
Sprite s;
s.Load( ID );
m_iNumTilesWide = 2+int(SCREEN_WIDTH /s.GetUnzoomedWidth());
m_iNumTilesWide = min( m_iNumTilesWide, MAX_TILES_WIDE );
m_iNumTilesHigh = 2+int(SCREEN_HEIGHT/s.GetUnzoomedHeight());
m_iNumTilesHigh = min( m_iNumTilesHigh, MAX_TILES_HIGH );
m_fTilesStartX = s.GetUnzoomedWidth() / 2;
m_fTilesStartY = s.GetUnzoomedHeight() / 2;
m_fTilesSpacingX = s.GetUnzoomedWidth();
m_fTilesSpacingY = s.GetUnzoomedHeight();
for( int x=0; x<m_iNumTilesWide; x++ )
{
for( int y=0; y<m_iNumTilesHigh; y++ )
{
Sprite* pSprite = new Sprite;
this->AddChild( pSprite );
pSprite->Load( ID );
pSprite->SetTextureWrapping( true ); // gets rid of some "cracks"
switch( effect )
{
case EFFECT_TILE_STILL:
break;
case EFFECT_TILE_SCROLL_LEFT:
m_fTileVelocityX = -PARTICLE_SPEED;
break;
case EFFECT_TILE_SCROLL_RIGHT:
m_fTileVelocityX = +PARTICLE_SPEED;
break;
case EFFECT_TILE_SCROLL_UP:
m_fTileVelocityY = -PARTICLE_SPEED;
break;
case EFFECT_TILE_SCROLL_DOWN:
m_fTileVelocityY = +PARTICLE_SPEED;
break;
case EFFECT_TILE_FLIP_X:
pSprite->SetEffectSpin( RageVector3(180,0,0) );
break;
case EFFECT_TILE_FLIP_Y:
pSprite->SetEffectSpin( RageVector3(0,180,0) );
break;
case EFFECT_TILE_PULSE:
pSprite->SetEffectPulse( 1, 0.3f, 1.f );
break;
default:
ASSERT(0);
}
}
}
}
break;
default:
ASSERT(0);
}
RString sHint = sPath;
sHint.MakeLower();
if( sHint.find("cyclecolor") != RString::npos )
for( unsigned i=0; i<m_SubActors.size(); i++ )
m_SubActors[i]->SetEffectRainbow( 5 );
if( sHint.find("cyclealpha") != RString::npos )
for( unsigned i=0; i<m_SubActors.size(); i++ )
m_SubActors[i]->SetEffectDiffuseShift( 2, RageColor(1,1,1,1), RageColor(1,1,1,0) );
if( sHint.find("startonrandomframe") != RString::npos )
for( unsigned i=0; i<m_SubActors.size(); i++ )
m_SubActors[i]->SetState( RandomInt(m_SubActors[i]->GetNumStates()) );
if( sHint.find("dontanimate") != RString::npos )
for( unsigned i=0; i<m_SubActors.size(); i++ )
m_SubActors[i]->StopAnimating();
if( sHint.find("add") != RString::npos )
for( unsigned i=0; i<m_SubActors.size(); i++ )
m_SubActors[i]->SetBlendMode( BLEND_ADD );
}
void BGAnimationLayer::LoadFromNode( const XNode* pNode )
{
{
bool bCond;
if( pNode->GetAttrValue("Condition", bCond) && !bCond )
return;
}
bool bStretch = false;
{
RString type = "sprite";
pNode->GetAttrValue( "Type", type );
type.MakeLower();
/* The preferred way of stretching a sprite to fit the screen is "Type=sprite"
* and "stretch=1". "type=1" is for backwards-compatibility. */
pNode->GetAttrValue( "Stretch", bStretch );
// Check for string match first, then do integer match.
// "if(StringType(type)==0)" was matching against all string matches.
// -Chris
if( type.EqualsNoCase("sprite") )
{
m_Type = TYPE_SPRITE;
}
else if( type.EqualsNoCase("particles") )
{
m_Type = TYPE_PARTICLES;
}
else if( type.EqualsNoCase("tiles") )
{
m_Type = TYPE_TILES;
}
else if( StringToInt(type) == 1 )
{
m_Type = TYPE_SPRITE;
bStretch = true;
}
else if( StringToInt(type) == 2 )
{
m_Type = TYPE_PARTICLES;
}
else if( StringToInt(type) == 3 )
{
m_Type = TYPE_TILES;
}
else
{
m_Type = TYPE_SPRITE;
}
}
pNode->GetAttrValue( "FOV", m_fFOV );
pNode->GetAttrValue( "Lighting", m_bLighting );
pNode->GetAttrValue( "TexCoordVelocityX", m_fTexCoordVelocityX );
pNode->GetAttrValue( "TexCoordVelocityY", m_fTexCoordVelocityY );
// compat:
pNode->GetAttrValue( "StretchTexCoordVelocityX", m_fTexCoordVelocityX );
pNode->GetAttrValue( "StretchTexCoordVelocityY", m_fTexCoordVelocityY );
// particle and tile stuff
float fZoomMin = 1;
float fZoomMax = 1;
pNode->GetAttrValue( "ZoomMin", fZoomMin );
pNode->GetAttrValue( "ZoomMax", fZoomMax );
float fVelocityXMin = 10, fVelocityXMax = 10;
float fVelocityYMin = 0, fVelocityYMax = 0;
float fVelocityZMin = 0, fVelocityZMax = 0;
float fOverrideSpeed = 0; // 0 means don't override speed
pNode->GetAttrValue( "VelocityXMin", fVelocityXMin );
pNode->GetAttrValue( "VelocityXMax", fVelocityXMax );
pNode->GetAttrValue( "VelocityYMin", fVelocityYMin );
pNode->GetAttrValue( "VelocityYMax", fVelocityYMax );
pNode->GetAttrValue( "VelocityZMin", fVelocityZMin );
pNode->GetAttrValue( "VelocityZMax", fVelocityZMax );
pNode->GetAttrValue( "OverrideSpeed", fOverrideSpeed );
int iNumParticles = 10;
pNode->GetAttrValue( "NumParticles", iNumParticles );
pNode->GetAttrValue( "ParticlesBounce", m_bParticlesBounce );
pNode->GetAttrValue( "TilesStartX", m_fTilesStartX );
pNode->GetAttrValue( "TilesStartY", m_fTilesStartY );
pNode->GetAttrValue( "TilesSpacingX", m_fTilesSpacingX );
pNode->GetAttrValue( "TilesSpacingY", m_fTilesSpacingY );
pNode->GetAttrValue( "TileVelocityX", m_fTileVelocityX );
pNode->GetAttrValue( "TileVelocityY", m_fTileVelocityY );
switch( m_Type )
{
case TYPE_SPRITE:
{
Actor* pActor = ActorUtil::LoadFromNode( pNode, this );
this->AddChild( pActor );
if( bStretch )
pActor->StretchTo( FullScreenRectF );
}
break;
case TYPE_PARTICLES:
{
RString sFile;
ActorUtil::GetAttrPath( pNode, "File", sFile );
FixSlashesInPlace( sFile );
CollapsePath( sFile );
for( int i=0; i<iNumParticles; i++ )
{
Actor* pActor = ActorUtil::MakeActor( sFile, this );
if( pActor == NULL )
continue;
this->AddChild( pActor );
pActor->SetXY( randomf(float(FullScreenRectF.left),float(FullScreenRectF.right)),
randomf(float(FullScreenRectF.top),float(FullScreenRectF.bottom)) );
pActor->SetZoom( randomf(fZoomMin,fZoomMax) );
m_vParticleVelocity.push_back( RageVector3(
randomf(fVelocityXMin,fVelocityXMax),
randomf(fVelocityYMin,fVelocityYMax),
randomf(fVelocityZMin,fVelocityZMax) ) );
if( fOverrideSpeed != 0 )
{
RageVec3Normalize( &m_vParticleVelocity[i], &m_vParticleVelocity[i] );
m_vParticleVelocity[i] *= fOverrideSpeed;
}
}
}
break;
case TYPE_TILES:
{
RString sFile;
ActorUtil::GetAttrPath( pNode, "File", sFile );
FixSlashesInPlace( sFile );
CollapsePath( sFile );
AutoActor s;
s.Load( sFile );
if( m_fTilesSpacingX == -1 )
m_fTilesSpacingX = s->GetUnzoomedWidth();
if( m_fTilesSpacingY == -1 )
m_fTilesSpacingY = s->GetUnzoomedHeight();
m_iNumTilesWide = 2+(int)(SCREEN_WIDTH /m_fTilesSpacingX);
m_iNumTilesHigh = 2+(int)(SCREEN_HEIGHT/m_fTilesSpacingY);
unsigned NumSprites = m_iNumTilesWide * m_iNumTilesHigh;
for( unsigned i=0; i<NumSprites; i++ )
{
Actor* pSprite = ActorUtil::MakeActor( sFile, this );
this->AddChild( pSprite );
pSprite->SetTextureWrapping( true ); // gets rid of some "cracks"
pSprite->SetZoom( randomf(fZoomMin,fZoomMax) );
}
}
break;
default:
ASSERT(0);
}
bool bStartOnRandomFrame = false;
pNode->GetAttrValue( "StartOnRandomFrame", bStartOnRandomFrame );
if( bStartOnRandomFrame )
{
for( unsigned i=0; i<m_SubActors.size(); i++ )
m_SubActors[i]->SetState( RandomInt(m_SubActors[i]->GetNumStates()) );
}
}
void BGAnimationLayer::UpdateInternal( float fDeltaTime )
{
ActorFrame::UpdateInternal( fDeltaTime );
fDeltaTime *= m_fUpdateRate;
switch( m_Type )
{
case TYPE_SPRITE:
if( m_fTexCoordVelocityX || m_fTexCoordVelocityY )
{
for( unsigned i=0; i<m_SubActors.size(); i++ )
{
// XXX: there's no longer any guarantee that this is a Sprite
Sprite *pSprite = (Sprite*)m_SubActors[i];
pSprite->StretchTexCoords(
fDeltaTime*m_fTexCoordVelocityX,
fDeltaTime*m_fTexCoordVelocityY );
}
}
break;
case TYPE_PARTICLES:
for( unsigned i=0; i<m_SubActors.size(); i++ )
{
Actor* pActor = m_SubActors[i];
RageVector3 &vel = m_vParticleVelocity[i];
m_SubActors[i]->SetX( pActor->GetX() + fDeltaTime*vel.x );
m_SubActors[i]->SetY( pActor->GetY() + fDeltaTime*vel.y );
pActor->SetZ( pActor->GetZ() + fDeltaTime*vel.z );
if( m_bParticlesBounce )
{
if( HitGuardRailLeft(pActor) )
{
vel.x *= -1;
pActor->SetX( GetGuardRailLeft(pActor) );
}
if( HitGuardRailRight(pActor) )
{
vel.x *= -1;
pActor->SetX( GetGuardRailRight(pActor) );
}
if( HitGuardRailTop(pActor) )
{
vel.y *= -1;
pActor->SetY( GetGuardRailTop(pActor) );
}
if( HitGuardRailBottom(pActor) )
{
vel.y *= -1;
pActor->SetY( GetGuardRailBottom(pActor) );
}
}
else // !m_bParticlesBounce
{
if( vel.x<0 && IsOffScreenLeft(pActor) )
pActor->SetX( GetOffScreenRight(pActor) );
if( vel.x>0 && IsOffScreenRight(pActor) )
pActor->SetX( GetOffScreenLeft(pActor) );
if( vel.y<0 && IsOffScreenTop(pActor) )
pActor->SetY( GetOffScreenBottom(pActor) );
if( vel.y>0 && IsOffScreenBottom(pActor) )
pActor->SetY( GetOffScreenTop(pActor) );
}
}
break;
case TYPE_TILES:
{
float fSecs = RageTimer::GetTimeSinceStartFast();
float fTotalWidth = m_iNumTilesWide * m_fTilesSpacingX;
float fTotalHeight = m_iNumTilesHigh * m_fTilesSpacingY;
ASSERT( int(m_SubActors.size()) == m_iNumTilesWide * m_iNumTilesHigh );
for( int x=0; x<m_iNumTilesWide; x++ )
{
for( int y=0; y<m_iNumTilesHigh; y++ )
{
int i = y*m_iNumTilesWide + x;
float fX = m_fTilesStartX + m_fTilesSpacingX * x + fSecs * m_fTileVelocityX;
float fY = m_fTilesStartY + m_fTilesSpacingY * y + fSecs * m_fTileVelocityY;
fX += m_fTilesSpacingX/2;
fY += m_fTilesSpacingY/2;
fX = fmodf( fX, fTotalWidth );
fY = fmodf( fY, fTotalHeight );
if( fX < 0 ) fX += fTotalWidth;
if( fY < 0 ) fY += fTotalHeight;
fX -= m_fTilesSpacingX/2;
fY -= m_fTilesSpacingY/2;
m_SubActors[i]->SetX( fX );
m_SubActors[i]->SetY( fY );
}
}
}
break;
default:
ASSERT(0);
}
}
/*
* (c) 2001-2004 Ben Nordstrom, Chris Danford, Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/