1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00

[programming challenge] Improved spell handling.

This commit is contained in:
Michał W. Urbańczyk 2011-12-11 01:51:59 +00:00
parent 67571d5823
commit 2a18d2efcc
9 changed files with 95 additions and 11 deletions

View File

@ -8,6 +8,8 @@
#include <algorithm>
//#include <boost/thread.hpp>
#include "../../lib/CHeroHandler.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/CSpellHandler.h"
CBattleCallback * cbc;
@ -98,6 +100,8 @@ static bool willSecondHexBlockMoreEnemyShooters(const THex &h1, const THex &h2)
return shooters[0] < shooters[1];
}
BattleAction CStupidAI::activeStack( const CStack * stack )
{
//boost::this_thread::sleep(boost::posix_time::seconds(2));
@ -106,6 +110,14 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
std::vector<int> dists = cb->battleGetDistances(stack);
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
// const CStack *firstEnemy = cb->battleGetStacks(CBattleCallback::ONLY_ENEMY).front();
// if(cb->battleCanCastThisSpell(VLC->spellh->spells[Spells::FORGETFULNESS]) == SpellCasting::OK)
// castSpell(Spells::FORGETFULNESS, firstEnemy->position);
const CStack *firstEnemy = cb->battleGetStacks(CBattleCallback::ONLY_MINE).front();
if(cb->battleCanCastThisSpell(VLC->spellh->spells[Spells::AIR_SHIELD]) == SpellCasting::OK)
castSpell(Spells::AIR_SHIELD, firstEnemy->position);
BOOST_FOREACH(const CStack *s, cb->battleGetStacks(CBattleCallback::ONLY_ENEMY))
{
if(cb->battleCanShoot(stack, s->position))
@ -295,3 +307,29 @@ void CStupidAI::printOpeningReport()
% (i+1) % s->count % s->getCreature()->namePl % s->Attack() % s->Defense() % s->MaxHealth() % s->getMinDamage() % s->getMaxDamage();
}
}
void CStupidAI::castSpell(int spellID, int destinationTile, bool safe/* = true*/)
{
const CGHeroInstance *h = cb->battleGetFightingHero(side);
if(!h)
{
tlog1 << "A hero is required for casting spells!\n";
return;
}
SpellCasting::ESpellCastProblem canCast = safe ? cb->battleCanCastThisSpell(VLC->spellh->spells[spellID]) : SpellCasting::OK;
if(canCast != SpellCasting::OK)
{
tlog1 << "Spell cannot be cast (problem=" << canCast << ")!\n";
return;
}
BattleAction ba;
ba.actionType = BattleAction::HERO_SPELL;
ba.destinationTile = destinationTile;
ba.side = side;
ba.stackNumber = -(side+1); //-1 dla lewego bohatera, -2 dla prawego
ba.additionalInfo = spellID;
cb->battleMakeAction(&ba);
}

View File

@ -33,5 +33,6 @@ public:
void battleStacksRemoved(const BattleStacksRemoved & bsr) OVERRIDE; //called when certain stack is completely removed from battlefield
BattleAction goTowards(const CStack * stack, THex hex );
void castSpell(int spellID, int destinationTile, bool safe = true);
};

View File

