浏览代码

* support for Redwood Observatory
* support for Shrine of Magic Incantation / Gesture / Thought
* minor improvements

Michał W. Urbańczyk 16 年之前
父节点
当前提交
6ce9a253a4

+ 5 - 21
CGameState.cpp

@@ -99,6 +99,11 @@ std::string DLL_EXPORT toString(MetaString &ms)
 				ret += VLC->generaltexth->mines[ser].second;
 				continue;
 			}
+			else if(type == MetaString::SPELL_NAME)
+			{
+				ret += VLC->spellh->spells[ser].name;
+				continue;
+			}
 			else
 			{
 				switch(type)
@@ -1325,27 +1330,6 @@ float CGameState::getMarketEfficiency( int player, int mode/*=0*/ )
 	return ret;
 }
 
-std::set<int3> CGameState::tilesToReveal(int3 pos, int radious, int player) const
-{
-	std::set<int3> ret;
-	if(player >= PLAYER_LIMIT)
-	{
-		tlog1 << "Illegal call to tilesToReveal!\n";
-		return ret;
-	}
-
-	for (int xd = std::max<int>(pos.x - radious , 0); xd <= std::min<int>(pos.x + radious, map->width-1); xd++)
-	{
-		for (int yd = std::max<int>(pos.y - radious, 0); yd <= std::min<int>( pos.y + radious, map->height-1); yd++)
-		{
-			double distance = pos.dist2d(int3(xd,yd,pos.z)) - 0.5;
-			if(distance <= radious  &&  (player<0 || players.find(player)->second.fogOfWarMap[xd][yd][pos.z]==0))
-				ret.insert(int3(xd,yd,pos.z));
-		}
-	}
-	return ret;
-}
-
 void CGameState::loadTownDInfos()
 {
 	for(int i=0;i<F_NUMBER;i++)

+ 0 - 1
CGameState.h

@@ -241,7 +241,6 @@ public:
 	int battleGetBattlefieldType(int3 tile = int3());//   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos);
 	float getMarketEfficiency(int player, int mode=0);
-	std::set<int3> tilesToReveal(int3 pos, int radious, int player) const; //if player==-1 => adds all tiles in radious
 	int canBuildStructure(const CGTownInstance *t, int ID);// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
 
 	CGameState();

+ 15 - 2
CPlayerInterface.cpp

@@ -630,9 +630,12 @@ void CInfoPopup::close()
 	if(free)
 		SDL_FreeSurface(bitmap);
 	delete this;
+	if(LOCPLINT->curint->subInt)
+		return;
+
 	if(LOCPLINT->curint == LOCPLINT->adventureInt)
 		LOCPLINT->adventureInt->show();
-	else if((LOCPLINT->curint == LOCPLINT->castleInt) && !LOCPLINT->castleInt->subInt)
+	else if(LOCPLINT->curint == LOCPLINT->castleInt)
 		LOCPLINT->castleInt->showAll();
 }
 void CInfoPopup::show(SDL_Surface * to)
@@ -684,6 +687,11 @@ void SComponent::init(Etype Type, int Subtype, int Val)
 	subtype = Subtype;
 	val = Val;
 	SDL_Surface * temp = this->getImg();
+	if(!temp)
+	{
+		tlog1 << "Error: cannot find graphic for component with id=" << type << " subid=" << subtype << " val=" << val << std::endl;
+		return;
+	}
 	pos.w = temp->w;
 	pos.h = temp->h;
 }
@@ -696,6 +704,8 @@ SComponent::SComponent(const Component &c)
 {
 	if(c.id==5)
 		init(experience,c.subtype,c.val);
+	else if(c.id == Component::SPELL)
+		init(spell,c.subtype,c.val);
 	else
 		init((Etype)c.id,c.subtype,c.val);
 
@@ -734,6 +744,9 @@ SDL_Surface * SComponent::getImg()
 	case luck:
 		return graphics->luck82->ourImages[val+3].bitmap;
 		break;
+	case spell:
+		return graphics->spellscr->ourImages[subtype].bitmap;
+		break;
 	}
 	return NULL;
 }
@@ -1124,7 +1137,7 @@ void CPlayerInterface::yourTurn()
 
 
 		//if there are any waiting dialogs, show them
