Partie 37: Les attaques Partie 1
Téléchargements
Les attaques
<text id="ATTACK01">FENDRE</text>
<text id="ATTACK02">ENTAILLER</text>
<text id="ATTACK03">SONNER FORT</text>
<text id="ATTACK04">PILE OU FACE</text>
<text id="ATTACK05">COGNER</text>
<text id="ATTACK06">COUP DE PIED</text>
<text id="ATTACK07">AMORT</text>
<text id="ATTACK08">POIGNARDER</text>
<text id="ATTACK09">DESCENDRE</text>
<text id="ATTACK10">CONGELER</text>
<text id="ATTACK11">FRAPPER</text>
<text id="ATTACK12">TAILLADER</text>
<text id="ATTACK13">PLONGER</text>
<text id="ATTACK14">PIQUER</text>
<text id="ATTACK15">PARER</text>
<text id="ATTACK16">ABATTRE</text>
<text id="ATTACK17">SE RUER SUR</text>
<text id="ATTACK18">BOULE DE FEU</text>
<text id="ATTACK19">CONJURER</text>
<text id="ATTACK20">SUBJUGUER</text>
<text id="ATTACK21">ECLAIR</text>
<text id="ATTACK22">ENSORCELER</text>
<text id="ATTACK23">MELEE</text>
<text id="ATTACK24">INVOQUER</text>
<text id="ATTACK25">TAILLADER</text>
<text id="ATTACK26">POURFENDRE</text>
<text id="ATTACK27">ASSOMMER</text>
<text id="ATTACK28">ETOURDIR</text>
<text id="ATTACK29">TIRER</text>
<text id="ATTACK30">AURA MAGIQUE</text>
<text id="ATTACK31">PARE-FEU</text>
<text id="ATTACK32">CAGE</text>
<text id="ATTACK33">GUERIR</text>
<text id="ATTACK34">TRANQUILISER</text>
<text id="ATTACK35">LUMIERE</text>
<text id="ATTACK36">FENETRE</text>
<text id="ATTACK37">CRACHER</text>
<text id="ATTACK38">BRANDIR</text>
<text id="ATTACK39">LANCER</text>
<text id="ATTACK40">FUSIONNER</text>
J'ai créé une nouvelle base de données "attacks.xml" qui contient quelques paramètres pour chacune d'entre-elles,
Combos
<?xml version="1.0" encoding="utf-8"?>
<combos>
<!-- Combo 0 (inutilise) -->
<combo>
<attack type="0">
</attack>
<attack type="0">
</attack>
<attack type="0">
</attack>
</combo>
<!-- Combo 1 Invoquer/Fusionner/Cage -->
<combo>
<attack type="24">
</attack>
<attack type="40">
</attack>
<attack type="32">
</attack>
</combo>
<!-- Combo 2 Cogner/Coup de Pied/Amort -->
<combo>
<attack type="5">
</attack>
<attack type="6">
</attack>
<attack type="7">
</attack>
</combo>
[...]
</combos>
Chaque combo contient 3 attaques qui ont seulement un type. A l'avenir ces attaques contiendront plus de paramètres.
class CAttacks
{
public:
[...]
struct SComboAttack
{
uint8_t type;
};
struct SCombo
{
SComboAttack attacks[3];
};
[...]
void readCombosDB();
[...]
SCombo& getCombo(int num);
private:
[...]
std::vector<SCombo> mCombos;
};
Lier les combos aux armes
<?xml version="1.0" encoding="utf-8"?>
<items>
<!-- MAIN NUE -->
<item name="ITEM000">
<category>Weapon</category>
<image_file>Items6.png</image_file>
<image_num>9</image_num>
<weight>0</weight>
<combo>2</combo>
</item>
<!-- ======================== Armes ======================== -->
<!-- OEIL DU TEMPS -->
<item name="ITEM001">
<category>Weapon</category>
<floor_image>00W_Ring.png</floor_image>
<image_file>Items0.png</image_file>
<image_num>17</image_num>
<position>hBCP</position>
<weight>1</weight>
<combo>43</combo>
</item>
<!-- ROND D'ECLAIRS -->
<item name="ITEM002">
<category>Weapon</category>
<floor_image>00W_Ring.png</floor_image>
<image_file>Items0.png</image_file>
<image_num>19</image_num>
<position>hBCP</position>
<weight>1</weight>
<combo>7</combo>
</item>
Vous pouvez voir que j'ai ajouté un objet "main nue" parce que quand il n'y a pas d'arme dans la main du champion,
CObjects::CObjectInfo& infos = objects.mObjectInfos[objType - 1];
Ca prêtait à confusion et ça pouvait créer des bugs. Alors maintenant, on n'a plus besoin du "-1".
void CInterface::drawWeapon(QImage* image, int num)
{
if (isWeaponEmpty(num) == false)
{
// dessine le rectangle de fond
CRect rect(CVec2(233 + 22 * num, 86),
CVec2(252 + 22 * num, 120));
graph2D.rect(image, rect, MAIN_INTERFACE_COLOR, true);
// dessine l'arme
CVec2 pos = CVec2(235 + 22 * num, 96);
CObject& weapon = getWeapon(num);
int weaponType = weapon.getType();
CObjects::CObjectInfo weaponInfo = objects.mObjectInfos[weaponType];
if (weaponInfo.combo != 0)
{
std::string imageFile;
int imageNum;
CObjects::CObjectInfo object = objects.mObjectInfos[weaponType];
imageFile = object.imageFile.toLocal8Bit().constData();
imageNum = object.imageNum;
QImage objImage = fileCache.getImage(imageFile);
CRect objRect = getItemRect(imageNum);
graph2D.drawImageAtlas(image, pos, objImage, objRect, BLACK);
}
// griser ou zone souris
if (isWeaponGreyed(num) == true)
graph2D.patternRectangle(image, rect);
else if (weaponInfo.combo != 0)
mouse.addArea(eMouseArea_Weapon, rect, eCursor_Arrow, (void*)num);
}
}
Bien entendu on utilise aussi les données de la combo pour trouver combien d'attaques a une arme et afficher leurs
void CInterface::drawAttacks(QImage* image, int num)
{
CObject& weapon = getWeapon(num);
int weaponType = weapon.getType();
CObjects::CObjectInfo weaponInfo = objects.mObjectInfos[weaponType];
CAttacks::SCombo& combo = attacks.getCombo(weaponInfo.combo);
// dessine l'image de fond
QImage attacksBg = fileCache.getImage("gfx/interface/WeaponsActions.png");
CVec2 pos(233, 77);
graph2D.drawImage(image, pos, attacksBg);
// cache les slots inutiles
for (int i = 0; i < 3; ++i)
{
if (combo.attacks[i].type == 0)
{
CRect rect(CVec2(233, 86 + i * 12),
CVec2(319, 97 + i * 12));
graph2D.rect(image, rect, BLACK, true);
}
}
// écrit le nom du personnage
CCharacter* c = &game.characters[num];
drawText(image, CVec2(235, 79), eFontStandard, c->firstName.c_str(), BLACK);
// écrit le nom des attaques
for (int i = 0; i < 3; ++i)
{
if (combo.attacks[i].type != 0)
{
static char attackName[16];
sprintf(attackName, "ATTACK%02d", combo.attacks[i].type);
drawText(image, CVec2(241, 89 + i * 12), eFontStandard, getText(attackName).c_str(), MAIN_INTERFACE_COLOR);
if (attackHighlightTime.isRunning() == true && currentAttack == i)
{
CRect rect(CVec2(234, 86 + i * 12),
CVec2(318, 96 + i * 12));
graph2D.Xor(image, rect, MAIN_INTERFACE_COLOR);
}
}
}
// zones souris
if (isWeaponGreyed(num) == false && attackHighlightTime.isRunning() == false)
{
for (int i = 0; i < 3; ++i)
{
if (combo.attacks[i].type != 0)
{
CRect rect(CVec2(234, 86 + i * 12),
CVec2(318, 96 + i * 12));
mouse.addArea(eMouseArea_Attack, rect, eCursor_Arrow, (void*)i);
}
}
CRect rect(CVec2(290, 77), CVec2(314, 83));
mouse.addArea(eMouseArea_CloseAttack, rect, eCursor_Arrow);
}
}
Toutes les attaques d'une arme ne seront pas disponibles dès le départ. Certaines nécessiteront que le personnage
La base de données des attaques
<?xml version="1.0" encoding="utf-8"?>
<attacks>
<!-- Rien -->
<attack>
<cooldown>0</cooldown>
<hitprob>0</hitprob>
</attack>
<!-- FENDRE -->
<attack>
<cooldown>60</cooldown>
<hitprob>22</hitprob>
</attack>
<!-- ENTAILLER -->
<attack>
<cooldown>80</cooldown>
<hitprob>48</hitprob>
</attack>
Pour le moment il contient seulement le temps de cooldown en nombre de frames et la probabilité de toucher avec
void CInterface::updateWeapons()
{
// surbrillance de l'attaque
if (attackHighlightTime.update() == true)
{
CObject& weapon = getWeapon(currentWeapon);
int weaponType = weapon.getType();
CObjects::CObjectInfo weaponInfo = objects.mObjectInfos[weaponType];
CAttacks::SCombo& combo = attacks.getCombo(weaponInfo.combo);
int attackType = combo.attacks[currentAttack].type;
CAttacks::SAttack& attack = attacks.getAttack(attackType);
weaponCoolDown[currentWeapon].set(attack.cooldown, true);
if (((rand() % 75) + 1) <= attack.hitProbability)
{
damages = rand() % 200 + 1;
damagesDisplayTime.set(DAMAGES_DISPLAY_TIME, false);
weaponsAreaState = eWeaponsAreaDamage;
}
else
{
weaponsAreaState = eWeaponsAreaWeapons;
}
}
// timer des dégats
if (damagesDisplayTime.update() == true)
weaponsAreaState = eWeaponsAreaWeapons;
// cooldowns des armes
for (int i = 0; i < 4; ++i)
weaponCoolDown[i].update();
}
Ce n'est pas la formule exacte utilisée dans le jeu d'origine, car la probabilité de toucher va varier avec les