1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

* added support for archery, offence and armorer secondary abilities

* hero's primary skills account for damage dealt by creatures in battle
* magical hero are given spellbook at the beginning
* added initial secondary skills for heroes
* minor fixes and improvements
This commit is contained in:
mateuszb 2008-09-29 11:03:30 +00:00
parent 917be0693d
commit 538ffb1579
13 changed files with 290 additions and 29 deletions

View File

@ -56,13 +56,18 @@ CGObjectInstance * createObject(int id, int subid, int3 pos, int owner)
nobj->defInfo->blockMap[5] = 253;
nobj->defInfo->visitMap[5] = 2;
nobj->artifWorn[16] = 3;
if(nobj->type->heroType % 2 == 1) //it's a magical hero
{
nobj->artifWorn[17] = 0; //give him spellbook
}
nobj->portrait = subid;
nobj->primSkills.resize(4);
nobj->primSkills[0] = nobj->type->heroClass->initialAttack;
nobj->primSkills[1] = nobj->type->heroClass->initialDefence;
nobj->primSkills[2] = nobj->type->heroClass->initialPower;
nobj->primSkills[3] = nobj->type->heroClass->initialKnowledge;
nobj->mana = 10 * nobj->primSkills[3];
nobj->secSkills = nobj->type->secSkillsInit; //copying initial secondary skills
nobj->mana = 10 * nobj->getPrimSkillLevel(3);
return nobj;
}
case 98: //town
@ -1000,7 +1005,10 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
vhi->exp=40+ran()%50;
vhi->level = 1;
}
if (vhi->level>1) ;//TODO dodac um dr, ale potrzebne los
if(vhi->secSkills.size() == 1 && vhi->secSkills[0] == std::make_pair(-1, -1)) //set secondary skills to default
{
vhi->secSkills = vhi->type->secSkillsInit;
}
if ((!vhi->primSkills.size()) || (vhi->primSkills[0]<0))
{
if (vhi->primSkills.size()<PRIMARY_SKILLS)
@ -1010,7 +1018,7 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
vhi->primSkills[2] = vhi->type->heroClass->initialPower;
vhi->primSkills[3] = vhi->type->heroClass->initialKnowledge;
}
vhi->mana = vhi->primSkills[3]*10;
vhi->mana = vhi->getPrimSkillLevel(3)*10;
if (!vhi->name.length())
{
vhi->name = vhi->type->name;
@ -1023,6 +1031,10 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
vhi->portrait = vhi->type->ID;
vhi->artifWorn[16] = 3;
if(vhi->type->heroType % 2 == 1) //it's a magical hero
{
vhi->artifWorn[17] = 0; //give him spellbook
}
//initial army
if (!vhi->army.slots.size()) //standard army
@ -1050,6 +1062,7 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
vhi->army.slots[x-pom2].second = (ran()%pom)+vhi->type->lowStack[x];
else
vhi->army.slots[x-pom2].second = +vhi->type->lowStack[x];
vhi->army.formation = false;
}
}
@ -1347,9 +1360,9 @@ std::set<int3> CGameState::tilesToReveal(int3 pos, int radious, int player)
return ret;
}
int BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender)
int BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting)
{
int attackDefenseBonus = attacker->creature->attack - defender->creature->defence;
int attackDefenseBonus = attacker->creature->attack + (attackerHero ? attackerHero->getPrimSkillLevel(0) : 0) - (defender->creature->defence + (defendingHero ? defendingHero->getPrimSkillLevel(1) : 0));
int damageBase = 0;
if(attacker->creature->damageMax == attacker->creature->damageMin) //constant damage
{
@ -1360,7 +1373,7 @@ int BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender)
damageBase = rand()%(attacker->creature->damageMax - attacker->creature->damageMin) + attacker->creature->damageMin + 1;
}
float dmgBonusMultiplier = 1.0;
float dmgBonusMultiplier = 1.0f;
if(attackDefenseBonus < 0) //decreasing dmg
{
if(0.02f * (-attackDefenseBonus) > 0.3f)
@ -1383,6 +1396,55 @@ int BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender)
dmgBonusMultiplier += 0.05f * attackDefenseBonus;
}
}
//handling secondary abilities
if(attackerHero)
{
if(shooting)
{
switch(attackerHero->getSecSkillLevel(1)) //archery
{
case 1: //basic
dmgBonusMultiplier *= 1.1f;
break;
case 2: //advanced
dmgBonusMultiplier *= 1.25f;
break;
case 3: //expert
dmgBonusMultiplier *= 1.5f;
break;
}
}
else
{
switch(attackerHero->getSecSkillLevel(22)) //offence
{
case 1: //basic
dmgBonusMultiplier *= 1.1f;
break;
case 2: //advanced
dmgBonusMultiplier *= 1.2f;
break;
case 3: //expert
dmgBonusMultiplier *= 1.3f;
break;
}
}
}
if(defendingHero)
{
switch(defendingHero->getSecSkillLevel(23)) //armourer
{
case 1: //basic
dmgBonusMultiplier *= 0.95f;
break;
case 2: //advanced
dmgBonusMultiplier *= 0.9f;
break;
case 3: //expert
dmgBonusMultiplier *= 0.85f;
break;
}
}
return (float)damageBase * (float)attacker->amount * dmgBonusMultiplier;
}

