Note: It would be a good exercise to try and find/fix the code yourself.
Common
iterator can't be decremented
This is an issue with Visual Studio 2005 as compared to Visual Studio 2003. Even if you're running VS 2003, do make these changes, otherwise the code might break under other compilers. It is invalid to decrement the iterator returned by begin(), an operation that silently fails in VS 2003.
Modify the code so that it conforms to the following structure:
Code:
for (it = collection.begin(); it != collection.end();)
{
// ...
if (/* the current iterator should be deleted */)
{
// ...
it = erase(it);
}
else
++it;
}
Examples:
- Level::update()
From VTMs
Code:
void Level::update()
{
for(iter = npc.begin(); iter != npc.end(); iter++)
{
// ...
if((*iter)->isAlive() == false)
{
Sprite *temp = *iter;
iter--;
delete temp;
npc.remove(temp);
}
}
}
Converted
Code:
void Level::update()
{
for(iter = npc.begin(); iter != npc.end();)
{
// ...
if((*iter)->isAlive() == false)
{
Sprite *temp = *iter;
delete temp;
iter = npc.erase(iter);
}
else
iter++;
}
}
- Particle Engine:
From VTMs
Code:
for (list<Particle *>::iterator it = particles.begin(); it != particles.end(); it++)
{
...
if ( particle->active == false )
{
delete particle;
list<Particle *>::iterator pTemp = it--;
particles.erase(pTemp);
}
}
Converted
Code:
for (list<Particle *>::iterator it = particles.begin(); it != particles.end();)
{
// ...
if ( particle->active == false )
{
delete particle;
it = particles.erase(it);
}
else
++it;
}
Cannot convert from 'const char[]' to 'const wxString'
Solution: Wrap strings with "_T()", or construct a "wxString" first.
Example: Change code such as the following:
Code:
wxImage myImage("test.bmp");
to either
Code:
wxImage myImage(_T("test.bmp"));
or
Code:
wxString imageFile(_T("test.bmp"));
wxImage myImage(imageFile);
VTM 1
The code used in the VTM is as follows:
Code:
#include <iostream>
main()
{
std::cout << "Hello World!" << std::endl;
}
This code is not standard C++ and only works under Visual C++ 2003 and lower. Please change it to the following:
Code:
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
return 0;
}
The difference is the inclusion of "int" and "return 0;". When a program ends, it must inform the operating system if it ended in a good or bad state and then the operating system can inform the user of this or simply pass the information on to other programs. The extra code supports this. When saying, "return 0", we are telling the operating system how the program performed. In this case "0", means "everything went well". As you watch further VTMs, the need for the "int" will make sense. For now, accept it as it is. Do not use "void" instead of "int". The use of "void" is non-standard and is not supported across all compilers.
VTM 3
Level.cpp
Code:
void Level::setPlayerStart(void)
{
player->setPosition(startX, startY);
}
int Level::numEnemies(void)
{
int num = 0;
for (Iter = npc.begin(); Iter != npc.end(); Iter++)
{
if ((*Iter)->getID() == ENEMY_CLASSID)
num++;
}
return num;
}
int Level::getWidth(void)
{
return width;
}
int Level::getHeight(void)
{
return height;
}
Level.h
Code:
public:
void setPlayerStart(void);
int numEnemies(void);
int getWidth(void);
int getHeight(void);
private:
int startX, startY;
Mage.cpp
Code:
void Mage::castSpell(void)
{
if (facingDirection.x == 0 && facingDirection.y == 0)
return;
if (facingDirection.y < -1 && facingDirection.y < -1)
return;
Fireball *temp = new Fireball(level, drawArea, SPRITE_FIREBALL, (int)pos.x + facingDirection.x,
(int)pos.y + facingDirection.y, facingDirection.x, facingDirection.y);
if (temp->move(facingDirection.x, facingDirection.y))
{
temp->update();
level->addNPC((Sprite *)temp);
}
else
update();
}
Sprite.cpp
Code:
bool Sprite::isValidLevelMove(int xpos, int ypos)
{
if (xpos >= 0 && xpos < level->getWidth() &&
ypos >= 0 && ypos < level->getHeight() &&
level->level[xpos][ypos] != TILE_WALL )
return true;
return false;
}
void Sprite::setPosition(int x, int y)
{
// erase sprite
erase(pos.x, pos.y);
facingDirection.x = facingDirection.y = -100;
pos.x = x;
pos.y = y;
// draw sprite
draw(pos.x, pos.y);
}
void Sprite::setLevel(Level *newLevel)
{
level = newLevel;
}
void Sprite::update(void)
{
draw(pos.x, pos.y);
}
int Sprite::getID(void)
{
return classID;
}
Sprite.h
Code:
public:
void setLevel(Level *newLevel);
void setPosition(int x, int y);
int getID(void);
virtual void update(void);
Character.cpp
Code:
void Character::addLives(int num)
{
Sprite::addLives(num);
if (Sprite::isAlive())
{
level->setPlayerStart();
update();
}
}
Fireball.cpp
Code:
void Fireball::idleUpdate(void)
{
if ((isValidLevelMove((int)pos.x, (int)pos.y)) && ((facingDirection.x + facingDirection.y) != 0))
{
if (Sprite::move(facingDirection.x, facingDirection.y))
{
list <Sprite *>::iterator Iter;
for (Iter = level->npc.begin(); Iter != level->npc.end(); Iter++)
{
if ((*Iter)->classID != classID &&
(int)(*Iter)->getX() ==(int)pos.x && (int)(*Iter)->getY() ==(int)pos.y)
{
(*Iter)->addLives(-1);
addLives(-1);
}
}
}
else
addLives(-1);
}
}
VTM 6
Non zero ID field length
Use seekg() to skip past the ID field. Example:
Code:
// read TGAheader
// skip image ID
file.seekg(TGAheader.ID_Length, ios::cur);
// read imageData
Flipped TGAs
Also see: http://3dbuzz.com/vbforum/showthread.php?t=111270
The TGA specification names a field not mentioned in the VTM - the Image Descriptor (field 5.6), under the Image Specification (field 5). Bits 4 and 5 are of interest.
What needs to be done:
- Add another byte at the end of the TGAHeader type defined by Joel:
Code:
GLubyte ImageDescriptor;
- Figure out if the image was flipped horizontally or vertically:
Code:
bool flipH = ((header.ImageDescriptor & 0x10) == 0x10);
bool flipV = ((header.ImageDescriptor & 0x20) == 0x20);
flipImage(imageData, flipH, flipV, width, height, bpp);
Add this code before the call to createTexure() in the loadTGA() function.
- Flip the image when appropriate:
Code:
void Texture::flipImage(unsigned char * image, bool flipHorizontal, bool flipVertical, GLushort width, GLushort height, GLbyte bpp){
GLbyte Bpp = bpp / 8;
if (flipHorizontal){
for (int h = 0; h < height; h++) {
for (int w = 0; w < width / 2; w++){
swap(image + (h * width + w) * Bpp, image + (h * width + width - w - 1)* Bpp, Bpp);
}
}
}
if (flipVertical){
for (int w = 0; w < width; w++){
for (int h = 0; h < height / 2; h++) {
swap(image + (h * width + w) * Bpp, image + ((height - h - 1) * width + w)* Bpp, Bpp);
}
}
}
}
void Texture::swap(unsigned char * ori, unsigned char * dest, GLint size){
GLubyte * temp = new unsigned char[size];
memcpy(temp, ori, size);
memcpy(ori, dest, size);
memcpy(dest, temp, size);
delete [] temp;
}
Both these functions are added to Texture with private visibility.
Available resources: explanation of flipImage(), how flipH and flipV are calculated.
Generously provided by ostamo, randywong and KhaoticMind.