Partie 43: Vie et mort des champions
Téléchargements
Dégâts et mort
void CCharacter::hitChampion(int num, int damages)
{
// déjà mort ou vide ?
if (portrait == -1 || isDead() == true)
return;
displayedDamages = damages;
damagesTimer.set(90, false);
stats[eStatHealth].value -= damages;
static CVec2 championPos[4][4] =
{
{{0, 0}, {1, 0}, {0, 1}, {1, 1}}, // up
{{0, 1}, {0, 0}, {1, 1}, {1, 0}}, // left
{{1, 1}, {0, 1}, {1, 0}, {0, 0}}, // down
{{1, 0}, {1, 1}, {0, 0}, {0, 1}} // right
};
// mort ?
if (stats[eStatHealth].value <= 0)
{
stats[eStatHealth].value = 0;
// jette tous les objet sur le sol
CVec2 stackPos = player.pos * 2 + championPos[player.dir][num];
CObjectStack* stack = map.addObjectsStack(stackPos, eWallSideMax);
for (int i = 0; i < eBodyPartCount; ++i)
{
CObject& obj = bodyObjects[i];
if (obj.getType() != 0)
{
stack->addObject(obj);
obj.setType(0);
}
}
// jette les os du champion
CObject bones;
bones.setType(OBJECT_TYPE_BONES);
bones.setIntParam("Champion", num);
stack->addObject(bones);
}
}
On soustrait les dégâts à la santé du champion.
<!-- OS DE # -->
<item name="ITEM149">
[...]
<param type="int">Champion</param>
</item>
Parce qu'on doit connaître le nom du champion pour afficher le nom des os quand on les ramasse.
void CInterface::drawObjectName(QImage* image)
{
int type = mouse.mObjectInHand.getType();
if (type != 0)
{
std::string name;
if (type == OBJECT_TYPE_BONES)
name = setChampionNameInString(mouse.mObjectInHand.getIntParam("Champion"), "ITEM149");
else
name = objects.mObjectInfos[type].name;
drawText(image, CVec2(233, 33), eFontStandard, name.c_str(), MAIN_INTERFACE_COLOR);
}
}
Se cogner contre les murs
void CPlayer::moveIfPossible(EWallSide side)
{
[...]
if (canMove == true)
{
[...]
}
else
{
if (game.characters[0].portrait != -1)
{
static const int championsHit[eWallSideMax][2] =
{
{0, 1}, // eWallSideUp
{2, 3}, // eWallSideDown
{0, 2}, // eWallSideLeft
{1, 3} // eWallSideRight
};
for (int i = 0; i < 2; ++i)
{
int champNum = championsHit[invWallSide(side)][i];
game.characters[champNum].hitChampion(champNum, (RANDOM(4) == 0 ? 2 : 1));
}
sound->play(newPos, "sound/Party_Damaged.wav");
}
}
}
J'aurais probablement dû utiliser la fonction getChampionRow() de la partie précédente ici et prendre en compte le
Tomber dans un trou
void CPlayer::fall()
{
[...]
hitAllChampions(20);
}
//-----------------------------------------------------------------------------------------
void CPlayer::hitAllChampions(int damages)
{
int rnd = damages / 8 + 1;
sound->play(pos, "sound/Party_Damaged.wav");
for (int i = 0; i < 4; ++i)
{
int dmg = damages - rnd + RANDOM(rnd * 2);
dmg = MAX(1, dmg);
game.characters[i].hitChampion(i, dmg);
}
}
L'autel Vi
void CGame::update(SMouseArea* clickedArea)
{
[...]
else if (clickedArea->type == eMouseAreaG_DropObject)
{
// récupère la position et le coté
[...]
// jette l'objet
CObjectStack* stack = map.addObjectsStack(pos, side);
stack->addObject(mouse.mObjectInHand);
// test si on ressuscite un champion
if (mouse.mObjectInHand.getType() == OBJECT_TYPE_BONES &&
side != eWallSideMax)
{
CWall& wall = map.getTile(pos)->mWalls[side];
if (wall.getType() == eWallAlcove &&
wall.getEnumParam("Type") == 2)
{
// ressuscite
sound->play(pos, "sound/Spell.wav");
walls.resurrectWallPos = pos;
walls.resurrectWallSide = side;
walls.resurrectTimer.set(120, true);
}
}
Le timer est utilisé pour jouer une animation d'éclair pendant 2 secondes.
CWalls::COrnateData alcoveLightning =
{
"",
"gfx/3DView/missiles/Lightning_side.png",
"",
{82, 95},
{0, 0}
};
//---------------------------------------------------------------------------------------------
CRect CWalls::drawOrnate(QImage* image, CVec2 tablePos, EWallSide side, int ornate, int textLines)
{
if (ornate != 0)
{
int tableSide = walls.getTableSide(side, false);
if (gWallsInfos[tablePos.y][tablePos.x][tableSide].size.x != 0)
{
COrnateData* ornateData = NULL;
if (ornate == ORNATE_ALCOVE_LIGHTNING)
ornateData = &alcoveLightning;
else
ornateData = &walls.ornatesDatas[ornate];
[...]
// dessine les décors à l'écran.
bool flipX = false;
bool flipY = false;
if (ornate == ORNATE_ALCOVE_LIGHTNING)
{
int image = walls.resurrectTimer.get() / 30;
flipX = image & 1;
flipY = (image >> 1) & 1;
}
graph2D.drawImage(image, ornatePos, tempImage, 0, flipX, QRect(0, 33, MAINVIEW_WIDTH, MAINVIEW_HEIGHT), flipY);
Explosion
void CExplosions::add(EExplosions type, CVec2 mapPos, int duration)
{
SExplosion explo;
explo.type = type;
explo.mapPos = mapPos;
explo.timer.set(duration, true);
mExploList.push_back(explo);
if (type == eExplo_Resurrect)
sound->play(mapPos, "sound/Explosion.wav");
}
Une fonction update() gère les timers de toutes les explosions et les retire de la liste quand elles sont
void CExplosions::update()
{
std::vector<SExplosion>::iterator it = mExploList.begin();
while(it != mExploList.end())
{
if (it->timer.update() == true)
it = mExploList.erase(it);
else
it++;
}
}
La fonction draw() est une version simplifiée de CWalls::drawOrnate()
void CExplosions::draw(QImage* image, CVec2 mapPos, CVec2 tablePos)
{
for (size_t i = 0; i < mExploList.size(); ++i)
{
SExplosion& explo = mExploList[i];
if (explo.mapPos == mapPos)
{
QImage exploGfx;
CVec2 pos;
float scale = 1.0;
if (explo.type == eExplo_Resurrect)
{
exploGfx = fileCache.getImage("gfx/3DView/explosions/Fireball.png");
pos = CVec2(57, 66);
scale = 110.0 / 145.0;
}
const SWallInfos* tabData = &gWallsInfos[tablePos.y][tablePos.x][eWallSideUp];
if (tabData->size.x != 0)
{
const SWallInfos* refWall = NULL;
refWall = &gWallsInfos[3][2][eWallSideUp];
// calcule la position et la taille et dessine l'explosion dans une image temporaire
scale *= (float)tabData->size.y / 111.0f;
QSize scaledSize = exploGfx.size() * scale;
pos = pos - refWall->pos;
pos.x = pos.x * tabData->size.x / refWall->size.x;
pos.y = pos.y * tabData->size.y / refWall->size.y;
pos += tabData->pos;
QImage tempImage(scaledSize, QImage::Format_ARGB32);
tempImage.fill(TRANSPARENT);
graph2D.drawImageScaled(&tempImage, CVec2(), exploGfx, scale);
// assombrit l'explosion en fonction de sa distance
static const float shadowLevels[] = {0.0f, 0.2f, 0.4f};
float shadow = shadowLevels[WALL_TABLE_HEIGHT - 1 - tablePos.y];
graph2D.darken(&tempImage, shadow);
// dessine l'explosion à l'écran.
graph2D.drawImage(image, pos, tempImage, 0, false, QRect(0, 33, MAINVIEW_WIDTH, MAINVIEW_HEIGHT));
}
}
}
}
Quand on crée cette explosion, le champion est aussi ressuscité et récupère la moitié de sa santé de départ.
void CWalls::update()
{
// animation de l'autel vi
if (resurrectTimer.update() == true)
{
// retrouve le numéro du champion
CObjectStack* stack = map.findObjectsStack(resurrectWallPos, resurrectWallSide);
int last = stack->getSize() - 1;
CObject& bones = stack->getObject(last);
int champNum = bones.getIntParam("Champion");
// efface l'objet
stack->removeObject(last);
resurrectWallSide = eWallSideMax;
// ressuscite le champion
CCharacter::SStat& health = game.characters[champNum].stats[CCharacter::eStatHealth];
health.value = health.startValue / 2;
// lance l'explosion
explosions.add(eExplo_Resurrect, player.pos, 20);
}
}
Je n'ai pas regardé le code du jeu d'origine pour voir si d'autres stats du champion changeaient.
Régénération de vie
#define HEALTH_REGEN_TIMER (30 * 60)
#define HEALTH_REGEN_TIMER_SLEEP 90