Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
76 KB
Referenced Files
None
Subscribers
None
diff --git a/data/themes/Cloudscape/theme.mnmstheme b/data/themes/Cloudscape/theme.mnmstheme
index 81cd0d8..dcaa080 100644
--- a/data/themes/Cloudscape/theme.mnmstheme
+++ b/data/themes/Cloudscape/theme.mnmstheme
@@ -1,462 +1,484 @@
name="Cloudscape"
background(background.png){
repeat=0,0
}
block(Block){
editorPicture(tiles.png,0,0,50,50)
blockState(default){
object{
picture(tiles.png,0,0,50,50)
optionalPicture(tiles.png,50,0,50,50,0.15)
optionalPicture(tiles.png,100,0,50,50,0.15)
}
}
}
block(ShadowBlock){
editorPicture(tiles.png,0,50,50,50)
blockState(default){
object{
picture(tiles.png,0,50,50,50)
}
}
}
character(Player){
characterState(standleft){
object{
picture(player.png,115,0,23,40)
}
}
characterState(standright){
object{
picture(player.png,0,0,23,40)
}
}
characterState(walkleft){
object{
animation=20,0
pictureAnimation(player.png){
point(138,0,23,40,1,5)
point(230,0,23,40,4,5)
}
}
}
characterState(walkright){
object{
animation=20,0
pictureAnimation(player.png){
point(23,0,23,40,1,5)
point(115,0,23,40,4,5)
}
}
}
characterState(jumpleft){
object{
picture(player.png,276,0,23,40)
}
}
characterState(fallleft){
object{
picture(player.png,299,0,23,40)
}
}
characterState(jumpright){
object{
picture(player.png,230,0,23,40)
}
}
characterState(fallright){
object{
picture(player.png,253,0,23,40)
}
}
characterState(holding){
object{
picture(player.png,322,0,23,40)
}
}
characterState(line){
object{
picture(line.png,0,0,5,5)
}
}
characterState(dieright){
oneTimeAnimation=8,dead
object{
offset(0,-14)
animation=8,0
pictureAnimation(deathright.png){
point(0,0,23,54,1,2)
point(92,0,23,54,4,2)
}
}
}
characterState(dieleft){
oneTimeAnimation=8,dead
object{
offset(0,-14)
animation=8,0
pictureAnimation(deathleft.png){
point(0,0,23,54,1,2)
point(92,0,23,54,4,2)
}
}
}
characterState(dead){
object{
picture(player.png,0,0,23,40)
invisibleAtRunTime=1
}
}
}
character(Shadow){
characterState(standleft){
object{
picture(shadow.png,115,0,23,40)
}
}
characterState(standright){
object{
picture(shadow.png,0,0,23,40)
}
}
characterState(walkleft){
object{
animation=20,0
pictureAnimation(shadow.png){
point(138,0,23,40,1,5)
point(230,0,23,40,4,5)
}
}
}
characterState(walkright){
object{
animation=20,0
pictureAnimation(shadow.png){
point(23,0,23,40,1,5)
point(115,0,23,40,4,5)
}
}
}
characterState(jumpleft){
object{
picture(shadow.png,276,0,23,40)
}
}
characterState(fallleft){
object{
picture(shadow.png,299,0,23,40)
}
}
characterState(jumpright){
object{
picture(shadow.png,230,0,23,40)
}
}
characterState(fallright){
object{
picture(shadow.png,253,0,23,40)
}
}
characterState(holding){
object{
picture(shadow.png,322,0,23,40)
}
}
characterState(line){
object{
picture(line.png,0,0,5,5)
}
}
characterState(dieright){
oneTimeAnimation=8,dead
object{
offset(0,-14)
animation=8,0
pictureAnimation(shadowdeathright.png){
point(0,0,23,54,1,2)
point(92,0,23,54,4,2)
}
}
}
characterState(dieleft){
oneTimeAnimation=8,dead
object{
offset(0,-14)
animation=8,0
pictureAnimation(shadowdeathleft.png){
point(0,0,23,54,1,2)
point(92,0,23,54,4,2)
}
}
}
characterState(dead){
object{
picture(player.png,0,0,23,40)
invisibleAtRunTime=1
}
}
}
block(Fragile){
editorPicture(tiles.png,150,0,50,50)
blockState(default){
object{
picture(tiles.png,150,0,50,50)
}
}
- blockState(fragile1){
+ transitionState(default,fragile1){
+ oneTimeAnimation=20,fragile1
object{
oneTimeAnimation=20,6
pictureAnimation(tiles.png){
point(200,0,50,50,1,5)
point(250,0,50,50,1,5)
}
}
}
- blockState(fragile2){
+ blockState(fragile1){
+ object{
+ picture(tiles.png,250,0,50,50)
+ }
+ }
+ transitionState(fragile1,fragile2){
+ oneTimeAnimation=20,fragile2
object{
oneTimeAnimation=20,6
pictureAnimation(tiles.png){
point(300,0,50,50,1,5)
point(350,0,50,50,1,5)
}
}
}
+ blockState(fragile2){
+ object{
+ picture(tiles.png,350,0,50,50)
+ }
+ }
blockState(fragile3){
oneTimeAnimation=6,fragile3_1
object{
animation=20,0
pictureAnimation(tiles.png){
point(150,50,50,100,1,2)
point(250,50,50,100,2,2)
}
}
}
blockState(fragile3_1){
object{
picture(tiles.png,250,50,50,100)
invisibleAtRunTime=1
}
}
}
block(MovingBlock){
editorPicture(tiles.png,350,200,50,50)
blockState(default){
object{
picture(tiles.png,0,0,50,50)
optionalPicture(tiles.png,50,0,50,50,0.15)
optionalPicture(tiles.png,100,0,50,50,0.15)
editorPicture(tiles.png,350,200,50,50)
}
}
}
block(MovingShadowBlock){
editorPicture(tiles.png,300,200,50,50)
blockState(default){
object{
picture(tiles.png,0,50,50,50)
editorPicture(tiles.png,300,200,50,50)
}
}
}
block(Exit){
editorPicture(tiles.png,150,200,50,50)
blockState(default){
- oneTimeAnimation=6,open
object{
- animation=80,0
- pictureAnimation(tiles.png){
- point(50,200,50,50,1,2)
- point(150,200,50,50,2,2)
- }
+ picture(tiles.png,150,200,50,50)
}
}
blockState(closed){
object{
picture(tiles.png,0,200,50,50)
}
}
- blockState(open){
+ transitionState(closed,default){
+ oneTimeAnimation=6,open
object{
- picture(tiles.png,150,200,50,50)
+ animation=80,0
+ pictureAnimation(tiles.png){
+ point(50,200,50,50,1,2)
+ point(150,200,50,50,2,2)
+ }
}
}
}
block(Spikes){
editorPicture(tiles.png,450,0,50,50)
blockState(default){
object{
picture(tiles.png,450,0,50,50)
optionalPicture(tiles.png,450,50,50,50,0.5)
}
}
}
block(MovingSpikes){
editorPicture(tiles.png,400,200,50,50)
blockState(default){
object{
editorPicture(tiles.png,400,200,50,50)
picture(tiles.png,450,0,50,50)
optionalPicture(tiles.png,450,50,50,50,0.5)
}
}
}
block(Checkpoint){
editorPicture(tiles.png,50,50,50,50)
blockState(default){
object{
picture(tiles.png,50,50,50,50)
}
}
blockState(activated){
object{
animation=16,0
picture(tiles.png,100,50,50,50)
offsetAnimation{
point(0,0)
point(0,-4,4,1)
point(0,4,8,1)
point(0,0,4,1)
}
}
}
}
block(Swap){
editorPicture(swap.png,0,0,50,50)
blockState(default){
object{
picture(swap.png,0,0,50,50)
}
}
blockState(activated){
oneTimeAnimation=24,default
object{
animation=12,0
pictureAnimation(swap.png){
point(0,0,50,50)
point(600,0,50,50,12,1)
}
}
}
}
block(Teleporter){
editorPicture(tiles.png,300,50,50,50)
blockState(default){
object{
picture(tiles.png,300,50,50,50)
}
}
blockState(activated){
oneTimeAnimation=9,default
object{
animation=3,0
pictureAnimation(tiles.png){
point(300,50,50,50)
point(450,50,50,50,3,1)
}
}
}
}
block(Switch){
editorPicture(tiles.png,0,100,50,50)
blockState(default){
object{
picture(tiles.png,0,100,50,50)
}
}
- blockState(done){
+ transitionState(default,activated){
+ oneTimeAnimation=3,activated
object{
- picture(tiles.png,100,100,50,50)
+ animation=3,0
+ pictureAnimation(tiles.png){
+ point(0,100,50,50)
+ point(150,100,50,50,3,1)
+ }
}
}
blockState(activated){
- oneTimeAnimation=3,done
+ object{
+ picture(tiles.png,100,100,50,50)
+ }
+ }
+ transitionState(activated,default){
+ oneTimeAnimation=3,default
object{
animation=3,0
pictureAnimation(tiles.png){
- point(0,100,50,50)
- point(150,100,50,50,3,1)
+ point(150,100,50,50)
+ point(0,100,50,50,3,1)
}
}
}
}
block(Button){
editorPicture(tiles.png,450,200,50,50)
blockState(default){
object{
picture(tiles.png,0,0,50,50)
optionalPicture(tiles.png,50,0,50,50,0.15)
optionalPicture(tiles.png,100,0,50,50,0.15)
}
}
blockState(button){
object{
# TODO:
picture(tiles.png,300,100,50,16)
}
}
}
block(NotificationBlock){
editorPicture(tiles.png,350,100,50,50)
blockState(default){
object{
picture(tiles.png,350,100,50,50)
}
}
}
block(ConveyorBelt){
editorPicture(tiles.png,450,100,50,50)
blockState(default){
object{
picture(tiles.png,450,100,50,50)
editorPicture(tiles.png,450,100,50,50)
}
}
}
block(ShadowConveyorBelt){
editorPicture(tiles.png,400,100,50,50)
blockState(default){
object{
picture(tiles.png,400,100,50,50)
}
}
}
block(PlayerStart){
editorPicture(tiles.png,250,200,50,50)
blockState(default){
object{
picture(tiles.png,250,200,50,50)
invisibleAtRunTime=1
}
}
}
block(ShadowStart){
editorPicture(tiles.png,200,200,50,50)
blockState(default){
object{
picture(tiles.png,200,200,50,50)
invisibleAtRunTime=1
}
}
}
block(Collectable){
editorPicture(collectable.png,0,0,50,50)
blockState(inactive){
object{
}
}
blockState(default){
object{
picture(collectable.png,0,0,50,50)
}
}
}
block(Pushable){
editorPicture(tiles.png,350,200,50,50)
blockState(default){
object{
picture(tiles.png,350,200,50,50)
editorPicture(tiles.png,350,200,50,50)
}
}
}
diff --git a/src/ThemeManager.cpp b/src/ThemeManager.cpp
index 59078e4..e8ea0a4 100644
--- a/src/ThemeManager.cpp
+++ b/src/ThemeManager.cpp
@@ -1,989 +1,1012 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ThemeManager.h"
#include "POASerializer.h"
#include "Functions.h"
#include "FileManager.h"
#include "Game.h"
#ifdef __APPLE__
#include <SDL_gfx/SDL_rotozoom.h>
#else
#include <SDL/SDL_rotozoom.h>
#endif
#include <string.h>
#include <iostream>
using namespace std;
//The ThemeStack that is be used by the GameState.
ThemeStack objThemes;
bool ThemeManager::loadFile(const string& fileName){
POASerializer objSerializer;
TreeStorageNode objNode;
//First we destroy the current ThemeManager.
destroy();
//Now we try to load the file, if it fails we return false.
if(!objSerializer.loadNodeFromFile(fileName.c_str(),&objNode,true)){
cerr<<"ERROR: Unable to open theme file: "<<fileName<<endl;
return false;
}
//Set the themePath.
themePath=pathFromFileName(fileName);
//Retrieve the name of the theme from the file.
{
vector<string> &v=objNode.attributes["name"];
if(!v.empty()) themeName=v[0];
}
//Reset themeable colors to default
themeTextColor.r=themeTextColor.g=themeTextColor.b=0;
themeTextColorDialog.r=themeTextColorDialog.g=themeTextColorDialog.b=0;
//Read themeable colors if any
vector<string> &ct=objNode.attributes["textColor"];
if(!ct.empty()){
themeTextColor.r=atoi(ct[0].c_str());
themeTextColor.g=atoi(ct[1].c_str());
themeTextColor.b=atoi(ct[2].c_str());
}
vector<string> &ct2=objNode.attributes["textColorDialog"];
if(!ct2.empty()){
themeTextColorDialog.r=atoi(ct2[0].c_str());
themeTextColorDialog.g=atoi(ct2[1].c_str());
themeTextColorDialog.b=atoi(ct2[2].c_str());
}
//Loop the subnodes of the theme.
for(unsigned int i=0;i<objNode.subNodes.size();i++){
TreeStorageNode *obj=objNode.subNodes[i];
//Check if it's a block or a background.
if(obj->name=="block" && !obj->value.empty()){
map<string,int>::iterator it=Game::blockNameMap.find(obj->value[0]);
if(it!=Game::blockNameMap.end()){
int idx=it->second;
if(!objBlocks[idx]) objBlocks[idx]=new ThemeBlock;
if(!objBlocks[idx]->loadFromNode(obj,themePath)){
cerr<<"ERROR: Unable to load "<<Game::blockName[idx]<<" for theme "<<fileName<<endl;
delete objBlocks[idx];
objBlocks[idx]=NULL;
return false;
}
}
}else if(obj->name=="background" && !obj->value.empty()){
if(!objBackground) objBackground=new ThemeBackground();
if(!objBackground->addPictureFromNode(obj,themePath)){
cerr<<"ERROR: Unable to load background for theme "<<fileName<<endl;
delete objBackground;
objBackground=NULL;
return false;
}
}else if(obj->name=="character" && !obj->value.empty()){
if(obj->value[0]=="Shadow"){
if(!shadow) shadow=new ThemeCharacter();
if(!shadow->loadFromNode(obj,themePath)){
cerr<<"ERROR: Unable to load shadow for theme "<<fileName<<endl;
delete shadow;
shadow=NULL;
return false;
}
}else if(obj->value[0]=="Player"){
if(!player) player=new ThemeCharacter();
if(!player->loadFromNode(obj,themePath)){
cerr<<"ERROR: Unable to load player for theme "<<fileName<<endl;
delete player;
player=NULL;
return false;
}
}
}else if(obj->name=="menuBackground" && !obj->value.empty()){
if(!menuBackground) menuBackground=new ThemeBackground();
if(!menuBackground->addPictureFromNode(obj,themePath)){
cerr<<"ERROR: Unable to load background for theme "<<fileName<<endl;
delete menuBackground;
menuBackground=NULL;
return false;
}
}else if(obj->name=="menu" && obj->value[0]=="Block"){
if(!menuBlock) menuBlock=new ThemeBlock;
if(!menuBlock->loadFromNode(obj,themePath)){
cerr<<"ERROR: Unable to load menu block for theme "<<fileName<<endl;
delete menuBlock;
menuBlock=NULL;
return false;
}
}
}
//Done and nothing went wrong so return true.
return true;
}
bool ThemeBlock::loadFromNode(TreeStorageNode* objNode, string themePath){
destroy();
//Loop the subNodes.
for(unsigned int i=0;i<objNode->subNodes.size();i++){
TreeStorageNode *obj=objNode->subNodes[i];
//Check if the subnode is an editorPicture or a blockState.
if(obj->name=="editorPicture"){
if(!editorPicture.loadFromNode(obj,themePath)) return false;
}else if(obj->name=="blockState" && !obj->value.empty()){
string& s=obj->value[0];
map<string,ThemeBlockState*>::iterator it=blockStates.find(s);
if(it==blockStates.end()) blockStates[s]=new ThemeBlockState;
if(!blockStates[s]->loadFromNode(obj,themePath)) return false;
+ }else if(obj->name=="transitionState" && obj->value.size()==2){
+ pair<string,string> s=pair<string,string>(obj->value[0],obj->value[1]);
+ map<pair<string,string>,ThemeBlockState*>::iterator it=transitions.find(s);
+ if(it==transitions.end()) transitions[s]=new ThemeBlockState;
+ if(!transitions[s]->loadFromNode(obj,themePath)) return false;
}
}
//Done and nothing went wrong so return true.
return true;
}
bool ThemeBlockState::loadFromNode(TreeStorageNode* objNode, string themePath){
destroy();
//Retrieve the oneTimeAnimation attribute.
{
vector<string> &v=objNode->attributes["oneTimeAnimation"];
//Check if there are enough values for the oneTimeAnimation attribute.
if(v.size()>=2 && !v[0].empty()){
oneTimeAnimationLength=atoi(v[0].c_str());
nextState=v[1];
}
}
//Loop the subNodes.
for(unsigned int i=0;i<objNode->subNodes.size();i++){
TreeStorageNode *obj=objNode->subNodes[i];
if(obj->name=="object"){
ThemeObject *obj1=new ThemeObject();
if(!obj1->loadFromNode(obj,themePath)){
delete obj1;
return false;
}
themeObjects.push_back(obj1);
}
}
//Done and nothing went wrong so return true.
return true;
}
bool ThemeCharacter::loadFromNode(TreeStorageNode* objNode,string themePath){
destroy();
//Loop the subNodes.
for(unsigned int i=0;i<objNode->subNodes.size();i++){
TreeStorageNode *obj=objNode->subNodes[i];
//Check if the subnode is an characterState.
if(obj->name=="characterState" && !obj->value.empty()){
string& s=obj->value[0];
map<string,ThemeCharacterState*>::iterator it=characterStates.find(s);
if(it==characterStates.end()) characterStates[s]=new ThemeCharacterState;
if(!characterStates[s]->loadFromNode(obj,themePath)) return false;
}
}
//Done and nothing went wrong so return true.
return true;
}
bool ThemeCharacterState::loadFromNode(TreeStorageNode* objNode,string themePath){
destroy();
//Retrieve the oneTimeAnimation attribute.
{
vector<string> &v=objNode->attributes["oneTimeAnimation"];
//Check if there are enough values for the oneTimeAnimation attribute.
if(v.size()>=2 && !v[0].empty()){
oneTimeAnimationLength=atoi(v[0].c_str());
nextState=v[1];
}
}
//Loop the subNodes.
for(unsigned int i=0;i<objNode->subNodes.size();i++){
TreeStorageNode *obj=objNode->subNodes[i];
if(obj->name=="object"){
ThemeObject *obj1=new ThemeObject();
if(!obj1->loadFromNode(obj,themePath)){
delete obj1;
return false;
}
themeObjects.push_back(obj1);
}
}
//Done and nothing went wrong so return true.
return true;
}
bool ThemeObject::loadFromNode(TreeStorageNode* objNode,string themePath){
destroy();
//Retrieve the animation attribute.
{
vector<string> &v=objNode->attributes["animation"];
if(v.size()>=2){
animationLength=atoi(v[0].c_str());
animationLoopPoint=atoi(v[1].c_str());
}
}
//Retrieve the oneTimeAnimation attribute.
{
vector<string> &v=objNode->attributes["oneTimeAnimation"];
if(v.size()>=2){
animationLength=atoi(v[0].c_str());
animationLoopPoint=atoi(v[1].c_str())|0x80000000;
}
}
//Retrieve the invisibleAtRunTime attribute.
{
vector<string> &v=objNode->attributes["invisibleAtRunTime"];
if(!v.empty() && !v[0].empty()){
invisibleAtRunTime=atoi(v[0].c_str())?true:false;
}
}
//Retrieve the invisibleAtDesignTime attribute.
{
vector<string> &v=objNode->attributes["invisibleAtDesignTime"];
if(!v.empty() && !v[0].empty()){
invisibleAtDesignTime=atoi(v[0].c_str())?true:false;
}
}
//Loop the subnodes.
for(unsigned int i=0;i<objNode->subNodes.size();i++){
TreeStorageNode *obj=objNode->subNodes[i];
if(obj->name=="picture" || obj->name=="pictureAnimation"){
if(!picture.loadFromNode(obj,themePath)){
return false;
}
}else if(obj->name=="editorPicture"){
if(!editorPicture.loadFromNode(obj,themePath)){
return false;
}
}else if(obj->name=="optionalPicture" && obj->value.size()>=6){
ThemePicture *objPic=new ThemePicture();
double f=atof(obj->value[5].c_str());
if(!objPic->loadFromNode(obj,themePath)){
delete objPic;
return false;
}
optionalPicture.push_back(pair<double,ThemePicture*>(f,objPic));
}else if(obj->name=="offset" || obj->name=="offsetAnimation"){
if(!offset.loadFromNode(obj)) return false;
}
}
//Done and nothing went wrong so return true.
return true;
}
bool ThemePicture::loadFromNode(TreeStorageNode* objNode,string themePath){
destroy();
//Check if the node has enough values.
if(!objNode->value.empty()){
//Load teh picture.
picture=loadImage(themePath+objNode->value[0]);
if(picture==NULL) return false;
//Check if it's an animation.
if(objNode->name=="pictureAnimation"){
if(!offset.loadFromNode(objNode)) return false;
return true;
}else if(objNode->value.size()>=5){
typeOffsetPoint r={atoi(objNode->value[1].c_str()),
atoi(objNode->value[2].c_str()),
atoi(objNode->value[3].c_str()),
atoi(objNode->value[4].c_str()),0,0};
offset.offsetData.push_back(r);
offset.length=0;
return true;
}
}
//Done and nothing went wrong so return true.
return false;
}
bool ThemeOffsetData::loadFromNode(TreeStorageNode* objNode){
destroy();
//Check what kind of offset it is.
if(objNode->name=="pictureAnimation"){
for(unsigned int i=0;i<objNode->subNodes.size();i++){
TreeStorageNode* obj=objNode->subNodes[i];
if(obj->name=="point" && obj->value.size()>=4){
typeOffsetPoint r={atoi(obj->value[0].c_str()),
atoi(obj->value[1].c_str()),
atoi(obj->value[2].c_str()),
atoi(obj->value[3].c_str()),1,1};
if(obj->value.size()>=5) r.frameCount=atoi(obj->value[4].c_str());
if(obj->value.size()>=6) r.frameDisplayTime=atoi(obj->value[5].c_str());
offsetData.push_back(r);
length+=r.frameCount*r.frameDisplayTime;
}
}
return true;
}else if(objNode->name=="offsetAnimation"){
for(unsigned int i=0;i<objNode->subNodes.size();i++){
TreeStorageNode* obj=objNode->subNodes[i];
if(obj->name=="point" && obj->value.size()>=2){
typeOffsetPoint r={atoi(obj->value[0].c_str()),
atoi(obj->value[1].c_str()),0,0,1,1};
if(obj->value.size()>=3) r.frameCount=atoi(obj->value[2].c_str());
if(obj->value.size()>=4) r.frameDisplayTime=atoi(obj->value[3].c_str());
offsetData.push_back(r);
length+=r.frameCount*r.frameDisplayTime;
}
}
return true;
}else if(objNode->name=="offset" && objNode->value.size()>=2){
typeOffsetPoint r={atoi(objNode->value[0].c_str()),
atoi(objNode->value[1].c_str()),0,0,0,0};
offsetData.push_back(r);
length=0;
return true;
}
//Done and nothing went wrong so return true.
return false;
}
void ThemeObjectInstance::draw(SDL_Surface *dest,int x,int y,SDL_Rect *clipRect){
//Get the picture.
SDL_Surface *src=picture->picture;
if(src==NULL) return;
int ex=0,ey=0,xx=0,yy=0,ww=0,hh=0;
int animationNew=animation&0x7FFFFFFF;
{
vector<typeOffsetPoint> &v=picture->offset.offsetData;
if(picture->offset.length==0 || animationNew<v[0].frameDisplayTime){
xx=v[0].x;
yy=v[0].y;
ww=v[0].w;
hh=v[0].h;
}else if(animationNew>=picture->offset.length){
int i=v.size()-1;
xx=v[i].x;
yy=v[i].y;
ww=v[i].w;
hh=v[i].h;
}else{
int t=animationNew-v[0].frameDisplayTime;
for(unsigned int i=1;i<v.size();i++){
int tt=t/v[i].frameDisplayTime;
if(tt>=0 && tt<v[i].frameCount){
xx=(int)((float)v[i-1].x+(float)(v[i].x-v[i-1].x)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
yy=(int)((float)v[i-1].y+(float)(v[i].y-v[i-1].y)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
ww=(int)((float)v[i-1].w+(float)(v[i].w-v[i-1].w)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
hh=(int)((float)v[i-1].h+(float)(v[i].h-v[i-1].h)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
break;
}else{
t-=v[i].frameCount*v[i].frameDisplayTime;
}
}
}
}
//Get the offset.
{
vector<typeOffsetPoint> &v=parent->offset.offsetData;
if(v.empty()){
ex=0;
ey=0;
}else if(parent->offset.length==0 || animationNew<v[0].frameDisplayTime){
ex=v[0].x;
ey=v[0].y;
}else if(animationNew>=parent->offset.length){
int i=v.size()-1;
ex=v[i].x;
ey=v[i].y;
}else{
int t=animationNew-v[0].frameDisplayTime;
for(unsigned int i=1;i<v.size();i++){
int tt=t/v[i].frameDisplayTime;
if(tt>=0 && tt<v[i].frameCount){
ex=(int)((float)v[i-1].x+(float)(v[i].x-v[i-1].x)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
ey=(int)((float)v[i-1].y+(float)(v[i].y-v[i-1].y)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
break;
}else{
t-=v[i].frameCount*v[i].frameDisplayTime;
}
}
}
}
//And finally draw the ThemeObjectInstance.
if(clipRect){
int d;
d=clipRect->x-ex;
if(d>0){
ex+=d;
xx+=d;
ww-=d;
}
d=clipRect->y-ey;
if(d>0){
ey+=d;
yy+=d;
hh-=d;
}
if(ww>clipRect->w) ww=clipRect->w;
if(hh>clipRect->h) hh=clipRect->h;
}
if(ww>0&&hh>0){
SDL_Rect r1={xx,yy,ww,hh};
SDL_Rect r2={x+ex,y+ey,0,0};
SDL_BlitSurface(src,&r1,dest,&r2);
}
}
void ThemeObjectInstance::updateAnimation(){
//First get the animation length.
int m;
m=parent->animationLength;
//If it's higher than 0 then we have an animation.
if(m>0 && animation>=0){
//Increase the animation frame.
animation++;
//Check if the animation is beyond the length, if so set it to the looppoint.
if(animation>=m)
animation=parent->animationLoopPoint;
}
}
void ThemeBlockInstance::updateAnimation(){
//Make sure the currentState isn't null.
if(currentState!=NULL){
//Call the updateAnimation method of the currentState.
currentState->updateAnimation();
//Get the length of the animation.
int m=currentState->parent->oneTimeAnimationLength;
//If it's higher than 0 then we have an animation.
//Also check if it's past the lenght, meaning done.
if(m>0 && currentState->animation>=m){
//Now we can change the state to the nextState.
changeState(currentState->parent->nextState);
}
}
}
void ThemeCharacterInstance::updateAnimation(){
//Make sure the currentState isn't null.
if(currentState!=NULL){
//Call the updateAnimation method of the currentState.
currentState->updateAnimation();
//Get the length of the animation.
int m=currentState->parent->oneTimeAnimationLength;
//If it's higher than 0 then we have an animation.
//Also check if it's past the lenght, meaning done.
if(m>0 && currentState->animation>=m){
//Now we can change the state to the nextState.
changeState(currentState->parent->nextState);
}
}
}
void ThemeBlock::createInstance(ThemeBlockInstance* obj){
//Make sure the given ThemeBlockInstance is ready.
obj->blockStates.clear();
+ obj->transitions.clear();
obj->currentState=NULL;
//Loop through the blockstates.
for(map<string,ThemeBlockState*>::iterator it=blockStates.begin();it!=blockStates.end();++it){
//Get the themeBlockStateInstance of the given ThemeBlockInstance.
ThemeBlockStateInstance &obj1=obj->blockStates[it->first];
//Set the parent of the state instance.
obj1.parent=it->second;
- //Get the vector with themeObjects.
- vector<ThemeObject*> &v=it->second->themeObjects;
-
- //Loop through them.
- for(unsigned int i=0;i<v.size();i++){
- //Create an instance for every one.
- ThemeObjectInstance p;
- //Set the parent.
- p.parent=v[i];
-
- //Choose the picture.
- if(stateID==STATE_LEVEL_EDITOR){
- if(p.parent->invisibleAtDesignTime)
- continue;
- if(p.parent->editorPicture.picture!=NULL)
- p.picture=&p.parent->editorPicture;
- }else{
- if(p.parent->invisibleAtRunTime)
- continue;
- }
-
- //Get the number of optional Pictures.
- int m=p.parent->optionalPicture.size();
- //If p.picture is null, not an editor picture, and there are optional pictures then give one random.
- if(p.picture==NULL && m>0){
- double f=0.0,f1=1.0/256.0;
- for(int j=0;j<8;j++){
- f+=f1*(double)(rand()&0xff);
- f1*=(1.0/256.0);
- }
- for(int j=0;j<m;j++){
- f-=p.parent->optionalPicture[j].first;
- if(f<0.0){
- p.picture=p.parent->optionalPicture[j].second;
- break;
- }
- }
- }
-
- //If random turned out to give nothing then give the non optional picture.
- if(p.picture==NULL && p.parent->picture.picture!=NULL)
- p.picture=&p.parent->picture;
- //If the picture isn't null then can we give it to the ThemeBlockStateInstance.
- if(p.picture!=NULL)
- obj1.objects.push_back(p);
- }
+
+ //Create the state instance.
+ createStateInstance(&obj1);
+ }
+
+ //Loop through the transitions.
+ for(map<pair<string,string>,ThemeBlockState*>::iterator it=transitions.begin();it!=transitions.end();++it){
+ //Get the themeBlockStateInstance of the given ThemeBlockInstance.
+ ThemeBlockStateInstance &obj1=obj->transitions[it->first];
+ //Set the parent of the state instance.
+ obj1.parent=it->second;
+
+ //Create the state instance.
+ createStateInstance(&obj1);
}
//Change the state to the default one.
//FIXME: Is that needed?
obj->changeState("default");
}
+void ThemeBlock::createStateInstance(ThemeBlockStateInstance* obj){
+ //Get the vector with themeObjects.
+ vector<ThemeObject*> &v=obj->parent->themeObjects;
+
+ //Loop through them.
+ for(unsigned int i=0;i<v.size();i++){
+ //Create an instance for every one.
+ ThemeObjectInstance p;
+ //Set the parent.
+ p.parent=v[i];
+
+ //Choose the picture.
+ if(stateID==STATE_LEVEL_EDITOR){
+ if(p.parent->invisibleAtDesignTime)
+ continue;
+ if(p.parent->editorPicture.picture!=NULL)
+ p.picture=&p.parent->editorPicture;
+ }else{
+ if(p.parent->invisibleAtRunTime)
+ continue;
+ }
+
+ //Get the number of optional Pictures.
+ int m=p.parent->optionalPicture.size();
+ //If p.picture is null, not an editor picture, and there are optional pictures then give one random.
+ if(p.picture==NULL && m>0){
+ double f=0.0,f1=1.0/256.0;
+ for(int j=0;j<8;j++){
+ f+=f1*(double)(rand()&0xff);
+ f1*=(1.0/256.0);
+ }
+ for(int j=0;j<m;j++){
+ f-=p.parent->optionalPicture[j].first;
+ if(f<0.0){
+ p.picture=p.parent->optionalPicture[j].second;
+ break;
+ }
+ }
+ }
+
+ //If random turned out to give nothing then give the non optional picture.
+ if(p.picture==NULL && p.parent->picture.picture!=NULL)
+ p.picture=&p.parent->picture;
+ //If the picture isn't null then can we give it to the ThemeBlockStateInstance.
+ if(p.picture!=NULL)
+ obj->objects.push_back(p);
+ }
+}
+
void ThemeCharacter::createInstance(ThemeCharacterInstance* obj){
//Make sure the given ThemeCharacterInstance is ready.
obj->characterStates.clear();
obj->currentState=NULL;
//Loop through the characterstates.
for(map<string,ThemeCharacterState*>::iterator it=characterStates.begin();it!=characterStates.end();++it){
//Get the themeCharacterStateInstance of the given ThemeCharacterInstance.
ThemeCharacterStateInstance &obj1=obj->characterStates[it->first];
//Set the parent of the state instance.
obj1.parent=it->second;
//Get the vector with themeObjects.
vector<ThemeObject*> &v=it->second->themeObjects;
//Loop through them.
for(unsigned int i=0;i<v.size();i++){
//Create an instance for every one.
ThemeObjectInstance p;
//Set the parent.
p.parent=v[i];
//Make sure it isn't invisible at runtime.
if(p.parent->invisibleAtRunTime)
continue;
//Get the number of optional Pictures.
int m=p.parent->optionalPicture.size();
//If p.picture is null, not an editor picture, and there are optional pictures then give one random.
if(p.picture==NULL && m>0){
double f=0.0,f1=1.0/256.0;
for(int j=0;j<8;j++){
f+=f1*(double)(rand()&0xff);
f1*=(1.0/256.0);
}
for(int j=0;j<m;j++){
f-=p.parent->optionalPicture[j].first;
if(f<0.0){
p.picture=p.parent->optionalPicture[j].second;
break;
}
}
}
//If random turned out to give nothing then give the non optional picture.
if(p.picture==NULL && p.parent->picture.picture!=NULL)
p.picture=&p.parent->picture;
//If the picture isn't null then can we give it to the ThemeCharacterStateInstance.
if(p.picture!=NULL)
obj1.objects.push_back(p);
}
}
//Set it to the standing right state.
obj->changeState("standright");
}
void ThemePicture::draw(SDL_Surface *dest,int x,int y,int animation,SDL_Rect *clipRect){
//Get the Picture.
if(picture==NULL) return;
int ex=0,ey=0,xx,yy,ww,hh;
{
vector<typeOffsetPoint> &v=offset.offsetData;
if(offset.length==0 || animation<v[0].frameDisplayTime){
xx=v[0].x;
yy=v[0].y;
ww=v[0].w;
hh=v[0].h;
}else if(animation>=offset.length){
int i=v.size()-1;
xx=v[i].x;
yy=v[i].y;
ww=v[i].w;
hh=v[i].h;
}else{
int t=animation-v[0].frameDisplayTime;
for(unsigned int i=1;i<v.size();i++){
int tt=t/v[i].frameDisplayTime;
if(tt>=0 && tt<v[i].frameCount){
xx=(int)((float)v[i-1].x+(float)(v[i].x-v[i-1].x)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
yy=(int)((float)v[i-1].y+(float)(v[i].y-v[i-1].y)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
ww=(int)((float)v[i-1].w+(float)(v[i].w-v[i-1].w)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
hh=(int)((float)v[i-1].h+(float)(v[i].h-v[i-1].h)*(float)(tt+1)/(float)v[i].frameCount+0.5f);
break;
}else{
t-=v[i].frameCount*v[i].frameDisplayTime;
}
}
}
}
//Draw the Picture.
if(clipRect){
int d;
d=clipRect->x-ex;
if(d>0){
ex+=d;
xx+=d;
ww-=d;
}
d=clipRect->y-ey;
if(d>0){
ey+=d;
yy+=d;
hh-=d;
}
if(ww>clipRect->w) ww=clipRect->w;
if(hh>clipRect->h) hh=clipRect->h;
}
if(ww>0&&hh>0){
SDL_Rect r1={xx,yy,ww,hh};
SDL_Rect r2={x+ex,y+ey,0,0};
SDL_BlitSurface(picture,&r1,dest,&r2);
}
}
//This method will scale the background picture (if needed and configured) to the current SCREEN_WIDTH and SCREEN_HEIGHT.
void ThemeBackgroundPicture::scaleToScreen(){
//Only scale if needed.
if(scale){
//Free the surface of the scaled picture, if scaled.
if(picture!=cachedPicture)
SDL_FreeSurface(picture);
//Set src and destSize back to the initial cached value.
srcSize=cachedSrcSize;
destSize=cachedDestSize;
//Scale the image.
//Calculate the x and y factors.
double xFactor=double(SCREEN_WIDTH)/double(100);
double yFactor=double(SCREEN_HEIGHT)/double(100);
//The default scaling method is chosen (destSize in precentages).
destSize.x*=xFactor;
destSize.w*=xFactor;
destSize.y*=yFactor;
destSize.h*=yFactor;
//Now update the image.
xFactor=(double(destSize.w)/double(srcSize.w));
yFactor=(double(destSize.h)/double(srcSize.h));
if(xFactor!=1 || yFactor!=1){
picture=zoomSurface(cachedPicture,xFactor,yFactor,0);
//Also update the source size.
srcSize.x*=xFactor;
srcSize.y*=yFactor;
srcSize.w*=xFactor;
srcSize.h*=yFactor;
}else{
//We don't need to scale the image
picture=cachedPicture;
}
}
}
void ThemeBackgroundPicture::draw(SDL_Surface *dest){
//Check if the picture is visible.
if(!(picture&&srcSize.w>0&&srcSize.h>0&&destSize.w>0&&destSize.h>0))
return;
//Calculate the draw area.
int sx=(int)((float)destSize.x+currentX-cameraX*(float)camera.x+0.5f);
int sy=(int)((float)destSize.y+currentY-cameraY*(float)camera.y+0.5f);
int ex,ey;
//Include repeating.
if(repeatX){
sx%=destSize.w;
if(sx>0) sx-=destSize.w;
ex=SCREEN_WIDTH;
}else{
if(sx<=-(int)destSize.w || sx>=SCREEN_WIDTH) return;
ex=sx+1;
}
if(repeatY){
sy%=destSize.h;
if(sy>0) sy-=destSize.h;
ey=SCREEN_HEIGHT;
}else{
if(sy<=-(int)destSize.h || sy>=SCREEN_HEIGHT) return;
ey=sy+1;
}
//And finally draw the ThemeBackgroundPicture.
for(int x=sx;x<ex;x+=destSize.w){
for(int y=sy;y<ey;y+=destSize.h){
SDL_Rect r={x,y,0,0};
SDL_BlitSurface(picture,&srcSize,dest,&r);
}
}
}
bool ThemeBackgroundPicture::loadFromNode(TreeStorageNode* objNode,string themePath){
//Load the picture.
picture=loadImage(themePath+objNode->value[0]);
//Store pointer to the cached picture.
cachedPicture=picture;
if(picture==NULL) return false;
//Retrieve the source size.
{
vector<string> &v=objNode->attributes["srcSize"];
if(v.size()>=4){
srcSize.x=atoi(v[0].c_str());
srcSize.y=atoi(v[1].c_str());
srcSize.w=atoi(v[2].c_str());
srcSize.h=atoi(v[3].c_str());
}else{
srcSize.x=0;
srcSize.y=0;
srcSize.w=picture->w;
srcSize.h=picture->h;
}
//Cache the sourcesize.
cachedSrcSize=srcSize;
}
//Retrieve the destination size.
{
vector<string> &v=objNode->attributes["destSize"];
if(v.size()>=4){
destSize.x=atoi(v[0].c_str());
destSize.y=atoi(v[1].c_str());
destSize.w=atoi(v[2].c_str());
destSize.h=atoi(v[3].c_str());
}else{
destSize.x=0;
destSize.y=0;
destSize.w=100;
destSize.h=100;
}
//Cache the destsize.
cachedDestSize=destSize;
}
//Retrieve if we should scale to screen.
{
//Get scaleToScreen.
vector<string> &v=objNode->attributes["scaleToScreen"];
//Boolean if the image should be scaled, default is true.
scale=true;
if(!v.empty()){
scale=atoi(v[0].c_str());
}
//Now scaleToScreen.
//NOTE: We don't check if scaleToScreen is true or false since that is done in scaleToScreen();
scaleToScreen();
}
//Retrieve if it should be repeated.
{
vector<string> &v=objNode->attributes["repeat"];
if(v.size()>=2){
repeatX=atoi(v[0].c_str())?true:false;
repeatY=atoi(v[1].c_str())?true:false;
}else{
repeatX=true;
repeatY=true;
}
}
//Retrieve the speed.
{
vector<string> &v=objNode->attributes["speed"];
if(v.size()>=2){
speedX=atof(v[0].c_str());
speedY=atof(v[1].c_str());
}else{
speedX=0.0f;
speedY=0.0f;
}
}
//Retrieve the camera speed.
{
vector<string> &v=objNode->attributes["cameraSpeed"];
if(v.size()>=2){
cameraX=atof(v[0].c_str());
cameraY=atof(v[1].c_str());
}else{
cameraX=0.0f;
cameraY=0.0f;
}
}
//Done and nothing went wrong so return true.
return true;
}
//Constructor.
ThemeStack::ThemeStack(){
}
//Destructor.
ThemeStack::~ThemeStack(){
//Loop through the themes and delete them.
for(unsigned int i=0;i<objThemes.size();i++)
delete objThemes[i];
}
//Method that will destroy the ThemeStack.
void ThemeStack::destroy(){
//Loop through the themes and delete them.
for(unsigned int i=0;i<objThemes.size();i++)
delete objThemes[i];
//Clear the vector to prevent dangling pointers.
objThemes.clear();
}
//Method that will append a theme to the stack.
//obj: The ThemeManager to add.
void ThemeStack::appendTheme(ThemeManager* obj){
objThemes.push_back(obj);
//debug
#if defined(DEBUG) || defined(_DEBUG)
cout<<"ThemeStack::appendTheme(): theme count="<<objThemes.size()<<endl;
#endif
}
//Method that will remove the last theme added to the stack.
void ThemeStack::removeTheme(){
//Make sure that the stack isn't empty.
if(!objThemes.empty()){
delete objThemes.back();
objThemes.pop_back();
}
}
//Method that will append a theme that will be loaded from file.
//fileName: The file to load the theme from.
//Returns: Pointer to the newly added theme, NULL if failed.
ThemeManager* ThemeStack::appendThemeFromFile(const string& fileName){
//Create a new themeManager.
ThemeManager* obj=new ThemeManager();
//Let it load from the given file.
if(!obj->loadFile(fileName)){
//Failed thus delete the theme and return null.
cerr<<"ERROR: Failed loading theme "<<fileName<<endl;
delete obj;
return NULL;
}else{
//Succeeded, add it to the stack and return it.
objThemes.push_back(obj);
return obj;
}
}
//Method that is used to let the themes scale.
void ThemeStack::scaleToScreen(){
//Loop through the themes and call their scaleToScreen method.
for(unsigned int i=0;i<objThemes.size();i++)
objThemes[i]->scaleToScreen();
}
//Get a pointer to the ThemeBlock of a given block type.
//index: The type of block.
//Returns: Pointer to the ThemeBlock.
ThemeBlock* ThemeStack::getBlock(int index,bool menu){
//Loop through the themes from top to bottom.
for(int i=objThemes.size()-1;i>=0;i--){
//Get the block from the theme.
ThemeBlock* obj=objThemes[i]->getBlock(index,menu);
//Check if it isn't null.
if(obj)
return obj;
}
//Nothing found.
return NULL;
}
//Get a pointer to the ThemeCharacter of the shadow or the player.
//isShadow: Boolean if it's the shadow
//Returns: Pointer to the ThemeCharacter.
ThemeCharacter* ThemeStack::getCharacter(bool isShadow){
//Loop through the themes from top to bottom.
for(int i=objThemes.size()-1;i>=0;i--){
//Get the ThemeCharacter from the theme.
ThemeCharacter* obj=objThemes[i]->getCharacter(isShadow);
//Check if it isn't null.
if(obj)
return obj;
}
//Nothing found.
return NULL;
}
//Get a pointer to the ThemeBackground of the theme.
//Returns: Pointer to the ThemeBackground.
ThemeBackground* ThemeStack::getBackground(bool menu){
//Loop through the themes from top to bottom.
for(int i=objThemes.size()-1;i>=0;i--){
//Get the ThemeBackground from the theme.
ThemeBackground* obj=objThemes[i]->getBackground(menu);
//Check if it isn't null.
if(obj)
return obj;
}
//Nothing found.
return NULL;
}
diff --git a/src/ThemeManager.h b/src/ThemeManager.h
index 26a931f..95c9bec 100644
--- a/src/ThemeManager.h
+++ b/src/ThemeManager.h
@@ -1,1070 +1,1108 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow 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.
*
* Me and My Shadow 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 Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef THEMEMANAGER_H
#define THEMEMANAGER_H
#include "Globals.h"
#include "TreeStorageNode.h"
#ifdef __APPLE__
#include <SDL_gfx/SDL_rotozoom.h>
#else
#include <SDL/SDL_rotozoom.h>
#endif
#include <string.h>
#include <math.h>
#include <string>
#include <vector>
#include <utility>
#include <iostream>
using namespace std;
//Structure containing offset data for one frame.
struct typeOffsetPoint{
//The location (x,y) and size (w,h).
int x,y,w,h;
//The frame to which this offset applies.
int frameCount;
//The number of frames this offset is shown.
int frameDisplayTime;
};
//We already need the classes so declare them here.
class ThemeOffsetData;
class ThemePicture;
class ThemeObject;
class ThemeBlockState;
class ThemeBlock;
class ThemeCharacterState;
class ThemeCharacter;
//Instance class of a ThemeObject, this is used by the other Instance classes.
class ThemeObjectInstance{
public:
//Pointer to the picture.
ThemePicture* picture;
//Pointer to the parent the object an instance os is.
ThemeObject* parent;
//Integer containing the current animation frame.
int animation;
//Integer containing the saved animation frame.
int savedAnimation;
public:
//Constructor.
ThemeObjectInstance():picture(NULL),parent(NULL),animation(0),savedAnimation(0){}
//Method used to draw the ThemeObject.
//dest: The destination surface to draw the ThemeObject on.
//x: The x location on the dest surface.
//y: The y location on the dest surface.
//clipRect: Rectangle used to clip.
void draw(SDL_Surface* dest,int x,int y,SDL_Rect* clipRect=NULL);
//Method that will update the animation.
void updateAnimation();
//Method that will reset the animation.
//save: Boolean if the saved animation should be deleted.
void resetAnimation(bool save){
animation=0;
if(save){
savedAnimation=0;
}
}
//Method that will save the animation.
void saveAnimation(){
savedAnimation=animation;
}
//Method that will load a saved animation.
void loadAnimation(){
animation=savedAnimation;
}
};
//Instance class of a ThemeBlockState, this is used by the ThemeBlockInstance.
class ThemeBlockStateInstance{
public:
//Pointer to the parent the state an instance of is.
ThemeBlockState *parent;
//Vector containing the ThemeObjectInstances.
vector<ThemeObjectInstance> objects;
//Integer containing the current animation frame.
int animation;
//Integer containing the saved animation frame.
int savedAnimation;
public:
//Constructor.
ThemeBlockStateInstance():parent(NULL),animation(0),savedAnimation(0){}
//Method used to draw the ThemeBlockState.
//dest: The destination surface to draw the ThemeBlockState on.
//x: The x location on the dest surface.
//y: The y location on the dest surface.
//clipRect: Rectangle used to clip.
void draw(SDL_Surface *dest,int x,int y,SDL_Rect *clipRect=NULL){
for(unsigned int i=0;i<objects.size();i++){
objects[i].draw(dest,x,y,clipRect);
}
}
//Method that will update the animation.
void updateAnimation(){
for(unsigned int i=0;i<objects.size();i++){
objects[i].updateAnimation();
}
animation++;
}
//Method that will reset the animation.
//save: Boolean if the saved state should be deleted.
void resetAnimation(bool save){
for(unsigned int i=0;i<objects.size();i++){
objects[i].resetAnimation(save);
}
animation=0;
if(save){
savedAnimation=0;
}
}
//Method that will save the animation.
void saveAnimation(){
for(unsigned int i=0;i<objects.size();i++){
objects[i].saveAnimation();
}
savedAnimation=animation;
}
//Method that will load a saved animation.
void loadAnimation(){
for(unsigned int i=0;i<objects.size();i++){
objects[i].loadAnimation();
}
animation=savedAnimation;
}
};
//Instance of a ThemeBlock, this is used by blocks in the game to prevent changing the theme in game.
//It also allows animation to run independently.
class ThemeBlockInstance{
public:
//Pointer to the current state.
ThemeBlockStateInstance* currentState;
//The name of the current state.
string currentStateName;
//Map containing the blockStates.
map<string,ThemeBlockStateInstance> blockStates;
+ //Map containing the blockTransitionStates.
+ map<pair<string,string>,ThemeBlockStateInstance> transitions;
//String containing the name of the saved state.
string savedStateName;
public:
//Constructor.
ThemeBlockInstance():currentState(NULL){}
//Method used to draw the ThemeBlock.
//dest: The destination surface to draw the ThemeBlock on.
//x: The x location on the dest surface.
//y: The y location on the dest surface.
//clipRect: Rectangle used to clip.
//Returns: True if it succeeds.
bool draw(SDL_Surface *dest,int x,int y,SDL_Rect *clipRect=NULL){
if(currentState!=NULL){
currentState->draw(dest,x,y,clipRect);
return true;
}
return false;
}
//Method that will draw a specific state.
//s: The name of the state to draw.
//dest: The destination surface to draw the ThemeBlock on.
//x: The x location on the dest surface.
//y: The y location on the dest surface.
//clipRect: Rectangle used to clip.
//Returns: True if it succeeds.
bool drawState(const string& s,SDL_Surface *dest,int x,int y,SDL_Rect *clipRect=NULL){
map<string,ThemeBlockStateInstance>::iterator it=blockStates.find(s);
if(it!=blockStates.end()){
it->second.draw(dest,x,y,clipRect);
return true;
}
return false;
}
//Method that will change the current state.
//s: The name of the state to change to.
//reset: Boolean if the animation should reset.
//Returns: True if it succeeds (exists).
bool changeState(const string& s,bool reset=true){
- //Get the new state.
- map<string,ThemeBlockStateInstance>::iterator it=blockStates.find(s);
- //Check if it exists.
- if(it!=blockStates.end()){
- //Set the state.
- currentState=&(it->second);
- currentStateName=it->first;
-
+ bool newState=false;
+
+ //First check if there's a transition.
+ {
+ pair<string,string> s1=pair<string,string>(currentStateName,s);
+ map<pair<string,string>,ThemeBlockStateInstance>::iterator it=transitions.find(s1);
+ if(it!=transitions.end()){
+ currentState=&it->second;
+ //NOTE: We set the currentState name to target state name.
+ //Worst case senario is that the animation is skipped when saving/loading at a checkpoint.
+ currentStateName=s;
+ newState=true;
+ }
+ }
+
+ //If there isn't a transition go directly to the state.
+ if(!newState){
+ //Get the new state.
+ map<string,ThemeBlockStateInstance>::iterator it=blockStates.find(s);
+ //Check if it exists.
+ if(it!=blockStates.end()){
+ currentState=&it->second;
+ currentStateName=it->first;
+ newState=true;
+ }
+ }
+
+ //Check if a state has been found.
+ if(newState){
//FIXME: Is it needed to set the savedStateName here?
if(savedStateName.empty())
savedStateName=currentStateName;
//If reset then reset the animation.
if(reset)
currentState->resetAnimation(true);
return true;
}
//It doesn't so return false.
return false;
}
//Method that will update the animation.
void updateAnimation();
//Method that will reset the animation.
//save: Boolean if the saved state should be deleted.
void resetAnimation(bool save){
for(map<string,ThemeBlockStateInstance>::iterator it=blockStates.begin();it!=blockStates.end();++it){
it->second.resetAnimation(save);
}
if(save){
savedStateName.clear();
}
}
//Method that will save the animation.
void saveAnimation(){
for(map<string,ThemeBlockStateInstance>::iterator it=blockStates.begin();it!=blockStates.end();++it){
it->second.saveAnimation();
}
savedStateName=currentStateName;
}
//Method that will restore a saved animation.
void loadAnimation(){
for(map<string,ThemeBlockStateInstance>::iterator it=blockStates.begin();it!=blockStates.end();++it){
it->second.loadAnimation();
}
changeState(savedStateName,false);
}
};
//Instance class of a ThemeCharacterState, this is used by the ThemeCharacterInstance.
class ThemeCharacterStateInstance{
public:
//Pointer to the parent the state an instance of is.
ThemeCharacterState* parent;
//Vector containing the ThemeObjectInstances.
vector<ThemeObjectInstance> objects;
//Integer containing the current animation frame.
int animation;
//Integer containing the saved animation frame.
int savedAnimation;
public:
//Constructor.
ThemeCharacterStateInstance():parent(NULL),animation(0),savedAnimation(0){}
//Method used to draw the ThemeCharacterState.
//dest: The destination surface to draw the ThemeCharacterState on.
//x: The x location on the dest surface.
//y: The y location on the dest surface.
//clipRect: Rectangle used to clip.
void draw(SDL_Surface *dest,int x,int y,SDL_Rect *clipRect=NULL){
for(unsigned int i=0;i<objects.size();i++){
objects[i].draw(dest,x,y,clipRect);
}
}
//Method that will update the animation.
void updateAnimation(){
for(unsigned int i=0;i<objects.size();i++){
objects[i].updateAnimation();
}
animation++;
}
//Method that will reset the animation.
//save: Boolean if the saved state should be deleted.
void resetAnimation(bool save){
for(unsigned int i=0;i<objects.size();i++){
objects[i].resetAnimation(save);
}
animation=0;
if(save)
savedAnimation=0;
}
//Method that will save the animation.
void saveAnimation(){
for(unsigned int i=0;i<objects.size();i++){
objects[i].saveAnimation();
}
savedAnimation=animation;
}
//Method that will load a saved animation.
void loadAnimation(){
for(unsigned int i=0;i<objects.size();i++){
objects[i].loadAnimation();
}
animation=savedAnimation;
}
};
//Instance of a ThemeCharacter.
class ThemeCharacterInstance{
public:
//Pointer to the current state.
ThemeCharacterStateInstance* currentState;
//The name of the current state.
string currentStateName;
//Map containing the ThemeCharacterStates.
map<string,ThemeCharacterStateInstance> characterStates;
//String containing the name of the saved state.
string savedStateName;
public:
//Constructor.
ThemeCharacterInstance():currentState(NULL){}
//Method used to draw the ThemeCharacter.
//dest: The destination surface to draw the ThemeCharacter on.
//x: The x location on the dest surface.
//y: The y location on the dest surface.
//clipRect: Rectangle used to clip.
//Returns: True if it succeeds.
bool draw(SDL_Surface *dest,int x,int y,SDL_Rect *clipRect=NULL){
if(currentState!=NULL){
currentState->draw(dest,x,y,clipRect);
return true;
}
return false;
}
//Method that will draw a specific state.
//s: The name of the state to draw.
//dest: The destination surface to draw the ThemeCharacter on.
//x: The x location on the dest surface.
//y: The y location on the dest surface.
//clipRect: Rectangle used to clip.
//Returns: True if it succeeds.
bool drawState(const string& s,SDL_Surface *dest,int x,int y,SDL_Rect *clipRect=NULL){
map<string,ThemeCharacterStateInstance>::iterator it=characterStates.find(s);
if(it!=characterStates.end()){
it->second.draw(dest,x,y,clipRect);
return true;
}
return false;
}
//Method that will change the current state.
//s: The name of the state to change to.
//reset: Boolean if the animation should reset.
//Returns: True if it succeeds (exists).
bool changeState(const string& s,bool reset=true){
//Get the new state.
map<string,ThemeCharacterStateInstance>::iterator it=characterStates.find(s);
//Check if it exists.
if(it!=characterStates.end()){
//Set the state.
currentState=&(it->second);
currentStateName=it->first;
//FIXME: Is it needed to set the savedStateName here?
if(savedStateName.empty())
savedStateName=currentStateName;
//If reset then reset the animation.
if(reset)
currentState->resetAnimation(true);
return true;
}
//It doesn't so return false.
return false;
}
//Method that will update the animation.
void updateAnimation();
//Method that will reset the animation.
//save: Boolean if the saved state should be deleted.
void resetAnimation(bool save){
for(map<string,ThemeCharacterStateInstance>::iterator it=characterStates.begin();it!=characterStates.end();++it){
it->second.resetAnimation(save);
}
if(save)
savedStateName.clear();
}
//Method that will save the animation.
void saveAnimation(){
for(map<string,ThemeCharacterStateInstance>::iterator it=characterStates.begin();it!=characterStates.end();++it){
it->second.saveAnimation();
}
savedStateName=currentStateName;
}
//Method that will restore a saved animation.
void loadAnimation(){
for(map<string,ThemeCharacterStateInstance>::iterator it=characterStates.begin();it!=characterStates.end();++it){
it->second.loadAnimation();
}
changeState(savedStateName,false);
}
};
//Class containing the offset data.
class ThemeOffsetData{
public:
//Vector containing the offsetDatas.
vector<typeOffsetPoint> offsetData;
//The length of the "animation" in frames.
int length;
public:
//Constructor.
ThemeOffsetData():length(0){}
//Destructor.
~ThemeOffsetData(){}
//Method used to destroy the offsetData.
void destroy(){
//Set length to zero.
length=0;
//And clear the offsetData vector.
offsetData.clear();
}
//Method that will load the offsetData from a node.
//objNode: Pointer to the TreeStorageNode to read the data from.
//Returns: True if it succeeds without errors.
bool loadFromNode(TreeStorageNode* objNode);
};
//This is the lowest level of the theme system.
//It's a picture with offset data.
class ThemePicture{
public:
//The SDL_Surface containing the picture.
SDL_Surface* picture;
//Offset data for the picture.
ThemeOffsetData offset;
public:
//Constructor.
ThemePicture():picture(NULL){}
//Destructor.
~ThemePicture(){}
//Method used to destroy the picture.
void destroy(){
//FIXME: Shouldn't the image be freed? (ImageManager)
picture=NULL;
//Destroy the offset data.
offset.destroy();
}
bool loadFromNode(TreeStorageNode* objNode, string themePath);
//Method that will draw the ThemePicture.
//dest: The destination surface.
//x: The x location on the dest to draw the picture.
//y: The y location on the dest to draw the picture.
//animation: The frame of the animation to draw.
//clipRect: Rectangle to clip the picture.
void draw(SDL_Surface* dest,int x,int y,int animation=0,SDL_Rect* clipRect=NULL);
};
//The ThemeObject class is used to contain a basic theme element.
//Contains the picture, animation information, etc...
class ThemeObject{
public:
//Integer containing the length of the animation.
int animationLength;
//Integer containing the frame from where the animation is going to loop.
int animationLoopPoint;
//Boolean if the animation is invisible at run time (Game state).
bool invisibleAtRunTime;
//Boolean if the animation is invisible at design time (Level editor).
bool invisibleAtDesignTime;
//Picture of the ThemeObject.
ThemePicture picture;
//Picture of the ThemeObject shown when in the level editor.
ThemePicture editorPicture;
//Vector containing optionalPicture for the ThemeObject.
vector<pair<double,ThemePicture*> > optionalPicture;
//ThemeOffsetData for the ThemeObject.
ThemeOffsetData offset;
public:
//Constructor.
ThemeObject():animationLength(0),animationLoopPoint(0),invisibleAtRunTime(false),invisibleAtDesignTime(false){}
//Destructor.
~ThemeObject(){
//Loop through the optionalPicture and delete them.
for(unsigned int i=0;i<optionalPicture.size();i++){
delete optionalPicture[i].second;
}
}
//Method that will destroy the ThemeObject.
void destroy(){
//Loop through the optionalPicture and delete them.
for(unsigned int i=0;i<optionalPicture.size();i++){
delete optionalPicture[i].second;
}
optionalPicture.clear();
animationLength=0;
animationLoopPoint=0;
invisibleAtRunTime=false;
invisibleAtDesignTime=false;
picture.destroy();
editorPicture.destroy();
offset.destroy();
}
//Method that will load a ThemeObject from a node.
//objNode: The TreeStorageNode to read the object from.
//themePath: Path to the theme.
//Returns: True if it succeeds.
bool loadFromNode(TreeStorageNode* objNode,string themePath);
};
//Class containing a single state of a themed block.
class ThemeBlockState{
public:
//The length in frames of the oneTimeAnimation.
int oneTimeAnimationLength;
//String containing the name of the next state.
string nextState;
//Vector containing the themeObjects that make up this state.
vector<ThemeObject*> themeObjects;
public:
//Constructor.
ThemeBlockState():oneTimeAnimationLength(0){}
//Destructor.
~ThemeBlockState(){
//Loop through the ThemeObjects and delete them.
for(unsigned int i=0;i<themeObjects.size();i++){
delete themeObjects[i];
}
}
//Method that will destroy the ThemeBlockState.
void destroy(){
//Loop through the ThemeObjects and delete them.
for(unsigned int i=0;i<themeObjects.size();i++){
delete themeObjects[i];
}
//Clear the themeObjects vector.
themeObjects.clear();
//Set the length to 0.
oneTimeAnimationLength=0;
//Clear the nextState string.
nextState.clear();
}
//Method that will load a ThemeBlockState from a node.
//objNode: The TreeStorageNode to read the state from.
//themePath: Path to the theme.
//Returns: True if it succeeds.
bool loadFromNode(TreeStorageNode* objNode,string themePath);
};
//Class containing the needed things for a themed block.
class ThemeBlock{
public:
//Picture that is shown only in the level editor.
ThemePicture editorPicture;
//Map containing ThemeBlockStates for the different states of a block.
map<string,ThemeBlockState*> blockStates;
+ //Map containing the transition states between blocks states.
+ map<pair<string,string>,ThemeBlockState*> transitions;
public:
//Constructor.
ThemeBlock(){}
//Destructor/
~ThemeBlock(){
//Loop through the ThemeBlockStates and delete them,
for(map<string,ThemeBlockState*>::iterator i=blockStates.begin();i!=blockStates.end();++i){
delete i->second;
}
+ //Loop through the ThemeBlockStates and delete them,
+ for(map<pair<string,string>,ThemeBlockState*>::iterator i=transitions.begin();i!=transitions.end();++i){
+ delete i->second;
+ }
}
//Method that will destroy the ThemeBlock.
void destroy(){
//Loop through the ThemeBlockStates and delete them,
for(map<string,ThemeBlockState*>::iterator i=blockStates.begin();i!=blockStates.end();++i){
delete i->second;
}
+ //Loop through the ThemeBlockStates transitions and delete them,
+ for(map<pair<string,string>,ThemeBlockState*>::iterator i=transitions.begin();i!=transitions.end();++i){
+ delete i->second;
+ }
//Clear the blockStates map.
blockStates.clear();
+ transitions.clear();
editorPicture.destroy();
}
//Method that will load a ThemeBlock from a node.
//objNode: The TreeStorageNode to load the ThemeBlock from.
//themePath: The path to the theme.
//Returns: True if it succeeds.
bool loadFromNode(TreeStorageNode* objNode,string themePath);
//Method that will create a ThemeBlockInstance.
//obj: Pointer that will be filled with the instance.
void createInstance(ThemeBlockInstance* obj);
+private:
+ //Method that will create a ThemeBlockStateInstance.
+ //obj: Pointer that will be filled with the instance.
+ void createStateInstance(ThemeBlockStateInstance* obj);
};
//Class containing one state of a ThemeCharacter
class ThemeCharacterState{
public:
//The length in frames of the oneTimeAnimation.
int oneTimeAnimationLength;
//String containing the name of the next id.
string nextState;
//Vector with the themeObjects in the character state.
vector<ThemeObject*> themeObjects;
public:
//Constructor.
ThemeCharacterState():oneTimeAnimationLength(0){}
//Destructor.
~ThemeCharacterState(){
//Loop through the themeObjects and delete them.
for(unsigned int i=0;i<themeObjects.size();i++){
delete themeObjects[i];
}
}
//Method used to destroy the ThemeCharacterState.
void destroy(){
//Loop through the themeObjects and delete them.
for(unsigned int i=0;i<themeObjects.size();i++){
delete themeObjects[i];
}
//Clear the themeObjects vector.
themeObjects.clear();
//Set oneTimeAnimation to zero.
oneTimeAnimationLength=0;
//Clear the nextState string.
nextState.clear();
}
//Method that will load the ThemeCharacterState from a node.
//objNode: The TreeStorageNode to load the state from.
//themePath: Path to the theme.
//Returns: True if it succeeds.
bool loadFromNode(TreeStorageNode* objNode,string themePath);
};
//Class containing the things needed for a themed character.
class ThemeCharacter{
public:
//Map containing ThemeCharacterStates for the different states of a character.
map<string,ThemeCharacterState*> characterStates;
public:
//Constructor.
ThemeCharacter(){}
//Destructor.
~ThemeCharacter(){
//Loop through the states and delete them.
for(map<string,ThemeCharacterState*>::iterator i=characterStates.begin();i!=characterStates.end();++i){
delete i->second;
}
}
//Method that will destroy the ThemeCharacter.
void destroy(){
//Loop through the states and delete them.
for(map<string,ThemeCharacterState*>::iterator i=characterStates.begin();i!=characterStates.end();++i){
delete i->second;
}
//Clear the characterStates map.
characterStates.clear();
}
//Method that will load a ThemeCharacter from a node.
//objNode: The TreeStorageNode to load the ThemeCharacter from.
//themePath: The path to the theme.
//Returns: True if it succeeds.
bool loadFromNode(TreeStorageNode* objNode,string themePath);
//Method that will create a ThemeCharacterInstance.
//obj: Pointer that will be filled with the instance.
void createInstance(ThemeCharacterInstance* obj);
};
//ThemeBackgroundPicture is a class containing the picture for the background.
class ThemeBackgroundPicture{
private:
//Pointer to the SDL_Surface cached by the ImageManager.
//This is used to rescale the theme.
SDL_Surface* cachedPicture;
//Rectangle that should be taken from the picture.
//NOTE The size is pixels of the image.
SDL_Rect cachedSrcSize;
//Rectangle with the size it will have on the destination (screen).
//NOTE The size is in pixels or in precentages (if scaleToScreen is true).
SDL_Rect cachedDestSize;
//SDL_Surface containing the picture.
//NOTE: This could point to the same surface as cachedPicture.
SDL_Surface* picture;
//Rectangle that should be taken from the picture.
//NOTE The size is pixels of the image.
SDL_Rect srcSize;
//Rectangle with the size it will have on the destination (screen).
//NOTE The size is in pixels even though the loaded value from the theme description file can be in precentages (if scaleToScreen is true).
SDL_Rect destSize;
//Boolean if the background picture should be scaled to screen.
bool scale;
//Boolean if the image should be repeated over the x-axis.
bool repeatX;
//Boolean if the image should be repeated over the y-axis.
bool repeatY;
//Float containing the speed the background picture moves along the x-axis.
float speedX;
//Float containing the speed the background picture moves along the y-axis.
float speedY;
//Float containing the horizontal speed the picture will have when moving the camera (horizontally).
float cameraX;
//Float containing the vertical speed the picture will have when moving the camera (vertically).
float cameraY;
private:
//Float with the current x position.
float currentX;
//Float with the current y position.
float currentY;
//Stored x location for when loading a state.
float savedX;
//Stored y location for when loading a state.
float savedY;
public:
//Constructor.
ThemeBackgroundPicture(){
//Set some default values.
picture=NULL;
cachedPicture=NULL;
memset(&srcSize,0,sizeof(srcSize));
memset(&destSize,0,sizeof(destSize));
memset(&cachedSrcSize,0,sizeof(cachedSrcSize));
memset(&cachedDestSize,0,sizeof(cachedDestSize));
scale=true;
repeatX=true;
repeatY=true;
speedX=0.0f;
speedY=0.0f;
cameraX=0.0f;
cameraY=0.0f;
currentX=0.0f;
currentY=0.0f;
savedX=0.0f;
savedY=0.0f;
}
//Method that will update the animation.
void updateAnimation(){
//Move the picture along the x-axis.
currentX+=speedX;
if(repeatX && destSize.w>0){
float f=(float)destSize.w;
if(currentX>f || currentX<-f) currentX-=f*floor(currentX/f);
}
//Move the picture along the y-axis.
currentY+=speedY;
if(repeatY && destSize.h>0){
float f=(float)destSize.h;
if(currentY>f || currentY<-f) currentY-=f*floor(currentY/f);
}
}
//Method that will reset the animation.
//save: Boolean if the saved state should be deleted.
void resetAnimation(bool save){
currentX=0.0f;
currentY=0.0f;
if(save){
savedX=0.0f;
savedY=0.0f;
}
}
//Method that will save the animation.
void saveAnimation(){
savedX=currentX;
savedY=currentY;
}
//Method that will load the animation.
void loadAnimation(){
currentX=savedX;
currentY=savedY;
}
//Method used to draw the ThemeBackgroundPicture.
//dest: Pointer to the SDL_Surface the picture should be drawn.
void draw(SDL_Surface *dest);
//Method used to load the ThemeBackgroundPicture from a node.
//objNode: The TreeStorageNode to load the picture from.
//themePath: The path to the theme.
bool loadFromNode(TreeStorageNode* objNode,string themePath);
//This method will scale the background picture (if needed and configured) to the current SCREEN_WIDTH and SCREEN_HEIGHT.
void scaleToScreen();
};
//Class that forms the complete background of a theme.
//It is in fact nothing more than a vector containing multiple ThemeBackgroundPictures.
class ThemeBackground{
private:
//Vector containing the ThemeBackgroundPictures.
vector<ThemeBackgroundPicture> picture;
public:
//Method that will update the animation of all the background pictures.
void updateAnimation(){
for(unsigned int i=0;i<picture.size();i++){
picture[i].updateAnimation();
}
}
//Method that will reset the animation of all the background pictures.
//save: Boolean if the saved state should be deleted.
void resetAnimation(bool save){
for(unsigned int i=0;i<picture.size();i++){
picture[i].resetAnimation(save);
}
}
//Method that will save the animation of all the background pictures.
void saveAnimation(){
for(unsigned int i=0;i<picture.size();i++){
picture[i].saveAnimation();
}
}
//Method that will load the animation of all the background pictures.
void loadAnimation(){
for(unsigned int i=0;i<picture.size();i++){
picture[i].loadAnimation();
}
}
//Method that will scale the background pictures (if set) to the current screen resolution.
void scaleToScreen(){
for(unsigned int i=0;i<picture.size();i++){
picture[i].scaleToScreen();
}
}
//This method will draw all the background pictures.
//dest: Pointer to the SDL_Surface to draw them on.
void draw(SDL_Surface* dest){
for(unsigned int i=0;i<picture.size();i++){
picture[i].draw(dest);
}
}
//Method that will add a ThemeBackgroundPicture to the ThemeBackground.
//objNode: The treeStorageNode to read from.
//themePath: The path to the theme.
//Returns: True if it succeeds.
bool addPictureFromNode(TreeStorageNode* objNode,string themePath){
picture.push_back(ThemeBackgroundPicture());
return picture.back().loadFromNode(objNode,themePath);
}
};
//The ThemeManager is actually a whole theme, filled with ThemeBlocks, ThemeCharacter and ThemeBackground.
class ThemeManager{
private:
//The ThemeCharacter of the shadow.
ThemeCharacter* shadow;
//The ThemeCharacter of the player.
ThemeCharacter* player;
//Array containing a ThemeBlock for every block type.
ThemeBlock* objBlocks[TYPE_MAX];
//The ThemeBackground.
ThemeBackground* objBackground;
//ThemeBackground for menu.
ThemeBackground* menuBackground;
//Level selection background block.
ThemeBlock* menuBlock;
public:
//String containing the path to the string.
string themePath;
//String containing the theme name.
string themeName;
public:
//Constructor.
ThemeManager(){
//Make sure the pointers are set to NULL.
objBackground=NULL;
//Reserve enough memory for the ThemeBlocks.
memset(objBlocks,0,sizeof(objBlocks));
shadow=NULL;
player=NULL;
menuBackground=NULL;
menuBlock=NULL;
}
//Destructor.
~ThemeManager(){
//Delete the ThemeCharacter of the shadow.
if(shadow)
delete shadow;
//Delete the ThemeCharacter of the player.
if(player)
delete player;
//Loop through the ThemeBlocks and delete them.
for(int i=0;i<TYPE_MAX;i++){
if(objBlocks[i])
delete objBlocks[i];
}
//Delete the ThemeBackgrounds.
if(objBackground)
delete objBackground;
if(menuBackground)
delete menuBackground;
if(menuBlock)
delete menuBlock;
}
//Method used to destroy the ThemeManager.
void destroy(){
//Delete the ThemeCharacter of the shadow.
if(shadow)
delete shadow;
//Delete the ThemeCharacter of the player.
if(player)
delete player;
//Loop through the ThemeBlocks and delete them.
for(int i=0;i<TYPE_MAX;i++){
if(objBlocks[i])
delete objBlocks[i];
}
//Delete the ThemeBackground.
if(objBackground)
delete objBackground;
//And clear the themeName.
themeName.clear();
}
//Method that will load the theme from a file.
//fileName: The file to load the theme from.
//Returns: True if it succeeds.
bool loadFile(const string& fileName);
//Method that will scale the theme to the current SCREEN_WIDTH and SCREEN_HEIGHT.
void scaleToScreen(){
//We only need to scale the background.
if(objBackground)
objBackground->scaleToScreen();
}
//Get a pointer to the ThemeBlock of a given block type.
//index: The type of block.
//menu: Boolean if get spefial blocks for menu
//Returns: Pointer to the ThemeBlock.
ThemeBlock* getBlock(int index,bool menu){
if(!menu)
return objBlocks[index];
else
if(index==TYPE_BLOCK)
if(menuBlock)
return menuBlock;
else
return objBlocks[TYPE_BLOCK];
else if(index==TYPE_SHADOW_BLOCK)
if(menuBlock)
return menuBlock;
else
return objBlocks[TYPE_SHADOW_BLOCK];
else
return objBlocks[index];
}
//Get a pointer to the ThemeCharacter of the shadow or the player.
//isShadow: Boolean if it's the shadow
//Returns: Pointer to the ThemeCharacter.
ThemeCharacter* getCharacter(bool isShadow){
if(isShadow)
return shadow;
return player;
}
//Get a pointer to the ThemeBackground of the theme.
//bool: Boolean if get menu background
//Returns: Pointer to the ThemeBackground.
ThemeBackground* getBackground(bool menu){
if(menu&&menuBackground)
return menuBackground;
else
return objBackground;
}
};
//Class that combines multiple ThemeManager into one stack.
//If a file is not in a certain theme it will use one of a lower theme.
class ThemeStack{
private:
//Vector containing the themes in the stack.
vector<ThemeManager*> objThemes;
public:
//Constructor.
ThemeStack();
//Destructor.
~ThemeStack();
//Method that will destroy the ThemeStack.
void destroy();
//Method that will append a theme to the stack.
//obj: The ThemeManager to add.
void appendTheme(ThemeManager* obj);
//Method that will remove the last theme added to the stack.
void removeTheme();
//Method that will append a theme that will be loaded from file.
//fileName: The file to load the theme from.
//Returns: Pointer to the newly added theme, NULL if failed.
ThemeManager* appendThemeFromFile(const string& fileName);
//Method that is used to let the themes scale.
void scaleToScreen();
//Get the number of themes in the stack.
//Returns: The theme count.
int themeCount(){
return (int)objThemes.size();
}
//Operator overloading so that the themes can be accesed using the [] operator.
//i: The index.
ThemeManager* operator[](int i){
return objThemes[i];
}
//Get a pointer to the ThemeBlock of a given block type.
//index: The type of block.
//Returns: Pointer to the ThemeBlock.
ThemeBlock* getBlock(int index,bool menu=false);
//Get a pointer to the ThemeCharacter of the shadow or the player.
//isShadow: Boolean if it's the shadow
//Returns: Pointer to the ThemeCharacter.
ThemeCharacter* getCharacter(bool isShadow);
//Get a pointer to the ThemeBackground of the theme.
//Returns: Pointer to the ThemeBackground.
ThemeBackground* getBackground(bool menu);
};
//The ThemeStack that is be used by the GameState.
extern ThemeStack objThemes;
#endif

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 16, 7:20 PM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63397
Default Alt Text
(76 KB)

Event Timeline