Partie 26: Alcoves et porte-torches
Téléchargements
Objets sur les murs
class CObjectStack
{
[...]
CVec2 mPos;
EWallSide mSide;
[...]
};
On utilisera cette convention:
CObjectStack* addObjectsStack(CVec2 pos, EWallSide side);
CObjectStack* findObjectsStack(CVec2 pos, EWallSide side);
void removeObjectsStack(CVec2 pos, EWallSide side);
Ce n'est pas un gros travail parce que la plupart de ces fonctions dépendent de findObjectsStackIndex() et c'est
int CMap::findObjectsStackIndex(CVec2 pos, EWallSide side)
{
for (size_t i = 0; i < mObjectsStacks.size(); ++i)
if (mObjectsStacks[i].mPos == pos && mObjectsStacks[i].mSide == side)
return i;
return -1;
}
Et naturellement, le format du fichier map va changer un peu pour stocker ce coté et les nouveaux types de murs
Le mur à alcove
<wall name="Alcove">
<image>Alcove.png</image>
<param type="enum" values="Arrondie;Carree;Autel Vi">Type</param>
<param type="stack"/>
</wall>
Il y 3 types d'alcove dans le jeu:
<ornate name="Alcove_Arrondie">
<image_front>Alcove_Arched_front.png</image_front>
<image_side>Alcove_Arched_side.png</image_side>
<pos_front x="64" y="69"/>
<pos_side x="34" y="69"/>
</ornate>
<ornate name="Alcove_Carree">
<image_front>Alcove_Square_front.png</image_front>
<image_side>Alcove_Square_side.png</image_side>
<pos_front x="67" y="69"/>
<pos_side x="38" y="70"/>
</ornate>
<ornate name="Alcove_Vi">
<image_front>Alcove_Vi_front.png</image_front>
<image_side>Alcove_Vi_side.png</image_side>
<pos_front x="64" y="69"/>
<pos_side x="36" y="70"/>
</ornate>
Le 2ième paramètre de ce type de mur ("stack") est une sorte de "faux" paramètre.
Les alcoves dans l'éditeur
void CEditor::addWallParamsItems(uint8_t type)
{
[...]
std::vector<CParamType>& sourceList = map.mWallsParams[type];
for (size_t i = 0; i < sourceList.size(); ++i)
{
[...]
else if (sourceList[i].mType == eParamEnum)
{
CParamEnum* par = (CParamEnum*)param;
addLabel(wallSelGrid, sourceList[i].mName + ":", &mWallParamItems);
addComboBox(wallSelGrid, sourceList[i].mValues, &mWallParamItems, i, par->mValue);
}
else if (sourceList[i].mType == eParamStack)
{
CObjectStack* stack = map.findObjectsStack(pos, side);
size_t size = 0;
if (stack != NULL)
{
for (size_t i = 0; i < stack->getSize(); ++i)
{
addButton(wallSelGrid, "effacer", &mWallParamItems, i);
addComboBox(wallSelGrid, bridge.itemsList, &mWallParamItems, sourceList.size() - 1 + i, stack->getObject(i).getType());
}
size = stack->getSize();
}
addButton(wallSelGrid, "ajouter", &mWallParamItems, size);
}
}
}
Dans CBridge::setSelParamCombo() on gère les changements des combo box.
void CBridge::setSelParamCombo(qint32 id, qint32 value)
{
[...]
else if (mTabIndex == eTabWalls)
{
CVec2 pos = editor.mSelectStart / TILE_SIZE;
EWallSide side = editor.getWallSideAbs(editor.mSelectStart);
CWall* wall = &map.getTile(pos)->mWalls[side];
bool isStack = false;
if ((size_t)id >= wall->mParams.size())
isStack = true;
else if (wall->mParams[id]->mType == eParamStack)
isStack = true;
if (isStack == true)
{
CObjectStack* stack = map.findObjectsStack(pos, side);
if (stack != NULL)
{
CObject& object = stack->getObject(id - (wall->mParams.size() - 1));
object.setType(value);
}
}
else
{
CParam* param = wall->mParams[id];
switch (param->mType)
{
SET_COMBO(eParamOrnate, CParamOrnate)
SET_COMBO(eParamEnum, CParamEnum)
default:
break;
}
}
}
[...]
}
Et dans CBridge::selButtonClicked() on s'occupe des boutons "ajouter" et "effacer":
void CBridge::selButtonClicked(qint32 id)
{
if (id == -1)
return;
if (mTabIndex == eTabObjects)
{
[...]
}
else if (mTabIndex == eTabWalls)
{
CVec2 pos = editor.mSelectStart / TILE_SIZE;
EWallSide side = editor.getWallSideAbs(editor.mSelectStart);
CObjectStack* stack = map.findObjectsStack(pos, side);
if (stack == NULL || (size_t)id == stack->getSize())
{
// ajoute un nouvel objet
if (stack == NULL)
stack = map.addObjectsStack(pos, side);
CObject object;
stack->addObject(object);
}
else
{
// retire un objet
stack->removeObject(id);
if (stack->getSize() == 0)
map.removeObjectsStack(pos, side);
}
// met à jour les boutons
QMetaObject::invokeMethod(this, "updateSelStack", Qt::QueuedConnection);
}
}
Il y a aussi beaucoup de changements dans "clipboard.cpp" et "tools.cpp" pour ajouter les tas des murs aux outils
Les alcoves dans le jeu
else if (wall->getType() == eWallAlcove)
{
//------------------------------------------------------------------------------
// alcove
int type = wall->getEnumParam("Type");
walls.drawOrnate(image, tablePos, side, ORNATE_ARCHED_ALCOVE + type);
if (tablePos.y > 0 && side == eWallSideUp)
{
objects.drawObjectsStack(image, mapPos, playerSide, tablePos);
if (tablePos == CVec2(2, 3) && mouse.mObjectInHand.getType() != 0)
{
CRect rectLeft(CVec2(66, 84), CVec2(157, 124));
mouse.addArea(eMouseAreaG_DropObject, rectLeft, eCursor_Hand, (void*)mapPos.x, (void*)mapPos.y, (void*)playerSide);
}
}
}
Notez que les objets sont affichés seulement quand le mur est face à nous.
void CObjects::drawObjectsStack(QImage* image, CVec2 mapPos, EWallSide side, CVec2 tablePos)
{
[...]
CObjectStack* stack = map.findObjectsStack(mapPos, side);
if (stack != NULL)
{
for (size_t i = 0; i < stack->getSize(); ++i)
{
int type = stack->getObject(i).getType();
if (type != 0)
{
// récupère l'image de l'objet
[...]
// récupère la position de l'objet et ajoute une valeur pseudo-aléatoire
CVec2 pos;
if (side == eWallSideMax)
pos = getObjectPos(tablePos);
else
pos = getAlcovePos(tablePos);
[...]
// calcule la taille par rapport à la position de référence (la plus proche)
CVec2 pos0, pos1;
float scale;
if (side == eWallSideMax)
{
pos0 = getObjectPos(CVec2(WALL_TABLE_WIDTH, (WALL_TABLE_HEIGHT - 1) * 2));
pos1 = getObjectPos(CVec2(WALL_TABLE_WIDTH, tablePos.y));
scale = (float)(pos1.x - 112) / (float)(pos0.x - 112);
}
else
{
static float scalesTable[] = {0.36, 0.53, 0.82};
scale = scalesTable[tablePos.y - 1];
}
// redimensionne l'objet dans une image temporaire
[...]
// assombrit l'objet en fonction de sa distance
float shadow;
if (side == eWallSideMax)
shadow = ((WALL_TABLE_HEIGHT - 1) * 2 - tablePos.y) * 0.13f;
else
shadow = ((WALL_TABLE_HEIGHT - 1) * 2 - tablePos.y * 2) * 0.13f;
graph2D.darken(&scaledObject, shadow);
// dessine l'objet à l'écran
[...]
// ajoute la zone souris
if (mouse.mObjectInHand.getType() == 0)
{
bool isAreaOK = false;
if (side == eWallSideMax)
{
if (tablePos.x >= WALL_TABLE_WIDTH - 1 && tablePos.x <= WALL_TABLE_WIDTH)
{
if (tablePos.y == (WALL_TABLE_HEIGHT - 1) * 2 ||
(tablePos.y == (WALL_TABLE_HEIGHT - 2) * 2 + 1 && isWallInFront() == false))
{
isAreaOK = true;
}
}
}
else
{
if (tablePos.x == WALL_TABLE_WIDTH / 2 && tablePos.y == WALL_TABLE_HEIGHT - 1)
isAreaOK = true;
}
if (isAreaOK)
{
CRect mouseRect(pos, CVec2(pos.x + scaledObject.width() - 1, pos.y + scaledObject.height() - 1));
mouse.addArea(eMouseAreaG_PickObject, mouseRect, eCursor_Hand, (void*)stack, (void*)i);
}
}
}
}
}
}
La position de l'objet à l'écran est calculée par une nouvelle fonction:
CVec2 CObjects::getAlcovePos(CVec2 tablePos)
{
CVec2 pos;
pos.x = 250 * (tablePos.x * 2 - 4) / (8.5 - tablePos.y * 2) + 112;
static int yTable[] = {91, 104, 120};
pos.y = yTable[tablePos.y - 1];
return pos;
}
Comme vous pouvez le voir, pour les zones souris, j'ai utilisé les mêmes identifiants que pour les tas au sol
Les porte-torches dans l'éditeur
<wall name="Torche">
<image>Torch.png</image>
<param type="bool">estVide</param>
</wall>
...et comment ça va apparaitre dans l'éditeur:
Les porte-torches dans le jeu
void CWalls::init()
{
// crée les tas d'objets pour les porte-torches
for (int y = 0; y < map.mSize.y; ++y)
for (int x = 0; x < map.mSize.x; ++x)
{
CVec2 pos(x, y);
CTile* t = map.getTile(pos);
for (int side = 0; side < eWallSideMax; ++side)
{
CWall* w = &t->mWalls[side];
if (w->getType() == eWallTorch)
{
bool isEmpty = w->getBoolParam("estVide");
map.removeObjectsStack(pos, (EWallSide)side);
if (isEmpty == false)
{
CObjectStack* stack = map.addObjectsStack(pos, (EWallSide)side);
CObject object;
object.setType(3);
stack->addObject(object);
}
}
}
}
}
On parcourt simplement tous les "murs torche" dans la map, et on crée un tas si leur flag "estVide" est faux.
else if (wall->getType() == eWallTorch)
{
//------------------------------------------------------------------------------
// porte-torche
CObjectStack* stack = map.findObjectsStack(mapPos, playerSide);
CRect rect = walls.drawOrnate(image, tablePos, side, (stack != NULL ? ORNATE_TORCH_FULL : ORNATE_TORCH_EMPTY));
if (tablePos == CVec2(2, 3) && side == eWallSideUp)
mouse.addArea(eMouseAreaG_WallTorch, rect, eCursor_Hand, (void*)mapPos.x, (void*)mapPos.y, (void*)playerSide);
}
Remarquez que j'ai modifié drawOrnate pour qu'elle renvoie le rectangle du décor qu'elle dessine. De cette façon,
else if (clickedArea->type == eMouseAreaG_WallTorch)
{
CVec2 pos;
EWallSide side;
pos.x = (int)clickedArea->param1;
pos.y = (int)clickedArea->param2;
side = (EWallSide)((int)clickedArea->param3);
int type = mouse.mObjectInHand.getType();
CObjectStack* stack = map.findObjectsStack(pos, side);
if (type == 0 && stack != NULL)
{
mouse.mObjectInHand = stack->getObject(0);
map.removeObjectsStack(pos, side);
}
else if (type == 3 && stack == NULL)
{
stack = map.addObjectsStack(pos, side);
stack->addObject(mouse.mObjectInHand);
mouse.mObjectInHand.setType(0);
}
characters[interface.selectedChampion].updateLoad();
}
Derniers mots