-		if(dialogs.size())
+		if(dialogs.size() && !showingDialog->get())
 		{
 			dialogs.front()->buttons[0]->callback += boost::bind(&IActivable::activate,LOCPLINT->curint);
 			showingDialog->set(true);

+ 2 - 0
client/Client.h

@@ -91,6 +91,8 @@ public:
 	void setManaPoints(int hid, int val){};
 	void giveHero(int id, int player){};
 	void changeObjPos(int objid, int3 newPos, ui8 flags){};
+	void sendAndApply(CPackForClient * info){};
+
 	//////////////////////////////////////////////////////////////////////////
 	friend class CCallback; //handling players actions
 	friend void processCommand(const std::string &message, CClient *&client); //handling console

+ 1 - 0
client/Graphics.cpp

@@ -223,6 +223,7 @@ Graphics::Graphics()
 	tasks += GET_DEF(abils32,"SECSK32.DEF");
 	tasks += GET_DEF(abils44,"SECSKILL.DEF");
 	tasks += GET_DEF(abils82,"SECSK82.DEF");
+	tasks += GET_DEF(spellscr,"SPELLSCR.DEF");
 
 	std::ifstream ifs("config/cr_bgs.txt"); 
 	int id;

+ 2 - 0
client/Graphics.h

@@ -50,6 +50,8 @@ public:
 	std::vector<std::string> guildBgs;// name of bitmaps with imgs for mage guild screen
 	//abilities
 	CDefHandler * abils32, * abils44, * abils82;
+	//spells
+	CDefHandler *spellscr; //spell on the scroll 83x61
 	//functions
 	Graphics();	
 	void initializeBattleGraphics();

+ 2 - 0
hch/CCreatureHandler.cpp

@@ -457,6 +457,8 @@ void CCreatureHandler::loadCreatures()
 	creatures[123].abilities.insert(DOUBLE_WIDE);//ice elemental should be treated as double-wide
 	creatures[140].abilities.insert(DOUBLE_WIDE);//boar should be treated as double-wide
 	creatures[142].abilities.insert(DOUBLE_WIDE);//nomads should be treated as double-wide
+
+	creatures[46].abilities -= FLYING;
 }
 
 void CCreatureHandler::loadAnimationInfo()

+ 84 - 0
hch/CObjectHandler.cpp

@@ -1893,4 +1893,88 @@ void CGEvent::activated( const CGHeroInstance * h ) const
 	if(guarders)
 		cb->startBattleI(h->id,guarders,pos,boost::bind(&CGEvent::endBattle,this,_1));
 	cb->removeObject(id);
+}
+
+void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
+{
+	FoWChange fw;
+	fw.player = h->tempOwner;
+	fw.mode = 1;
+	cb->getTilesInRange(fw.tiles,pos,20,h->tempOwner,1);
+	cb->sendAndApply(&fw);
+
+	InfoWindow iw;
+	iw.player = h->tempOwner;
+	iw.text.addTxt(MetaString::ADVOB_TXT,98);
+	cb->showInfoDialog(&iw);
+}
+
+void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
+{
+	if(spell == 255)
+	{
+		tlog1 << "Not initialized shrine visited!\n";
+		return;
+	}
+
+	InfoWindow iw;
+	iw.player = h->getOwner();
+	iw.text.addTxt(MetaString::ADVOB_TXT,127 + ID - 88);
+	iw.text.addTxt(MetaString::SPELL_NAME,spell);
+	iw.text << ".";
+
+	if(!h->getArt(17)) //no spellbook
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,131);
+	}
+	else if(ID == 90  &&  !h->getSecSkillLevel(7)) //it's third level spell and hero doesn't have wisdom
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,130);
+	}
+	else if(vstd::contains(h->spells,spell))//hero already knows the spell
+	{
+		iw.text.addTxt(MetaString::ADVOB_TXT,174);
+	}
+	else //give spell
+	{
+		std::set<ui32> spells;
+		spells.insert(spell);
+		cb->changeSpells(h->id,true,spells);
+
+		iw.components.push_back(Component(Component::SPELL,spell,0,0));
+	}
+
+	cb->showInfoDialog(&iw);
+}
+
+void CGShrine::initObj()
+{
+	if(spell == 255) //spell not set
+	{
+		int level = ID-87;
+		std::vector<ui32> possibilities;
+
+		//add all allowed spells of wanted level
+		for(int i=0; i<SPELLS_QUANTITY; i++)
+		{
+			if(VLC->spellh->spells[i].level == level
+				&& cb->isAllowed(0,VLC->spellh->spells[i].id))
+			{
+				possibilities.push_back(VLC->spellh->spells[i].id);
+			}
+		}
+
+		if(!possibilities.size())
+		{
+			tlog1 << "Error: cannot init shrine, no allowed spells!\n";
+			return;
+		}
+
+		spell = possibilities[ran() % possibilities.size()];
+	}
+}
+
+const std::string & CGShrine::getHoverText() const
+{
+	return hoverName;
 }