@ -58,9 +58,10 @@ struct CheckTime
//all ms
const int PROCESS_INFO_TIME = 5;
const int MAKE_DECIDION_TIME = 125;
const int MEASURE_MARGIN = 1;
const int HANGUP_TIME = 50;
const int MAKE_DECIDION_TIME = 150;
const int MEASURE_MARGIN = 3;
const int HANGUP_TIME = 250;
const int CONSTRUCT_TIME = 50;
const int STARTUP_TIME = 100;
void postInfoCall(int timeUsed);
@ -68,6 +69,7 @@ void postDecisionCall(int timeUsed, const std::string &text = "AI was thinking o
struct Bomb
{
std::string txt;
int armed;
void run(int time)
@ -76,13 +78,15 @@ struct Bomb
if(armed)
{
tlog1 << "BOOOM! The bomb exploded! AI was thinking for too long!\n";
if(txt.size())
tlog1 << "Bomb description: " << txt << std::endl;;
exit(1);
}
delete this;
}
Bomb(int timer)
Bomb(int timer, const std::string &TXT = "") : txt(TXT)
{
boost::thread t(&Bomb::run, this, timer);
t.detach();

View File

@ -105,7 +105,8 @@ void CClient::requestMoveFromAIWorker(const CStack *s)
try
{
Bomb *b = new Bomb(MAKE_DECIDION_TIME + HANGUP_TIME);
boost::shared_lock<boost::shared_mutex> shl(*gs->mx);
Bomb *b = new Bomb(MAKE_DECIDION_TIME + HANGUP_TIME, "activeStack timer");
CheckTime timer;
ba = ai->activeStack(s);
postDecisionCall(timer.timeSinceStart());

View File

@ -36,13 +36,13 @@ void postDecisionCall(int timeUsed, const std::string &text/* = "AI was thinking
//awaiting variadic templates...
//
#define BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(TIME_LIMIT, function, ...) \
#define BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(TIME_LIMIT, txt, function, ...) \
do \
{ \
int timeUsed = 0; \
if(cl->ai) \
{ \
Bomb *b = new Bomb(TIME_LIMIT + HANGUP_TIME); \
Bomb *b = new Bomb(TIME_LIMIT + HANGUP_TIME,txt);\
CheckTime pr; \
cl->ai->function(__VA_ARGS__); \
postInfoCall(pr.timeSinceStart(), TIME_LIMIT); \
@ -51,7 +51,7 @@ void postDecisionCall(int timeUsed, const std::string &text/* = "AI was thinking
} while(0)
#define BATTLE_INTERFACE_CALL_IF_PRESENT(function,...) \
BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(PROCESS_INFO_TIME, function, __VA_ARGS__)
BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(PROCESS_INFO_TIME, "process info timer", function, __VA_ARGS__)
#define UNEXPECTED_PACK assert(0)
@ -282,7 +282,7 @@ void GarrisonDialog::applyCl(CClient *cl)
void BattleStart::applyCl( CClient *cl )
{
BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(STARTUP_TIME, battleStart, info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], cl->color);
BATTLE_INTERFACE_CALL_IF_PRESENT_WITH_TIME_LIMIT(STARTUP_TIME, "battleStart timer", battleStart, info->belligerents[0], info->belligerents[1], info->tile, info->heroes[0], info->heroes[1], cl->color);
}
void BattleNextRound::applyFirstCl(CClient *cl)

View File

@ -89,7 +89,7 @@ int main(int argc, char** argv)
tlog0 << "Cbc created\n";
if(battleAIName.size())
{
Bomb *b = new Bomb(55 + HANGUP_TIME);
Bomb *b = new Bomb(CONSTRUCT_TIME + 5 + HANGUP_TIME, "startup timer");
CheckTime timer;
//////////////////////////////////////////////////////////////////////////
cl.ai = CDynLibHandler::getNewBattleAI(battleAIName);

View File

@ -976,6 +976,17 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
for(int i = 0; i < ss.heroPrimSkills.size(); i++)
h->pushPrimSkill(i, ss.heroPrimSkills[i]);
if(ss.spells.size())
{
h->putArtifact(Arts::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
BOOST_FOREACH(si32 spell, ss.spells)
h->spells.insert(spell);
}
typedef const std::pair<si32, si8> &TSecSKill;
BOOST_FOREACH(TSecSKill secSkill, ss.heroSecSkills)
h->setSecSkillLevel((CGHeroInstance::SecondarySkill)secSkill.first, secSkill.second, 1);
h->initHero(h->subID);
obj->initObj();
}
@ -2832,6 +2843,14 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname)
BOOST_FOREACH(const JsonNode &n, n["heroPrimSkills"].Vector())
ss.heroPrimSkills.push_back(n.Float());
BOOST_FOREACH(const JsonNode &skillNode, n["heroSecSkills"].Vector())
{
std::pair<si32, si8> secSkill;
secSkill.first = skillNode.Vector()[0].Float();
secSkill.second = skillNode.Vector()[1].Float();
ss.heroSecSkills.push_back(secSkill);
}
assert(ss.heroPrimSkills.empty() || ss.heroPrimSkills.size() == PRIMARY_SKILLS);
if(ss.heroId != -1)

View File

@ -298,6 +298,7 @@ struct DLL_EXPORT DuelParameters
si32 heroId; //-1 if none
std::vector<si32> heroPrimSkills; //may be empty
std::vector<std::pair<si32, si8> > heroSecSkills; //may be empty; pairs <id, level>, level [0-3]
std::set<si32> spells;
SideSettings();

View File

@ -53,7 +53,27 @@ public:
namespace Spells
{
enum {SUMMON_BOAT=0, SCUTTLE_BOAT, VISIONS, VIEW_EARTH, DISGUISE, VIEW_AIR, FLY, WATER_WALK, DIMENSION_DOOR, TOWN_PORTAL};
enum {SUMMON_BOAT=0, SCUTTLE_BOAT, VISIONS, VIEW_EARTH, DISGUISE, VIEW_AIR, FLY, WATER_WALK, DIMENSION_DOOR, TOWN_PORTAL,
QUICKSAND=10, LAND_MINE=11, FORCE_FIELD=12, FIRE_WALL=13, EARTHQUAKE=14,
MAGIC_ARROW=15, ICE_BOLT=16, LIGHTNING_BOLT=17, IMPLOSION=18,
CHAIN_LIGHTNING=19, FROST_RING=20, FIREBALL=21, INFERNO=22,
METEOR_SHOWER=23, DEATH_RIPPLE=24, DESTROY_UNDEAD=25, ARMAGEDDON=26,
SHIELD=27, AIR_SHIELD=28, FIRE_SHIELD=29, PROTECTION_FROM_AIR=30,
PROTECTION_FROM_FIRE=31, PROTECTION_FROM_WATER=32,
PROTECTION_FROM_EARTH=33, ANTI_MAGIC=34, DISPEL=35, MAGIC_MIRROR=36,
CURE=37, RESURRECTION=38, ANIMATE_DEAD=39, SACRIFICE=40, BLESS=41,
CURSE=42, BLOODLUST=43, PRECISION=44, WEAKNESS=45, STONE_SKIN=46,
DISRUPTING_RAY=47, PRAYER=48, MIRTH=49, SORROW=50, FORTUNE=51,
MISFORTUNE=52, HASTE=53, SLOW=54, SLAYER=55, FRENZY=56,
TITANS_LIGHTNING_BOLT=57, COUNTERSTRIKE=58, BERSERK=59, HYPNOTIZE=60,
FORGETFULNESS=61, BLIND=62, TELEPORT=63, REMOVE_OBSTACLE=64, CLONE=65,
SUMMON_FIRE_ELEMENTAL=66, SUMMON_EARTH_ELEMENTAL=67, SUMMON_WATER_ELEMENTAL=68, SUMMON_AIR_ELEMENTAL=69,
STONE_GAZE=70, POISON=71, BIND=72, DISEASE=73, PARALYZE=74, AGE=75, DEATH_CLOUD=76, THUNDERBOLT=77,
DISPEL_HELPFUL_SPELLS=78, DEATH_STARE=79, ACID_BREATH_DEFENSE=80, ACID_BREATH_DAMAGE=81
};
}
bool DLL_EXPORT isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door