View File

@ -73,7 +73,7 @@ struct DLL_EXPORT BattleInfo
static signed char mutualPosition(int hex1, int hex2); //returns info about mutual position of given hexes (-1 - they're distant, 0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left)
static std::vector<int> neighbouringTiles(int hex);
static int calculateDmg(const CStack* attacker, const CStack* defender); //TODO: add additional conditions and require necessary data
static int calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting); //TODO: add additional conditions and require necessary data
void calculateCasualties(std::set<std::pair<ui32,si32> > *casualties);
};

View File

@ -220,7 +220,7 @@ void CHeroWindow::setHero(const CGHeroInstance *Hero)
for(int g=0; g<primSkillAreas.size(); ++g)
{
primSkillAreas[g]->bonus = hero->primSkills[g];
primSkillAreas[g]->bonus = hero->getPrimSkillLevel(g);
}
for(int g=0; g<hero->secSkills.size(); ++g)
{
@ -236,7 +236,7 @@ void CHeroWindow::setHero(const CGHeroInstance *Hero)
sprintf(bufor, CGI->generaltexth->allTexts[2].substr(1, CGI->generaltexth->allTexts[2].size()-2).c_str(), hero->level, CGI->heroh->reqExp(hero->level+1), hero->exp);
expArea->text = std::string(bufor);
sprintf(bufor, CGI->generaltexth->allTexts[205].substr(1, CGI->generaltexth->allTexts[205].size()-2).c_str(), hero->name.c_str(), hero->mana, hero->primSkills[3]*10);
sprintf(bufor, CGI->generaltexth->allTexts[205].substr(1, CGI->generaltexth->allTexts[205].size()-2).c_str(), hero->name.c_str(), hero->mana, hero->getPrimSkillLevel(3)*10);
spellPointsArea->text = std::string(bufor);
for(int g=0; g<artWorn.size(); ++g)
@ -551,19 +551,19 @@ void CHeroWindow::redrawCurBack()
//printing primary skills' amounts
std::stringstream primarySkill1;
primarySkill1<<curHero->primSkills[0];
primarySkill1<<curHero->getPrimSkillLevel(0);
CSDL_Ext::printAtMiddle(primarySkill1.str(), 53, 165, TNRB16, zwykly, curBack);
std::stringstream primarySkill2;
primarySkill2<<curHero->primSkills[1];
primarySkill2<<curHero->getPrimSkillLevel(1);
CSDL_Ext::printAtMiddle(primarySkill2.str(), 123, 165, TNRB16, zwykly, curBack);
std::stringstream primarySkill3;
primarySkill3<<curHero->primSkills[2];
primarySkill3<<curHero->getPrimSkillLevel(2);
CSDL_Ext::printAtMiddle(primarySkill3.str(), 193, 165, TNRB16, zwykly, curBack);
std::stringstream primarySkill4;
primarySkill4<<curHero->primSkills[3];
primarySkill4<<curHero->getPrimSkillLevel(3);
CSDL_Ext::printAtMiddle(primarySkill4.str(), 263, 165, TNRB16, zwykly, curBack);
blitAt(graphics->luck42->ourImages[curHero->getCurrentLuck()+3].bitmap, 239, 182, curBack);
@ -650,7 +650,7 @@ void CHeroWindow::redrawCurBack()
CSDL_Ext::printAt(expstr.str(), 69, 247, GEOR16, zwykly, curBack);
CSDL_Ext::printAt(CGI->generaltexth->jktexts[7].substr(1, CGI->generaltexth->jktexts[7].size()-2), 212, 231, GEOR13, tytulowy, curBack);
std::stringstream manastr;
manastr<<curHero->mana<<'/'<<curHero->primSkills[3]*10;
manastr<<curHero->mana<<'/'<<curHero->getPrimSkillLevel(3)*10;
CSDL_Ext::printAt(manastr.str(), 212, 247, GEOR16, zwykly, curBack);
}