+ 13 - 0
hch/CObjectHandler.h

@@ -536,6 +536,9 @@ class DLL_EXPORT CGShrine : public CGObjectInstance
 {
 public:
 	ui8 spell; //number of spell or 255 if random
+	void onHeroVisit(const CGHeroInstance * h) const;
+	void initObj();
+	const std::string & getHoverText() const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -657,6 +660,16 @@ public:
 	}
 };
 
+class DLL_EXPORT CGObservatory : public CGObjectInstance //Redwood observatory
+{
+public:
+	void onHeroVisit(const CGHeroInstance * h) const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<CGObjectInstance&>(*this);
+	}
+};
 
 
 

+ 36 - 0
lib/IGameCallback.cpp

@@ -69,4 +69,40 @@ int IGameCallback::getHeroCount( int player, bool includeGarrisoned )
 			if(!gs->getPlayer(player)->heroes[i]->inTownGarrison)
 				ret++;
 	return ret;
+}
+
+void IGameCallback::getTilesInRange( std::set<int3> &tiles, int3 pos, int radious, int player/*=-1*/, int mode/*=0*/ )
+{
+	if(player >= PLAYER_LIMIT)
+	{
+		tlog1 << "Illegal call to getTilesInRange!\n";
+		return;
+	}
+
+	for (int xd = std::max<int>(pos.x - radious , 0); xd <= std::min<int>(pos.x + radious, gs->map->width - 1); xd++)
+	{
+		for (int yd = std::max<int>(pos.y - radious, 0); yd <= std::min<int>(pos.y + radious, gs->map->height - 1); yd++)
+		{
+			double distance = pos.dist2d(int3(xd,yd,pos.z)) - 0.5;
+			if(distance <= radious)
+			{
+				if(player < 0 
+					|| (mode == 1  && gs->players.find(player)->second.fogOfWarMap[xd][yd][pos.z]==0)
+					|| (mode == -1 && gs->players.find(player)->second.fogOfWarMap[xd][yd][pos.z]==1)
+				)
+					tiles.insert(int3(xd,yd,pos.z));
+			}
+		}
+	}
+}
+
+bool IGameCallback::isAllowed( int type, int id )
+{
+	switch(type)
+	{
+	case 0:
+		return gs->map->allowedSpell[id];
+	default:
+		tlog1 << "Wrong call to IGameCallback::isAllowed!\n";
+	}
 }

+ 5 - 0
lib/IGameCallback.h

@@ -19,6 +19,7 @@ struct ShowInInfobox;
 struct BattleResult;
 class CGameState;
 struct PlayerSettings;
