Browse Source

[programming challenge] Improved spell handling.

Michał W. Urbańczyk 14 years ago
parent
commit
2a18d2efcc

+ 38 - 0
AI/StupidAI/StupidAI.cpp

@@ -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);
+}

+ 1 - 0
AI/StupidAI/StupidAI.h

@@ -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);
 };
 

+ 8 - 4
VCMI_BattleAiHost/CheckTime.h

@@ -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();

+ 2 - 1
VCMI_BattleAiHost/Client.cpp

@@ -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());

+ 4 - 4
VCMI_BattleAiHost/NetPacksRunner.cpp

@@ -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)

+ 1 - 1
VCMI_BattleAiHost/main.cpp

@@ -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);

+ 19 - 0
lib/CGameState.cpp

@@ -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)

+ 1 - 0
lib/CGameState.h

@@ -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();

+ 21 - 1
lib/CSpellHandler.h

@@ -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