View File

@ -134,8 +134,8 @@ void CGarrisonSlot::clickRight (tribool down)
pom = new StackState();
const CGHeroInstance *h = static_cast<const CGHeroInstance *>(getObj());
pom->currentHealth = 0;
pom->attackBonus = h->primSkills[0];
pom->defenseBonus = h->primSkills[1];
pom->attackBonus = h->getPrimSkillLevel(0);
pom->defenseBonus = h->getPrimSkillLevel(1);
pom->luck = h->getCurrentLuck();
pom->morale = h->getCurrentMorale();
}

View File

@ -27,7 +27,7 @@ SDL_Surface * Graphics::drawPrimarySkill(const CGHeroInstance *curh, SDL_Surface
char * buf = new char[10];
for (int i=from;i<to;i++)
{
SDL_itoa(curh->primSkills[i],buf,10);
SDL_itoa(curh->getPrimSkillLevel(i),buf,10);
printAtMiddle(buf,84+28*i,68,GEOR13,zwykly,ret);
}
delete[] buf;

View File

@ -0,0 +1,158 @@
//heroes'_inintial_set_of_secondary_abilities_format:_heroID_numberOfInitialSecSkills_(Skill_ID,_skill_lvl_for_every_spell)
0 2 6 1 1 1
1 2 6 1 1 1
2 2 6 1 23 1
3 2 6 1 5 1
4 2 6 1 13 1
5 2 6 1 22 1
6 2 6 1 19 1
7 2 6 1 19 1
8 2 7 1 27 1
9 2 7 1 4 1
10 2 7 1 13 1
11 1 7 2
12 2 7 1 8 1
13 2 7 1 11 1
14 2 7 1 21 1
15 2 7 1 24 1
16 2 6 1 23 1
17 2 9 1 26 1
18 1 1 2
19 2 4 1 6 1
20 1 26 2
21 2 1 1 22 1
22 2 0 1 26 1
23 2 1 1 2 1
24 2 7 1 18 1
25 2 7 2 10 1
26 2 7 1 24 1
27 2 7 1 27 1
28 2 7 1 11 1
29 2 7 1 9 1
30 2 7 1 25 1
31 2 7 1 3 1
32 2 3 1 8 1
33 1 18 2
34 2 8 1 25 1
35 2 18 1 23 1
36 2 8 1 19 1
37 2 18 1 26 1
38 2 8 1 22 1
39 2 18 1 24 1
40 1 7 2
41 2 7 1 8 1
42 2 7 1 11 1
43 2 7 1 24 1
44 2 7 1 10 1
45 2 7 1 25 1
46 2 7 1 4 1
47 2 7 1 18 1
48 1 3 2
49 2 7 1 18 1
50 1 23 2
51 2 19 1 26 1
52 2 18 1 22 1
53 2 1 1 3 1
54 2 1 1 2 1
55 1 22 2
56 2 7 1 24 1
57 2 7 1 18 1
58 2 7 1 8 1
59 2 7 1 10 1
60 2 7 1 21 1
61 2 7 1 11 1
62 2 7 1 25 1
63 2 7 1 6 1
64 2 12 1 26 1
65 2 12 1 20 1
66 2 12 1 21 1
67 2 12 1 19 1
68 2 12 1 22 1
69 1 12 2
70 2 12 1 22 1
71 2 12 1 23 1
72 2 12 1 18 1
73 2 12 1 7 1
74 2 12 1 25 1
75 2 12 1 11 1
76 2 12 1 8 1
77 2 12 1 21 1
78 1 12 2
79 2 12 1 24 1
80 2 3 1 6 1
81 2 20 1 22 1
82 2 19 1 22 1
83 2 6 1 26 1
84 1 22 2
85 2 2 1 19 1
86 2 6 1 18 1
87 2 19 1 22 1
88 2 7 1 18 1
89 2 7 1 8 1
90 2 7 1 25 1
91 1 7 2
92 2 7 1 11 1
93 2 7 1 3 2
94 2 7 1 24 1
95 2 7 1 21 1
96 2 22 1 10 1
97 2 22 1 20 1
98 2 22 1 1 1
99 2 22 1 3 1
100 2 22 1 0 1
101 2 22 1 26 1
102 1 22 2
103 2 22 1 19 1
104 2 7 1 25 1
105 2 7 1 6 1
106 2 7 1 2 1
107 2 7 1 19 1
108 2 7 1 20 1
109 2 7 1 22 1
110 2 7 1 11 1
111 2 7 1 26 1
112 2 23 1 26 1
113 2 23 1 6 1
114 2 23 1 1 1
115 1 23 2
116 2 23 1 22 1
117 2 23 1 0 1
118 2 23 1 20 1
119 2 23 1 3 1
120 1 7 2
121 2 7 1 8 1
122 2 7 1 5 1
123 2 7 1 27 1
124 2 7 1 21 1
125 2 7 1 25 1
126 2 7 1 24 1
127 2 7 1 11 1
128 2 20 1 22 1
129 2 13 1 19 1
130 2 20 1 22 1
131 1 19 2
132 2 2 1 22 1
133 2 13 1 19 1
134 1 22 2
135 2 19 1 21 1
136 2 7 1 14 1
137 2 7 1 15 1
138 2 7 1 16 1
139 2 7 1 17 1
140 2 7 1 14 1
141 2 7 1 15 1
142 2 7 1 16 1
143 2 7 1 17 1
144 1 6 2
145 2 7 1 14 3
146 2 6 1 22 1
147 1 7 2
148 2 1 1 6 1
149 1 22 2
150 1 12 2
151 2 13 1 19 1
152 2 6 1 23 1
153 2 13 1 19 1
154 2 19 1 22 1
155 2 6 1 19 1
-1

View File

@ -14,17 +14,17 @@ CHeroClass::CHeroClass()
CHeroClass::~CHeroClass()
{
}
int CHeroClass::chooseSecSkill(std::set<int> possibles) //picks secondary skill out from given possibilities
int CHeroClass::chooseSecSkill(const std::set<int> & possibles) const //picks secondary skill out from given possibilities
{
if(possibles.size()==1)
return *possibles.begin();
int totalProb = 0;
for(std::set<int>::iterator i=possibles.begin(); i!=possibles.end(); i++)
for(std::set<int>::const_iterator i=possibles.begin(); i!=possibles.end(); i++)
{
totalProb += proSec[*i];
}
int ran = rand()%totalProb;
for(std::set<int>::iterator i=possibles.begin(); i!=possibles.end(); i++)
for(std::set<int>::const_iterator i=possibles.begin(); i!=possibles.end(); i++)
{
ran -= proSec[*i];
if(ran<0)
@ -111,6 +111,35 @@ void CHeroHandler::loadHeroes()
nher->ID = heroes.size();
heroes.push_back(nher);
}
//loading initial secondary skills
std::ifstream inp;
inp.open("config" PATHSEPARATOR "heroes_sec_skills.txt", std::ios_base::in|std::ios_base::binary);
if(!inp.is_open())
{
tlog1<<"missing file: config/heroes_sec_skills.txt"<<std::endl;
}
else
{
inp>>dump;
int hid; //ID of currently read hero
int secQ; //number of secondary abilities
while(true)
{
inp>>hid;
if(hid == -1)
break;
inp>>secQ;
for(int g=0; g<secQ; ++g)
{
int a, b;
inp>>a; inp>>b;
heroes[hid]->secSkillsInit.push_back(std::make_pair(a, b));
}
}
inp.close();
}
//initial skills loaded
loadSpecialAbilities();
loadBiographies();
loadHeroClasses();

View File

@ -20,6 +20,7 @@ public:
bool isAllowed; //true if we can play with this hero (depends on map)
CHeroClass * heroClass;
EHeroClasses heroType; //hero class
std::vector<std::pair<int,int> > secSkillsInit; //initial secondaryskills; first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert)
//bool operator<(CHero& drugi){if (ID < drugi.ID) return true; else return false;}
};
@ -35,7 +36,7 @@ public:
int selectionProbability[9]; //probability of selection in towns
std::vector<int> terrCosts; //default costs of going through terrains: dirt, sand, grass, snow, swamp, rough, subterrain, lava, water, rock; -1 means terrain is imapassable
CDefHandler * moveAnim; //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
int chooseSecSkill(std::set<int> possibles); //picks secondary skill out from given possibilities
int chooseSecSkill(const std::set<int> & possibles) const; //picks secondary skill out from given possibilities
CHeroClass();
~CHeroClass();
};