+struct CPackForClient;
 
 class DLL_EXPORT IGameCallback
 {
@@ -38,6 +39,8 @@ public:
 	virtual int getSelectedHero()=0;
 	virtual const PlayerSettings * getPlayerSettings(int color);
 	virtual int getHeroCount(int player, bool includeGarrisoned);
+	virtual void getTilesInRange(std::set<int3> &tiles, int3 pos, int radious, int player=-1, int mode=0); //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 -  only unrevealed
+	virtual bool isAllowed(int type, int id); //type: 0 - spell
 
 	//do sth
 	virtual void changeSpells(int hid, bool give, const std::set<ui32> &spells)=0;
@@ -65,6 +68,8 @@ public:
 	virtual void setManaPoints(int hid, int val)=0;
 	virtual void giveHero(int id, int player)=0;
 	virtual void changeObjPos(int objid, int3 newPos, ui8 flags)=0;
+	virtual void sendAndApply(CPackForClient * info)=0;
+
 
 	friend struct CPackForClient;
 	friend struct CPackForServer;

+ 7 - 2
lib/NetPacks.h

@@ -59,6 +59,8 @@ struct Query : public CPackForClient
 
 struct MetaString : public CPack //2001 helper for object scrips
 {
+	enum {GENERAL_TXT=1, XTRAINFO_TXT, OBJ_NAMES, RES_NAMES, ART_NAMES, ARRAY_TXT, CRE_PL_NAMES, CREGENS, MINE_NAMES, 
+		MINE_EVNTS, ADVOB_TXT, ART_EVNTS, SPELL_NAME};
 	std::vector<std::string> strings;
 	std::vector<std::pair<ui8,ui32> > texts; //pairs<text handler type, text number>; types: 1 - generaltexthandler->all; 2 - objh->xtrainfo; 3 - objh->names; 4 - objh->restypes; 5 - arth->artifacts[id].name; 6 - generaltexth->arraytxt; 7 - creh->creatures[os->subID].namePl; 8 - objh->creGens; 9 - objh->mines[ID].first; 10 - objh->mines[ID].second; 11 - objh->advobtxt
 	std::vector<si32> message;
@@ -68,7 +70,10 @@ struct MetaString : public CPack //2001 helper for object scrips
 	{
 		h & strings & texts & message & replacements;
 	}
-
+	void addTxt(ui8 type, ui32 serial)
+	{
+		*this << std::make_pair(type,serial);
+	}
 	MetaString& operator<<(const std::pair<ui8,ui32> &txt)
 	{
 		message.push_back(-((si32)texts.size())-1);
@@ -472,7 +477,7 @@ struct NewTurn : public CPackForClient //101
 
 struct Component : public CPack //2002 helper for object scrips informations
 {
-	enum {PRIM_SKILL,SEC_SKILL,RESOURCE,CREATURE,ARTIFACT,EXPERIENCE};
+	enum {PRIM_SKILL,SEC_SKILL,RESOURCE,CREATURE,ARTIFACT,EXPERIENCE,SPELL};
 	ui16 id, subtype; //id uses ^^^ enums, when id==EXPPERIENCE subtype==0 means exp points and subtype==1 levels)
 	si32 val; // + give; - take
 	si16 when; // 0 - now; +x - within x days; -x - per x days

+ 1 - 0
lib/RegisterTypes.h

@@ -25,6 +25,7 @@ template<typename Serializer> DLL_EXPORT void registerTypes1(Serializer &s)
 	s.registerType<CGQuestGuard>();
 	s.registerType<CGBonusingObject>();
 	s.registerType<CGMagicWell>();
+	s.registerType<CGObservatory>();
 	s.registerType<CGObjectInstance>();
 }
 

+ 5 - 0
map.cpp

@@ -1808,6 +1808,11 @@ void Mapa::readObjects( unsigned char * bufor, int &i)
 				nobj = new CGMagicWell();
 				break;
 			}
+		case 58: //Redwood Observatory
+			{
+				nobj = new CGObservatory();
+				break;
+			}
 		case 214: //hero placeholder
 			{
 				i+=3; //TODO: handle it more properly

+ 4 - 4
server/CGameHandler.cpp

@@ -1229,7 +1229,7 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 			{
 				obj->onHeroLeave(h);
 			}
-			tmh.fowRevealed = gs->tilesToReveal(h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner);
+			getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1);
 			sendAndApply(&tmh);
 			tlog5 << "Moved to " <<tmh.end<<std::endl;
 
@@ -1256,7 +1256,7 @@ void CGameHandler::moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker)
 			}
 		}
 		tmh.result = instant+1;
-		tmh.fowRevealed = gs->tilesToReveal(h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner);
+		getTilesInRange(tmh.fowRevealed,h->getSightCenter()+(tmh.end-tmh.start),h->getSightRadious(),h->tempOwner,1);
 		sendAndApply(&tmh);
 	}
 }
@@ -2406,9 +2406,9 @@ void CGameHandler::handleTimeEvents()
 			if( pinfo  //player exists
 				&& (ev->players & 1<<player) //event is enabled to this player
 				&& ((ev->computerAffected && !pinfo->human) 
-				|| (ev->humanAffected && pinfo->human)
-				)
+					|| (ev->humanAffected && pinfo->human)
 				)
+			)
 			{
 				//give resources
 				SetResources sr;

+ 1 - 1
server/NetPacksServer.cpp

@@ -136,5 +136,5 @@ void PlayerMessage::applyGh( CGameHandler *gh )
 void SetSelection::applyGh( CGameHandler *gh )
 {
 	if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;
-	gh->sendToAllClients(this);
+	gh->sendAndApply(this);
 }