Part 35: Teleporters
Downloads
Parameters of the teleporters
<tile name="Teleporter">
<image>Teleporter.png</image>
<param type="int">Level</param>
<param type="int">X</param>
<param type="int">Y</param>
<param type="enum" values="Up;Left;Down;Right;None">Side</param>
<param type="bool">forChampions</param>
<param type="bool">forObjects</param>
</tile>
Like the stairs, teleporters can bring you to another level at a (X,Y) position.
Graphics
QImage wallGfx = fileCache.getImage(fileName);
CVec2 wallPos(tileInfo->x, tileInfo->y);
QImage teleportGfx = fileCache.getImage("gfx/3DView/Teleporter.png");
// create the teleporter image
QImage tempImg(wallGfx.size(), QImage::Format_ARGB32);
tempImg.fill(TRANSPARENT);
int subImgX = (wallGfx.size().width() + teleportGfx.size().width() - 1) / teleportGfx.size().width();
int subImgY = (wallGfx.size().height() + teleportGfx.size().height() - 1) / teleportGfx.size().height();
[...]
for (int y = 0; y < subImgY; ++y)
for (int x = 0; x < subImgX; ++x)
{
CVec2 subImgPos(x * teleportGfx.size().width(),
y * teleportGfx.size().height());
[...]
graph2D.drawImage(&tempImg, subImgPos, teleportGfx, angle, flip);
}
// darken
int shadow = WALL_TABLE_HEIGHT - 2 - tablePos.y;
if (shadow < 0)
shadow = 0;
graph2D.darken(&tempImg, (float)shadow * 0.2f);
// draw the image
graph2D.drawImageWithMask(image, wallPos, tempImg, wallGfx);
Animation
int random = game.frameCounter / 20;
[...]
// create the teleporter image
[...]
random = (random * 31415821 + 1) % 100000000;
for (int y = 0; y < subImgY; ++y)
for (int x = 0; x < subImgX; ++x)
{
[...]
bool flip = ((random & 1) != 0);
int angle = ((random >> 1) % 4) * 90;
graph2D.drawImage(&tempImg, subImgPos, teleportGfx, angle, flip);
}
The whole tile
struct STelepTile
{
char file[16];
uint16_t x, y;
};
static const STelepTile gTelepTiles[WALL_TABLE_HEIGHT][WALL_TABLE_WIDTH][3] =
{
// Row 3 (farthest)
{
{{"Wall02_F.png", 0, 58}, {"", 0, 0}, {"Wall13_L.png", 8, 58}}, // 02
{{"Wall12_F.png", 7, 58}, {"", 0, 0}, {"Wall23_L.png", 78, 59}}, // 12
{{"Wall22_F.png", 77, 58}, {"", 0, 0}, {"", 0, 0} }, // 22
{{"Wall32_F.png", 146, 58}, {"Wall23_R.png", 134, 59}, {"", 0, 0} }, // 32
{{"Wall42_F.png", 216, 58}, {"Wall33_R.png", 180, 58}, {"", 0, 0} } // 42
},
// Row 2
{
{{"", 0, 0}, {"", 0, 0}, {"Wall12_L.png", 0, 57}}, // 01
{{"Wall11_F.png", 0, 52}, {"", 0, 0}, {"Wall22_L.png", 60, 52}}, // 11
{{"Wall21_F.png", 59, 52}, {"", 0, 0}, {"", 0, 0} }, // 21
{{"Wall31_F.png", 164, 52}, {"Wall22_R.png", 146, 52}, {"", 0, 0} }, // 31
{{"", 0, 0}, {"Wall32_R.png", 216, 57}, {"", 0, 0} } // 41
},
// Row 1
{
{{"", 0, 0}, {"", 0, 0}, {"", 0, 0} }, // 00
{{"Wall10_F.png", 0, 42}, {"", 0, 0}, {"Wall21_L.png", 33, 42}}, // 10
{{"Wall20_F.png", 32, 42}, {"", 0, 0}, {"", 0, 0} }, // 20
{{"Wall30_F.png", 191, 42}, {"Wall21_R.png", 164, 42}, {"", 0, 0} }, // 30
{{"", 0, 0}, {"", 0, 0}, {"", 0, 0} } // 40
},
// Row 0 (nearest)
{
{{"", 0, 0}, {"", 0, 0}, {"", 0, 0} }, // 00
{{"", 0, 0}, {"", 0, 0}, {"Wall20_L.png", 0, 33}}, // 10
{{"Wall20_F.png", 32, 42}, {"Wall20_R.png", 191, 33}, {"Wall20_L.png", 0, 33}}, // 20
{{"", 0, 0}, {"Wall20_R.png", 191, 33}, {"", 0, 0} }, // 30
{{"", 0, 0}, {"", 0, 0}, {"", 0, 0} } // 40
}
};
Note that this table handles the case when we are inside the teleporter - line 20.
void CTiles::drawTeleporter(QImage* image, CVec2 tablePos)
{
for (int i = 0; i < 3; ++i)
{
int random = game.frameCounter / 20;
const STelepTile* tileInfo = &gTelepTiles[tablePos.y][tablePos.x][i];
if (tileInfo->file[0] != 0)
{
static char fileName[256];
sprintf(fileName, "gfx/3DView/walls/%s", tileInfo->file);
QImage wallGfx = fileCache.getImage(fileName);
CVec2 wallPos(tileInfo->x, tileInfo->y);
QImage teleportGfx = fileCache.getImage("gfx/3DView/Teleporter.png");
// create the teleporter image
QImage tempImg(wallGfx.size(), QImage::Format_ARGB32);
tempImg.fill(TRANSPARENT);
int subImgX = (wallGfx.size().width() + teleportGfx.size().width() - 1) / teleportGfx.size().width();
int subImgY = (wallGfx.size().height() + teleportGfx.size().height() - 1) / teleportGfx.size().height();
random = (random * 31415821 + 1) % 100000000;
for (int y = 0; y < subImgY; ++y)
for (int x = 0; x < subImgX; ++x)
{
CVec2 subImgPos(x * teleportGfx.size().width(),
y * teleportGfx.size().height());
bool flip = ((random & 1) != 0);
int angle = ((random >> 1) % 4) * 90;
graph2D.drawImage(&tempImg, subImgPos, teleportGfx, angle, flip);
}
// darken
int shadow = WALL_TABLE_HEIGHT - 2 - tablePos.y;
if (shadow < 0)
shadow = 0;
graph2D.darken(&tempImg, (float)shadow * 0.2f);
// draw the image
graph2D.drawImageWithMask(image, wallPos, tempImg, wallGfx);
}
}
}
Stepping inside it
void CPlayer::teleport()
{
CTile* tile = map.getTile(pos);
int level = tile->getIntParam("Level");
CVec2 newPos = CVec2(tile->getIntParam("X"), tile->getIntParam("Y"));
int side = tile->getEnumParam("Side");
if (side == eWallSideMax)
side = dir;
if (level != game.currentLevel)
{
game.loadLevel(level, newPos, side);
}
else
{
player.pos = newPos;
player.dir = side;
}
sound->play(player.pos, "sound/Buzz.wav");
shouldTeleport = false;
}
Here it's pretty much the same as for the stairs. Except that we don't alway go to another level, and we don't
Dropping objects in it
else if (clickedArea->type == eMouseAreaG_DropObject)
{
CVec2 pos = CVec2((int)clickedArea->param1, (int)clickedArea->param2);
int side = (int)clickedArea->param3;
[... drop the object]
if (side == eWallSideMax)
{
CVec2 mapPos = pos / 2;
CTile* tile = map.getTile(mapPos);
if (tile->getType() == eTileTeleporter && tile->getBoolParam("forObjects") == true)
teleportStacks(mapPos);
}
}
The teleportStacks() function will teleport all the object stack on the 4 possible positions of the given tile.
void CGame::teleportStacks(CVec2 mapPos)
{
CTile* tile = map.getTile(mapPos);
std::vector<CObjectStack> copyStacks;
// copies the stacks
for (int y = 0; y < 2; ++y)
for (int x = 0; x < 2; ++x)
{
CVec2 objPos = mapPos * 2 + CVec2(x, y);
CObjectStack* stack = map.findObjectsStack(objPos, eWallSideMax);
if (stack != NULL)
{
CObjectStack copy = *stack;
copy.mPos = CVec2(x, y);
copyStacks.push_back(copy);
map.removeObjectsStack(objPos, eWallSideMax);
}
}
Then if the teleporter leads to another level we load it - after backuping some datas.
int lastLevel = currentLevel;
CVec2 lastPos = player.pos;
uint8_t lastDir = player.dir;
int level = tile->getIntParam("Level");
CVec2 newPos = CVec2(tile->getIntParam("X"), tile->getIntParam("Y"));
// load destination level
if (level != currentLevel)
loadLevel(level, CVec2(), 0);
After that we put back all of the objects in the copied stack to the destination position.
// set the stacks at the new pos
for (size_t i = 0; i < copyStacks.size(); ++i)
{
CVec2 objPos = newPos * 2 + copyStacks[i].mPos;
CObjectStack* stack = map.addObjectsStack(objPos, eWallSideMax);
for (size_t j = 0; j < copyStacks[i].getSize(); ++j)
stack->addObject(copyStacks[i].getObject(j));
}
And finally, if we changed level, we go back to the starting one.
// reload the last level
if (level != lastLevel)
loadLevel(lastLevel, lastPos, lastDir);
}
Bugs fixed in the editor