View File

@ -275,6 +275,10 @@ int CGHeroInstance::getCurrentMorale() const
//TODO: write it
return 0;
}
int CGHeroInstance::getPrimSkillLevel(int id) const
{
return primSkills[id];
}
int CGHeroInstance::getSecSkillLevel(const int & ID) const
{
for(int i=0;i<secSkills.size();i++)

View File

@ -92,7 +92,7 @@ public:
int portrait; //may be custom
int mana; // remaining spell points
std::vector<int> primSkills; //0-attack, 1-defence, 2-spell power, 3-knowledge
std::vector<std::pair<int,int> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert)
std::vector<std::pair<int,int> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities
int movement; //remaining movement points
int identifier; //from the map file
bool sex;
@ -125,6 +125,7 @@ public:
bool canWalkOnSea() const;
int getCurrentLuck() const;
int getCurrentMorale() const;
int getPrimSkillLevel(int id) const;
int getSecSkillLevel(const int & ID) const; //0 - no skill
ui32 getArtAtPos(ui16 pos) const; //-1 - no artifact
void setArtAtPos(ui16 pos, int art);

View File

@ -1052,6 +1052,10 @@ void Mapa::loadHero( CGObjectInstance * &nobj, unsigned char * bufor, int &i )
nhi->secSkills[yy].second = readNormalNr(bufor,i, 1); ++i;
}
}
else //set default secondary skils
{
nhi->secSkills.push_back(std::make_pair(-1, -1));
}
if(readChar(bufor,i))//true if hero has nonstandard garrison
nhi->army = readCreatureSet(bufor,i,7,(version>RoE));
nhi->army.formation =bufor[i]; ++i; //formation

