Browse Source

- string ID's for spells (not configurable due to hardcode)
- spell chances in new towns guilds can be specified in config

Ivan Savenko 13 years ago
parent
commit
94c55e2632
8 changed files with 127 additions and 101 deletions
  1. 5 0
      client/CPreGame.cpp
  2. 1 0
      client/CPreGame.h
  3. 84 83
      config/spell_info.json
  4. 6 1
      lib/CGameState.cpp
  5. 15 13
      lib/CSpellHandler.cpp
  6. 3 2
      lib/CSpellHandler.h
  7. 12 1
      lib/CTownHandler.cpp
  8. 1 1
      lib/Map/CMapService.cpp

+ 5 - 0
client/CPreGame.cpp

@@ -4047,6 +4047,11 @@ CLoadingScreen::CLoadingScreen(boost::function<void ()> loader):
     loadingThread(loader)
 {}
 
+CLoadingScreen::~CLoadingScreen()
+{
+	loadingThread.join();
+}
+
 void CLoadingScreen::showAll(SDL_Surface *to)
 {
 	Rect rect(0,0,to->w, to->h);

+ 1 - 0
client/CPreGame.h

@@ -693,6 +693,7 @@ class CLoadingScreen : public CWindowObject
 	std::string getBackground();
 public:
 	CLoadingScreen(boost::function<void()> loader);
+	~CLoadingScreen();
 
 	void showAll(SDL_Surface *to);
 };

+ 84 - 83
config/spell_info.json

@@ -7,87 +7,88 @@
 	//   anim: main effect animation (AC format), -1 - none
     //   ranges: spell range description in SRSL ([no magic] [basic] [advanced] [expert])
 