View File

@ -349,11 +349,11 @@ void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile
delete battleResult.data;
}
void prepareAttack(BattleAttack &bat, CStack *att, CStack *def)
void CGameHandler::prepareAttack(BattleAttack &bat, CStack *att, CStack *def, bool shooting)
{
bat.stackAttacking = att->ID;
bat.bsa.stackAttacked = def->ID;
bat.bsa.damageAmount = BattleInfo::calculateDmg(att, def);//counting dealt damage
bat.bsa.damageAmount = BattleInfo::calculateDmg(att, def, gs->getHero(att->attackerOwned ? gs->curB->hero1 : gs->curB->hero2), gs->getHero(def->attackerOwned ? gs->curB->hero1 : gs->curB->hero2), shooting);//counting dealt damage
//applying damages
bat.bsa.killedAmount = bat.bsa.damageAmount / def->creature->hitPoints;
@ -948,14 +948,14 @@ upgend:
return;
BattleAttack bat;
prepareAttack(bat,curStack,stackAtEnd);
prepareAttack(bat,curStack,stackAtEnd,false);
sendAndApply(&bat);
//counterattack
if(!vstd::contains(curStack->abilities,NO_ENEMY_RETALIATION)
&& stackAtEnd->alive()
&& stackAtEnd->counterAttacks ) //TODO: support for multiple retaliatons per turn
{
prepareAttack(bat,stackAtEnd,curStack);
prepareAttack(bat,stackAtEnd,curStack,false);
bat.flags |= 2;
sendAndApply(&bat);
}
@ -965,7 +965,7 @@ upgend:
&& stackAtEnd->alive() )
{
bat.flags = 0;
prepareAttack(bat,curStack,stackAtEnd);
prepareAttack(bat,curStack,stackAtEnd,false);
sendAndApply(&bat);
}
sendDataToClients(ui16(3008)); //end movement and attack
@ -981,13 +981,13 @@ upgend:
*destStack= gs->curB->getStackT(ba.destinationTile);
BattleAttack bat;
prepareAttack(bat,curStack,destStack);
prepareAttack(bat,curStack,destStack,true);
bat.flags |= 1;
if(vstd::contains(curStack->abilities,TWICE_ATTACK)
&& curStack->alive())
{
prepareAttack(bat,curStack,destStack);
prepareAttack(bat,curStack,destStack,true);
sendAndApply(&bat);
}

View File

@ -12,6 +12,7 @@ struct StartInfo;
class CCPPObjectScript;
class CScriptCallback;
struct BattleResult;
struct BattleAttack;
template <typename T> struct CPack;
template <typename T> struct Query;
class CGHeroInstance;
@ -56,6 +57,7 @@ class CGameHandler
void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
void moveStack(int stack, int dest);
void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
void prepareAttack(BattleAttack &bat, CStack *att, CStack *def, bool shooting); //if last parameter is true, attack is by shooting, if false it's a melee attack
void checkForBattleEnd( std::vector<CStack*> &stacks );
void setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army1, CCreatureSet &army2, CGHeroInstance * hero1, CGHeroInstance * hero2 );