-	"spells": [
-		{ "id": 0, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 1, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 2, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 3, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 4, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 5, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 6, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 7, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 8, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 9, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 10, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 11, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 12, "effect": 0, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 13, "effect": 0, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 14, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 15, "effect": -1, "anim": 64, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 16, "effect": -1, "anim": 46, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 17, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 18, "effect": -1, "anim": 10, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 19, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 20, "effect": -1, "anim": 45, "ranges": [ "1", "1", "1", "1" ] },
-		{ "id": 21, "effect": -1, "anim": 53, "ranges": [ "0,1", "0,1", "0,1", "0,1" ] },
-		{ "id": 22, "effect": -1, "anim": 9, "ranges": [ "0-2", "0-2", "0-2", "0-2" ] },
-		{ "id": 23, "effect": -1, "anim": 16, "ranges": [ "0,1", "0,1", "0,1", "0,1" ] },
-		{ "id": 24, "effect": -1, "anim": 8, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 25, "effect": -1, "anim": 29, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 26, "effect": -1, "anim": 12, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 27, "effect": 1, "anim": 27, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 28, "effect": 1, "anim": 2, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 29, "effect": 1, "anim": 11, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 30, "effect": 1, "anim": 22, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 31, "effect": 1, "anim": 24, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 32, "effect": 1, "anim": 23, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 33, "effect": 1, "anim": 26, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 34, "effect": 1, "anim": 5, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 35, "effect": 0, "anim": 41, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 36, "effect": 1, "anim": 3, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 37, "effect": 1, "anim": 39, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 38, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 39, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 40, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 41, "effect": 1, "anim": 36, "ranges": [ "0", "0", "0", "X" ], "counters" : [42] },
-		{ "id": 42, "effect": -1, "anim": 40, "ranges": [ "0", "0", "0", "X" ], "counters" : [41] },
-		{ "id": 43, "effect": 1, "anim": 4, "ranges": [ "0", "0", "0", "X" ], "counters" : [45] },
-		{ "id": 44, "effect": 1, "anim": 25, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 45, "effect": -1, "anim": 56, "ranges": [ "0", "0", "0", "X" ], "counters" : [43] },
-		{ "id": 46, "effect": 1, "anim": 54, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 47, "effect": -1, "anim": 14, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 48, "effect": 1, "anim": 0, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 49, "effect": 1, "anim": 20, "ranges": [ "0", "0", "0", "X" ], "counters" : [50] },
-		{ "id": 50, "effect": -1, "anim": 30, "ranges": [ "0", "0", "0", "X" ], "counters" : [49] },
-		{ "id": 51, "effect": 1, "anim": 18, "ranges": [ "0", "0", "0", "X" ], "counters" : [52] },
-		{ "id": 52, "effect": -1, "anim": 48, "ranges": [ "0", "0", "0", "X" ], "counters" : [51] },
-		{ "id": 53, "effect": 1, "anim": 31, "ranges": [ "0", "0", "0", "X" ], "counters" : [54] },
-		{ "id": 54, "effect": -1, "anim": 19, "ranges": [ "0", "0", "0", "X" ], "counters" : [53] },
-		{ "id": 55, "effect": 1, "anim": 28, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 56, "effect": 1, "anim": 17, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 57, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 58, "effect": 1, "anim": 7, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 59, "effect": -1, "anim": 35, "ranges": [ "0", "0", "0-1", "0-2" ] },
-		{ "id": 60, "effect": -1, "anim": 21, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 61, "effect": -1, "anim": 42, "ranges": [ "0", "0", "0", "X" ] },
-		{ "id": 62, "effect": -1, "anim": 6, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 63, "effect": 1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 64, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 65, "effect": 1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 66, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 67, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 68, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 69, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
-		{ "id": 70, "effect": 0, "anim": 70, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 71, "effect": -1, "anim": 67, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 72, "effect": 0, "anim": 68, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 73, "effect": -1, "anim": 69, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 74, "effect": -1, "anim": 70, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 75, "effect": -1, "anim": 71, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 76, "effect": 0, "anim": 72, "ranges": [ "0-1", "0-1", "0-1", "0-1" ] },
-		{ "id": 77, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 78, "effect": -1, "anim": 41, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 79, "effect": 0, "anim": 80, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 80, "effect": 0, "anim": 81, "ranges": [ "0", "0", "0", "0" ] }
-	]
+	"spells":
+	{
+		"summonBoat"     : { "id": 0, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"scuttleBoat"    : { "id": 1, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"visions"        : { "id": 2, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"viewEarth"      : { "id": 3, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"disguise"       : { "id": 4, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"viewAir"        : { "id": 5, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"fly"            : { "id": 6, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"waterWalk"      : { "id": 7, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"dimensionDoor"  : { "id": 8, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"townPortal"     : { "id": 9, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"quicksand"      : { "id": 10, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"landMine"       : { "id": 11, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"forceField"     : { "id": 12, "effect": 0, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
+		"fireWall"       : { "id": 13, "effect": 0, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
+		"earthquake"     : { "id": 14, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"magicArrow"     : { "id": 15, "effect": -1, "anim": 64, "ranges": [ "0", "0", "0", "0" ] },
+		"iceBolt"        : { "id": 16, "effect": -1, "anim": 46, "ranges": [ "0", "0", "0", "0" ] },
+		"lightningBolt"  : { "id": 17, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
+		"implosion"      : { "id": 18, "effect": -1, "anim": 10, "ranges": [ "0", "0", "0", "0" ] },
+		"chainLightning" : { "id": 19, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
+		"frostRing"      : { "id": 20, "effect": -1, "anim": 45, "ranges": [ "1", "1", "1", "1" ] },
+		"fireball"       : { "id": 21, "effect": -1, "anim": 53, "ranges": [ "0,1", "0,1", "0,1", "0,1" ] },
+		"inferno"        : { "id": 22, "effect": -1, "anim": 9, "ranges": [ "0-2", "0-2", "0-2", "0-2" ] },
+		"meteorShower"   : { "id": 23, "effect": -1, "anim": 16, "ranges": [ "0,1", "0,1", "0,1", "0,1" ] },
+		"deathRipple"    : { "id": 24, "effect": -1, "anim": 8, "ranges": [ "X", "X", "X", "X" ] },
+		"destroyUndead"  : { "id": 25, "effect": -1, "anim": 29, "ranges": [ "X", "X", "X", "X" ] },
+		"armageddon"     : { "id": 26, "effect": -1, "anim": 12, "ranges": [ "X", "X", "X", "X" ] },
+		"shield"         : { "id": 27, "effect": 1, "anim": 27, "ranges": [ "0", "0", "0", "X" ] },
+		"airShield"      : { "id": 28, "effect": 1, "anim": 2, "ranges": [ "0", "0", "0", "X" ] },
+		"fireShield"     : { "id": 29, "effect": 1, "anim": 11, "ranges": [ "0", "0", "0", "X" ] },
+		"protectAir"     : { "id": 30, "effect": 1, "anim": 22, "ranges": [ "0", "0", "0", "X" ] },
+		"protectFire"    : { "id": 31, "effect": 1, "anim": 24, "ranges": [ "0", "0", "0", "X" ] },
+		"protectWater"   : { "id": 32, "effect": 1, "anim": 23, "ranges": [ "0", "0", "0", "X" ] },
+		"protectEarth"   : { "id": 33, "effect": 1, "anim": 26, "ranges": [ "0", "0", "0", "X" ] },
+		"antiMagic"      : { "id": 34, "effect": 1, "anim": 5, "ranges": [ "0", "0", "0", "X" ] },
+		"dispel"         : { "id": 35, "effect": 0, "anim": 41, "ranges": [ "0", "0", "0", "X" ] },
+		"magicMirror"    : { "id": 36, "effect": 1, "anim": 3, "ranges": [ "0", "0", "0", "0" ] },
+		"cure"           : { "id": 37, "effect": 1, "anim": 39, "ranges": [ "0", "0", "0", "0" ] },
+		"resurrection"   : { "id": 38, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
+		"animateDead"    : { "id": 39, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
+		"sacrifice"      : { "id": 40, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
+		"bless"          : { "id": 41, "effect": 1, "anim": 36, "ranges": [ "0", "0", "0", "X" ], "counters" : [42] },
+		"curse"          : { "id": 42, "effect": -1, "anim": 40, "ranges": [ "0", "0", "0", "X" ], "counters" : [41] },
+		"bloodlust"      : { "id": 43, "effect": 1, "anim": 4, "ranges": [ "0", "0", "0", "X" ], "counters" : [45] },
+		"precision"      : { "id": 44, "effect": 1, "anim": 25, "ranges": [ "0", "0", "0", "X" ] },
+		"weakness"       : { "id": 45, "effect": -1, "anim": 56, "ranges": [ "0", "0", "0", "X" ], "counters" : [43] },
+		"stoneSkin"      : { "id": 46, "effect": 1, "anim": 54, "ranges": [ "0", "0", "0", "X" ] },
+		"disruptingRay"  : { "id": 47, "effect": -1, "anim": 14, "ranges": [ "0", "0", "0", "0" ] },
+		"prayer"         : { "id": 48, "effect": 1, "anim": 0, "ranges": [ "0", "0", "0", "X" ] },
+		"mirth"          : { "id": 49, "effect": 1, "anim": 20, "ranges": [ "0", "0", "0", "X" ], "counters" : [50] },
+		"sorrow"         : { "id": 50, "effect": -1, "anim": 30, "ranges": [ "0", "0", "0", "X" ], "counters" : [49] },
+		"fortune"        : { "id": 51, "effect": 1, "anim": 18, "ranges": [ "0", "0", "0", "X" ], "counters" : [52] },
+		"misfortune"     : { "id": 52, "effect": -1, "anim": 48, "ranges": [ "0", "0", "0", "X" ], "counters" : [51] },
+		"haste"          : { "id": 53, "effect": 1, "anim": 31, "ranges": [ "0", "0", "0", "X" ], "counters" : [54] },
+		"slow"           : { "id": 54, "effect": -1, "anim": 19, "ranges": [ "0", "0", "0", "X" ], "counters" : [53] },
+		"slayer"         : { "id": 55, "effect": 1, "anim": 28, "ranges": [ "0", "0", "0", "0" ] },
+		"frenzy"         : { "id": 56, "effect": 1, "anim": 17, "ranges": [ "0", "0", "0", "0" ] },
+		"titanBolt"      : { "id": 57, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
+		"counterstrike"  : { "id": 58, "effect": 1, "anim": 7, "ranges": [ "0", "0", "0", "X" ] },
+		"berserk"        : { "id": 59, "effect": -1, "anim": 35, "ranges": [ "0", "0", "0-1", "0-2" ] },
+		"hypnotize"      : { "id": 60, "effect": -1, "anim": 21, "ranges": [ "0", "0", "0", "0" ] },
+		"forgetfulness"  : { "id": 61, "effect": -1, "anim": 42, "ranges": [ "0", "0", "0", "X" ] },
+		"blind"          : { "id": 62, "effect": -1, "anim": 6, "ranges": [ "0", "0", "0", "0" ] },
+		"teleport"       : { "id": 63, "effect": 1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
+		"removeObstacle" : { "id": 64, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"clone"          : { "id": 65, "effect": 1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
+		"fireElemental"  : { "id": 66, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"earthElemental" : { "id": 67, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"waterElemental" : { "id": 68, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"airElemental"   : { "id": 69, "effect": 0, "anim": -1, "ranges": [ "X", "X", "X", "X" ] },
+		"stoneGaze"      : { "id": 70, "effect": 0, "anim": 70, "ranges": [ "0", "0", "0", "0" ] },
+		"poison"         : { "id": 71, "effect": -1, "anim": 67, "ranges": [ "0", "0", "0", "0" ] },
+		"bind"           : { "id": 72, "effect": 0, "anim": 68, "ranges": [ "0", "0", "0", "0" ] },
+		"disease"        : { "id": 73, "effect": -1, "anim": 69, "ranges": [ "0", "0", "0", "0" ] },
+		"paralyze"       : { "id": 74, "effect": -1, "anim": 70, "ranges": [ "0", "0", "0", "0" ] },
+		"age"            : { "id": 75, "effect": -1, "anim": 71, "ranges": [ "0", "0", "0", "0" ] },
+		"deathCloud"     : { "id": 76, "effect": 0, "anim": 72, "ranges": [ "0-1", "0-1", "0-1", "0-1" ] },
+		"thunderbolt"    : { "id": 77, "effect": -1, "anim": 38, "ranges": [ "0", "0", "0", "0" ] },
+		"dispelHelpful"  : { "id": 78, "effect": -1, "anim": 41, "ranges": [ "0", "0", "0", "0" ] },
+		"deathStare"     : { "id": 79, "effect": 0, "anim": 80, "ranges": [ "0", "0", "0", "0" ] },
+		"acidBreath"     : { "id": 80, "effect": 0, "anim": 81, "ranges": [ "0", "0", "0", "0" ] }
+	}
 }

+ 6 - 1
lib/CGameState.cpp

@@ -1418,7 +1418,11 @@ void CGameState::init(StartInfo * si)
 
 			for(ui32 ps=0;ps<vti->possibleSpells.size();ps++)
 				total += VLC->spellh->spells[vti->possibleSpells[ps]]->probabilities[vti->subID];
-			int r = (total)? ran()%total : -1;
+
+			if (total == 0) // remaining spells have 0 probability
+				break;
+
+			int r = ran()%total;
 			for(ui32 ps=0; ps<vti->possibleSpells.size();ps++)
 			{
 				r -= VLC->spellh->spells[vti->possibleSpells[ps]]->probabilities[vti->subID];
@@ -1435,6 +1439,7 @@ void CGameState::init(StartInfo * si)
 			vti->spells[s->level-1].push_back(s->id);
 			vti->possibleSpells -= s->id;
 		}
+		vti->possibleSpells.clear();
 		if(vti->getOwner() != 255)
 			getPlayer(vti->getOwner())->towns.push_back(vti);
 	}

+ 15 - 13
lib/CSpellHandler.cpp

@@ -8,6 +8,7 @@
 #include <cctype>
 #include "GameConstants.h"
 #include "BattleHex.h"
+#include "CModHandler.h"
 
 /*
  * CSpellHandler.cpp, part of VCMI engine
@@ -281,18 +282,15 @@ CSpell * CSpellHandler::loadSpell(CLegacyConfigParser & parser)
 	spell->fire    = parser.readString() == "x";
 	spell->air     = parser.readString() == "x";
 
-	for (int i = 0; i < 4 ; i++)
-		spell->costs.push_back(parser.readNumber());
+	spell->costs = parser.readNumArray<si32>(4);
 
 	spell->power = parser.readNumber();
-	for (int i = 0; i < 4 ; i++)
-		spell->powers.push_back(parser.readNumber());
+	spell->powers = parser.readNumArray<si32>(4);
 
 	for (int i = 0; i < 9 ; i++)
-		spell->probabilities.push_back(parser.readNumber());
+		spell->probabilities[i] = parser.readNumber();
 
-	for (int i = 0; i < 4 ; i++)
-		spell->AIVals.push_back(parser.readNumber());
+	spell->AIVals = parser.readNumArray<si32>(4);
 
 	for (int i = 0; i < 4 ; i++)
 		spell->descriptions.push_back(parser.readString());
@@ -350,21 +348,25 @@ void CSpellHandler::loadSpells()
 	//loading of additional spell traits
 	const JsonNode config(ResourceID("config/spell_info.json"));
 
-	BOOST_FOREACH(const JsonNode &spell, config["spells"].Vector())
+	BOOST_FOREACH(auto &spell, config["spells"].Struct())
 	{
 		//reading exact info
-		int spellID = spell["id"].Float();
+		int spellID = spell.second["id"].Float();
 		CSpell *s = spells[spellID];
 
-		s->positiveness = spell["effect"].Float();
-		s->mainEffectAnim = spell["anim"].Float();
+		s->positiveness = spell.second["effect"].Float();
+		s->mainEffectAnim = spell.second["anim"].Float();
 
 		s->range.resize(4);
 		int idx = 0;
-		BOOST_FOREACH(const JsonNode &range, spell["ranges"].Vector())
+		BOOST_FOREACH(const JsonNode &range, spell.second["ranges"].Vector())
 			s->range[idx++] = range.String();
 
-		s->counteredSpells = spell["counters"].convertTo<std::vector<TSpell> >();
+		s->counteredSpells = spell.second["counters"].convertTo<std::vector<TSpell> >();
+
+		s->identifier = spell.first;
+		VLC->modh->identifiers.registerObject("spell." + spell.first, spellID);
+
 	}
 	//spell fixes
 

+ 3 - 2
lib/CSpellHandler.h

@@ -23,6 +23,7 @@ public:
 	enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE};
 	enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
 	TSpell id;
+	std::string identifier;
 	std::string name;
 	std::string abbName; //abbreviated name
 	std::vector<std::string> descriptions; //descriptions of spell for skill levels: 0 - none, 1 - basic, etc
@@ -34,7 +35,7 @@ public:
 	si32 power; //spell's power
 	std::vector<si32> costs; //per skill level: 0 - none, 1 - basic, etc
 	std::vector<si32> powers; //[er skill level: 0 - none, 1 - basic, etc
-	std::vector<si32> probabilities; //% chance to gain for castles 
+	std::map<TFaction, si32> probabilities; //% chance to gain for castles
 	std::vector<si32> AIVals; //AI values: per skill level: 0 - none, 1 - basic, etc
 	std::string attributes; //reference only attributes
 	bool combatSpell; //is this spell combat (true) or adventure (false)
@@ -54,7 +55,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs 
+		h & identifier & id & name & abbName & descriptions & level & earth & water & fire & air & power & costs
 			& powers & probabilities & AIVals & attributes & combatSpell & creatureAbility & positiveness & range & counteredSpells & mainEffectAnim;
 	}
 };

+ 12 - 1
lib/CTownHandler.cpp

@@ -8,6 +8,7 @@
 #include "CModHandler.h"
 #include "CHeroHandler.h"
 #include "CArtHandler.h"
+#include "CSpellHandler.h"
 #include "Filesystem/CResourceLoader.h"
 
 /*
@@ -443,12 +444,22 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
 	{
 		int chance = node.second.Float();
 
-		VLC->modh->identifiers.requestIdentifier(std::string("heroClass.") + node.first, [=, &town](si32 classID)
+		VLC->modh->identifiers.requestIdentifier("heroClass." + node.first, [=, &town](si32 classID)
 		{
 			VLC->heroh->classes.heroClasses[classID]->selectionProbability[town.typeID] = chance;
 		});
 	}
 
+	BOOST_FOREACH(auto &node, source["guildSpells"].Struct())
+	{
+		int chance = node.second.Float();
+
+		VLC->modh->identifiers.requestIdentifier("spell." + node.first, [=, &town](si32 spellID)
+		{
+			VLC->spellh->spells[spellID]->probabilities[town.typeID] = chance;
+		});
+	}
+
 	loadBuildings(town, source["buildings"]);
 	loadClientData(town,source);
 }

+ 1 - 1
lib/Map/CMapService.cpp

@@ -2296,7 +2296,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 			{
 				if((pos - ist) * 8 + yy < GameConstants::SPELLS_QUANTITY)
 				{
-					if(c != (c | static_cast<ui8>(std::pow(2., yy))))
+					if(c == (c | static_cast<ui8>(std::pow(2., yy))))
 					{
 						nt->obligatorySpells.push_back((pos - ist) * 8 + yy);
 					}