diff --git a/CCallback.cpp b/CCallback.cpp index 21f0d6773..f92514ddf 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -732,6 +732,13 @@ void CCallback::sendMessage(const std::string &mess) *cl->serv << ± } +void CCallback::buildBoat( const IShipyard *obj ) +{ + BuildBoat bb; + bb.objid = obj->o->id; + *cl->serv << &bb; +} + InfoAboutHero::InfoAboutHero() { details = NULL; diff --git a/CCallback.h b/CCallback.h index 02d1d15d0..cd2a47a5c 100644 --- a/CCallback.h +++ b/CCallback.h @@ -37,6 +37,7 @@ struct lua_State; class CClient; struct TerrainTile; class CHeroClass; +class IShipyard; struct InfoAboutHero { @@ -102,6 +103,7 @@ public: virtual void recruitHero(const CGTownInstance *town, const CGHeroInstance *hero)=0; virtual void save(const std::string &fname) = 0; virtual void sendMessage(const std::string &mess) = 0; + virtual void buildBoat(const IShipyard *obj) = 0; //get info virtual bool verifyPath(CPath * path, bool blockSea)const =0; @@ -196,6 +198,7 @@ public: void recruitHero(const CGTownInstance *town, const CGHeroInstance *hero); void save(const std::string &fname); void sendMessage(const std::string &mess); + void buildBoat(const IShipyard *obj); //get info bool verifyPath(CPath * path, bool blockSea) const; diff --git a/CGameInterface.h b/CGameInterface.h index da7385c8b..47f450d5d 100644 --- a/CGameInterface.h +++ b/CGameInterface.h @@ -31,6 +31,7 @@ class CGObjectInstance; class CGDwelling; class CCreatureSet; class CArmedInstance; +class IShipyard; struct BattleResult; struct BattleAttack; struct BattleStackAttacked; @@ -83,12 +84,14 @@ public: virtual void receivedResource(int type, int val){}; virtual void showInfoDialog(const std::string &text, const std::vector &components, int soundID){}; virtual void showRecruitmentDialog(const CGDwelling *dwelling, int level){} + virtual void showShipyardDialog(const IShipyard *obj){} //obj may be town or shipyard; state: 0 - can buid, 1 - lack of resources, 2 - dest tile is blocked, 3 - no water //virtual void showSelDialog(const std::string &text, const std::vector &components, ui32 askID){}; //virtual void showYesNoDialog(const std::string &text, const std::vector &components, ui32 askID){}; virtual void showBlockingDialog(const std::string &text, const std::vector &components, ui32 askID, const int soundID, bool selection, bool cancel) = 0; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID. virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done virtual void tileHidden(const std::set &pos){}; virtual void tileRevealed(const std::set &pos){}; + virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard virtual void yourTurn(){}; virtual void availableCreaturesChanged(const CGTownInstance *town){}; virtual void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it diff --git a/client/CCastleInterface.cpp b/client/CCastleInterface.cpp index 8d385de26..b13c14a1e 100644 --- a/client/CCastleInterface.cpp +++ b/client/CCastleInterface.cpp @@ -393,6 +393,7 @@ public: CCastleInterface::CCastleInterface(const CGTownInstance * Town, int listPos) :hslotup(241,387,0,Town->garrisonHero,this),hslotdown(241,483,1,Town->visitingHero,this) { + showing = false; bars = CDefHandler::giveDefEss("TPTHBAR.DEF"); status = CDefHandler::giveDefEss("TPTHCHK.DEF"); LOCPLINT->castleInt = this; @@ -579,7 +580,11 @@ void CCastleInterface::buildingClicked(int building) break; } - //TODO: case 6: //shipyard + case 6: //shipyard + { + LOCPLINT->showShipyardDialog(town); + break; + } case 7: case 8: case 9: //fort/citadel/castle { CFortScreen *fs = new CFortScreen(this); @@ -622,7 +627,9 @@ void CCastleInterface::buildingClicked(int building) //TODO: case 17: //special 1 //TODO: case 18: //basic horde 1 //TODO: case 19: //upg horde 1 - //TODO: case 20: //ship at shipyard + case 20: //ship at shipyard + //Do nothing. + break; //TODO: case 21: //special 2 case 22: //special 3 { @@ -886,6 +893,19 @@ void CCastleInterface::recreateBuildings() else break; } + + //ship in shipyard + if(vstd::contains(town->builtBuildings,6)) + { + std::vector vobjs = LOCPLINT->cb->getVisitableObjs(town->bestLocation()); + if(vobjs.size() && vobjs.front()->ID == 8) //there is visitable obj at shipyard output tile and it's a boat + { + Structure * st = CGI->townh->structures[town->subID][20]; + buildings.push_back(new CBuildingRect(st)); + s.insert(std::pair(st->group,st->ID)); + } + } + std::sort(buildings.begin(),buildings.end(),srthlp); //code for Mana Vortex (there are two sets of animation frames - one without mage guild and one with @@ -934,6 +954,10 @@ void CCastleInterface::recreateBuildings() shipyard->max = 1; } } + + if(showing) + for(size_t i=0;iactivate(); } CRecruitmentWindow * CCastleInterface::showRecruitmentWindow( int building ) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 791a1fe99..6162203c6 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1726,6 +1726,7 @@ const CGHeroInstance * CPlayerInterface::getWHero( int pos ) void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, int level) { waitWhileDialog(); + boost::unique_lock un(*pim); std::vector > cres; for(int i = 0; i < dwelling->creatures.size(); i++) { @@ -1744,6 +1745,30 @@ void CPlayerInterface::waitWhileDialog() showingDialog->cond.wait(un); } +void CPlayerInterface::showShipyardDialog(const IShipyard *obj) +{ + boost::unique_lock un(*pim); + int state = obj->state(); + std::vector cost; + obj->getBoatCost(cost); + CShipyardWindow *csw = new CShipyardWindow(cost, state, boost::bind(&CCallback::buildBoat, cb, obj)); + pushInt(csw); +} + +void CPlayerInterface::newObject( const CGObjectInstance * obj ) +{ + boost::unique_lock un(*pim); + CGI->mh->printObject(obj); + //we might have built a boat in shipyard in opened town screen + if(obj->ID == 8 + && LOCPLINT->castleInt + && obj->pos-obj->getVisitableOffset() == LOCPLINT->castleInt->town->bestLocation()) + { + CGI->soundh->playSound(soundBase::newBuilding); + LOCPLINT->castleInt->recreateBuildings(); + } +} + void SystemOptions::setMusicVolume( int newVolume ) { musicVolume = newVolume; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 64477a3c4..28171dc13 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -163,10 +163,12 @@ public: void receivedResource(int type, int val); void showInfoDialog(const std::string &text, const std::vector &components, int soundID); void showRecruitmentDialog(const CGDwelling *dwelling, int level); + void showShipyardDialog(const IShipyard *obj); //obj may be town or shipyard; void showBlockingDialog(const std::string &text, const std::vector &components, ui32 askID, int soundID, bool selection, bool cancel); //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID. void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function &onEnd); void tileHidden(const std::set &pos); //called when given tiles become hidden under fog of war void tileRevealed(const std::set &pos); //called when fog of war disappears from given tiles + void newObject(const CGObjectInstance * obj); void yourTurn(); void availableCreaturesChanged(const CGTownInstance *town); void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain);//if gain hero received bonus, else he lost it @@ -206,7 +208,7 @@ public: void handleMouseMotion(SDL_Event *sEvent); void init(ICallback * CB); int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on - void showInfoDialog(const std::string &text, const std::vector & components, int soundID); + void showInfoDialog(const std::string &text, const std::vector & components = std::vector(), int soundID = 0); void showYesNoDialog(const std::string &text, const std::vector & components, CFunctionList onYes, CFunctionList onNo, bool DelComps); //deactivateCur - whether current main interface should be deactivated; delComps - if components will be deleted on window close bool moveHero(const CGHeroInstance *h, CPath path); diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 98ac00136..52c938b71 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -3976,4 +3976,67 @@ CExchangeWindow::~CExchangeWindow() //d-tor } } +void CShipyardWindow::activate() +{ + build->activate(); + quit->activate(); +} +void CShipyardWindow::deactivate() +{ + build->deactivate(); + quit->deactivate(); +} + +void CShipyardWindow::show( SDL_Surface * to ) +{ + blitAt(bg,pos,to); + CSDL_Ext::blit8bppAlphaTo24bpp(graphics->boatAnims[1]->ourImages[21 + frame++/8%8].bitmap, NULL, to, &genRect(64, 96, pos.x+110, pos.y+85)); + build->show(to); + quit->show(to); +} + +CShipyardWindow::~CShipyardWindow() +{ + delete build; + delete quit; +} + +CShipyardWindow::CShipyardWindow(const std::vector &cost, int state, const boost::function &onBuy) +{ + frame = 0; + SDL_Surface * bgtemp; //loaded as 8bpp surface + bgtemp = BitmapHandler::loadBitmap("TPSHIP.bmp"); + pos.x = screen->w/2 - bgtemp->w/2; + pos.y = screen->h/2 - bgtemp->h/2; + pos.w = bgtemp->w; + pos.h = bgtemp->h; + SDL_SetColorKey(bgtemp,SDL_SRCCOLORKEY,SDL_MapRGB(bgtemp->format,0,255,255)); + graphics->blueToPlayersAdv(bgtemp, LOCPLINT->playerID); + bg = SDL_ConvertSurface(bgtemp, screen->format, screen->flags); //to 24 bpp + SDL_FreeSurface(bgtemp); + + bgtemp = BitmapHandler::loadBitmap("TPSHIPBK.bmp"); + blitAt(bgtemp, 100, 69, bg); + SDL_FreeSurface(bgtemp); + + bool affordable = true; + for(int i = 0; i < cost.size(); i++) + { + if(cost[i] > LOCPLINT->cb->getResourceAmount(i)) + { + affordable = false; + break; + } + } + + quit = new AdventureMapButton(CGI->generaltexth->allTexts[599], "", boost::bind(&CPlayerInterface::popIntTotally, LOCPLINT, this), pos.x+224, pos.y+312, "ICANCEL.DEF", SDLK_RETURN); + build = new AdventureMapButton(CGI->generaltexth->allTexts[598], "", boost::bind(&CPlayerInterface::popIntTotally, LOCPLINT, this), pos.x+42, pos.y+312, "IBY6432.DEF", SDLK_RETURN); + build->callback += onBuy; + + if(!affordable) + build->block(true); + + printAtMiddle(CGI->generaltexth->jktexts[15], 165, 26, GEOR13, zwykly, bg); //Resource cost: + printAtMiddle(CGI->generaltexth->jktexts[14], 165, 218, GEOR16, tytulowy, bg); //Build A New Ship +} diff --git a/client/GUIClasses.h b/client/GUIClasses.h index 061e4548d..2d1d32e01 100644 --- a/client/GUIClasses.h +++ b/client/GUIClasses.h @@ -700,5 +700,20 @@ public: ~CExchangeWindow(); //d-tor }; +class CShipyardWindow : public CIntObject, public IShowActivable +{ +public: + CStatusBar *bar; + SDL_Surface *bg; //background + AdventureMapButton *build, *quit; + + unsigned char frame; //frame of the boat animation + + void activate(); + void deactivate(); + void show(SDL_Surface * to); + CShipyardWindow(const std::vector &cost, int state, const boost::function &onBuy); + ~CShipyardWindow(); +}; #endif //__GUICLASSES_H__ diff --git a/client/Graphics.cpp b/client/Graphics.cpp index 31e69f6c5..3960ed571 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -16,6 +16,7 @@ #include "../lib/VCMI_Lib.h" #include "../CCallback.h" #include "../hch/CTownHandler.h" +#include "../hch/CDefObjInfoHandler.h" using namespace boost::assign; using namespace CSDL_Ext; #ifdef min @@ -360,6 +361,9 @@ void Graphics::loadHeroAnims() loadHeroAnim("AB01_.DEF", rotations, &Graphics::boatAnims); loadHeroAnim("AB02_.DEF", rotations, &Graphics::boatAnims); loadHeroAnim("AB03_.DEF", rotations, &Graphics::boatAnims); + VLC->dobjinfo->gobjs[8][0]->handler = boatAnims[0]; + VLC->dobjinfo->gobjs[8][1]->handler = boatAnims[1]; + VLC->dobjinfo->gobjs[8][2]->handler = boatAnims[2]; } void Graphics::loadHeroAnim( const std::string &name, const std::vector > &rotations, std::vector Graphics::*dst ) diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 23b289173..5d6e8e616 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -488,6 +488,28 @@ void OpenWindow::applyCl(CClient *cl) const CGDwelling *dw = dynamic_cast(cl->getObj(id1)); INTERFACE_CALL_IF_PRESENT(dw->tempOwner,showRecruitmentDialog, dw, 0); } + break; + case SHIPYARD_WINDOW: + { + const IShipyard *sy = IShipyard::castFrom(cl->getObj(id1)); + INTERFACE_CALL_IF_PRESENT(sy->o->tempOwner, showShipyardDialog, sy); + } + break; } } + +void NewObject::applyCl(CClient *cl) +{ + const CGObjectInstance *obj = cl->getObj(id); + //notify interfaces about move + for(std::map::iterator i=cl->playerint.begin();i!=cl->playerint.end();i++) + { + //TODO: check if any covered tile is visible + if(i->first >= PLAYER_LIMIT) continue; + if(GS(cl)->players[i->first].fogOfWarMap[obj->pos.x][obj->pos.y][obj->pos.z]) + { + i->second->newObject(obj); + } + } +} diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index dfd29371c..793adef02 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -9,6 +9,7 @@ #include "CSpellHandler.h" #include #include +#include #include #include #include "CTownHandler.h" @@ -20,6 +21,7 @@ #include "../lib/NetPacks.h" #include "../StartInfo.h" #include "../lib/map.h" +using namespace boost::assign; /* * CObjectHandler.cpp, part of VCMI engine @@ -291,6 +293,10 @@ void CGObjectInstance::giveDummyBonus(int heroID, ui8 duration) const cb->giveHeroBonus(&gbonus); } +void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const +{ +} + static int lowestSpeed(const CGHeroInstance * chi) { if(!chi->army.slots.size()) @@ -1222,6 +1228,7 @@ bool CGTownInstance::hasCapitol() const return (builtBuildings.find(13))!=builtBuildings.end(); } CGTownInstance::CGTownInstance() + :IShipyard(this) { builded=-1; destroyed=-1; @@ -1283,6 +1290,11 @@ int3 CGTownInstance::getSightCenter() const return pos - int3(2,0,0); } +void CGTownInstance::getOutOffsets( std::vector &offsets ) const +{ + offsets += int3(-1,3,0), int3(-3,3,0); +} + void CGVisitableOPH::onHeroVisit( const CGHeroInstance * h ) const { if(visitors.find(h->id)==visitors.end()) @@ -2498,6 +2510,7 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const { case 11: //buoy messageID = 21; + sound = soundBase::MORALE; gbonus.bonus.type = HeroBonus::MORALE; gbonus.bonus.val = +1; gbonus.bdescr << std::pair(6,94); @@ -3412,4 +3425,122 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const } } cb->showInfoDialog(&iw); +} + +void IShipyard::getBoatCost( std::vector &cost ) const +{ + cost.resize(RESOURCE_QUANTITY); + cost[0] = 10; + cost[6] = 1000; +} + +//bool IShipyard::validLocation() const +//{ +// std::vector offsets; +// getOutOffsets(offsets); +// +// TerrainTile *tile; +// for(int i = 0; i < offsets.size(); i++) +// if((tile = IObjectInterface::cb->getTile(o->pos + offsets[i])) && tile->tertype == TerrainTile::water) //tile is in the map and is water +// return true; +// return false; +//} + +int3 IShipyard::bestLocation() const +{ + std::vector offsets; + getOutOffsets(offsets); + + TerrainTile *tile; + for(int i = 0; i < offsets.size(); i++) + if((tile = IObjectInterface::cb->getTile(o->pos + offsets[i])) && tile->tertype == TerrainTile::water) //tile is in the map and is water + return o->pos + offsets[i]; + return int3(-1,-1,-1); +} + +IShipyard::IShipyard(const CGObjectInstance *O) + : o(O) +{ +} + +int IShipyard::state() const +{ + int3 tile = bestLocation(); + TerrainTile *t = IObjectInterface::cb->getTile(tile); + if(!t) + return 3; //no water + else if(!t->blockingObjects.size()) + return 0; //OK + else if(t->blockingObjects.front()->ID == 8) + return 1; //blocked with boat + else + return 2; //blocked +} + +IShipyard * IShipyard::castFrom( CGObjectInstance *obj ) +{ + if(obj->ID == TOWNI_TYPE) + { + return static_cast(obj); + } + else if(obj->ID == 87) + { + return static_cast(obj); + } + else + { + tlog1 << "Cannot cast to IShipyar object with ID " << obj->ID << std::endl; + return NULL; + } +} + +const IShipyard * IShipyard::castFrom( const CGObjectInstance *obj ) +{ + return castFrom(const_cast(obj)); +} + +CGShipyard::CGShipyard() + :IShipyard(this) +{ +} + +void CGShipyard::getOutOffsets( std::vector &offsets ) const +{ + offsets += int3(1,0,0), int3(-3,0,0), int3(1,1,0), int3(-3,1,0), int3(1,-1,0), int3(-3,-1,0), + int3(-2,-1,0), int3(0,-1,0), int3(-1,-1,0), int3(-2,1,0), int3(0,1,0), int3(-1,1,0); +} + +void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const +{ + if(tempOwner != h->tempOwner) + cb->setOwner(id, h->tempOwner); + + int s = state(); + if(s) + { + InfoWindow iw; + iw.player = tempOwner; + switch(s) + { + case 1: + iw.text.addTxt(MetaString::GENERAL_TXT, 51); + break; + case 2: + iw.text.addTxt(MetaString::ADVOB_TXT, 189); + break; + case 3: + tlog1 << "Shipyard without water!!! " << pos << "\t" << id << std::endl; + return; + } + + cb->showInfoDialog(&iw); + } + else + { + OpenWindow ow; + ow.id1 = id; + ow.id2 = h->id; + ow.window = OpenWindow::SHIPYARD_WINDOW; + cb->sendAndApply(&ow); + } } \ No newline at end of file diff --git a/hch/CObjectHandler.h b/hch/CObjectHandler.h index fdb505e25..d9e61107f 100644 --- a/hch/CObjectHandler.h +++ b/hch/CObjectHandler.h @@ -104,6 +104,22 @@ public: virtual void setProperty(ui8 what, ui32 val);//synchr }; +class DLL_EXPORT IShipyard +{ +public: + const CGObjectInstance *o; + + IShipyard(const CGObjectInstance *O); + void getBoatCost(std::vector &cost) const; + virtual void getOutOffsets(std::vector &offsets) const =0; //offsets to obj pos when we boat can be placed + //virtual bool validLocation() const; //returns true if there is a water tile near where boat can be placed + int3 bestLocation() const; //returns location when the boat should be placed + int state() const; //0 - can buid, 1 - there is already a boat at dest tile, 2 - dest tile is blocked, 3 - no water + + static const IShipyard *castFrom(const CGObjectInstance *obj); + static IShipyard *castFrom(CGObjectInstance *obj); +}; + class DLL_EXPORT CGObjectInstance : protected IObjectInterface { protected: @@ -139,6 +155,7 @@ public: virtual const std::string & getHoverText() const; ////////////////////////////////////////////////////////////////////////// void initObj(); + void onHeroVisit(const CGHeroInstance * h) const; void setProperty(ui8 what, ui32 val);//synchr virtual void setPropertyDer(ui8 what, ui32 val);//synchr @@ -309,7 +326,7 @@ public: void wantsFight(const CGHeroInstance *h, ui32 answer) const; }; -class DLL_EXPORT CGTownInstance : public CGDwelling +class DLL_EXPORT CGTownInstance : public CGDwelling, public IShipyard { public: CTown * town; @@ -357,6 +374,7 @@ public: int3 getSightCenter() const; //"center" tile from which the sight distance is calculated int getSightRadious() const; //returns sight distance + void getOutOffsets(std::vector &offsets) const; //offsets to obj pos when we boat can be placed ////////////////////////////////////////////////////////////////////////// @@ -777,7 +795,13 @@ public: } }; - +class CGShipyard : public CGObjectInstance, public IShipyard +{ +public: + void getOutOffsets(std::vector &offsets) const; //offsets to obj pos when we boat can be placed + CGShipyard(); + void onHeroVisit(const CGHeroInstance * h) const; +}; class DLL_EXPORT CObjectHandler { diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index bfc0c4742..0893f6c85 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -145,3 +145,10 @@ void IGameCallback::getAllowed(std::vector &out, int flags) if(flags & CArtifact::ART_RELIC) getAllowedArts(out,&CArtHandler::relics); } + +TerrainTile * IGameCallback::getTile( int3 pos ) +{ + if(!gs->map->isInTheMap(pos)) + return NULL; + return &gs->map->getTile(pos); +} \ No newline at end of file diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 96d1babbe..d9f5aa277 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -32,6 +32,7 @@ struct CPackForClient; class CArtHandler; class CArtifact; class CArmedInstance; +struct TerrainTile; class DLL_EXPORT IGameCallback { @@ -55,6 +56,7 @@ public: virtual bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact virtual void getAllowedArts(std::vector &out, std::vector CArtHandler::*arts); virtual void getAllowed(std::vector &out, int flags); //flags: bitfield uses EartClass + virtual TerrainTile * getTile(int3 pos); //do sth virtual void changeSpells(int hid, bool give, const std::set &spells)=0; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 2c08242d5..fd310526e 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -518,7 +518,7 @@ struct OpenWindow : public CPackForClient //517 OpenWindow(){type = 517;}; void applyCl(CClient *cl); - enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL}; + enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW}; ui8 window; ui32 id1, id2; @@ -528,6 +528,23 @@ struct OpenWindow : public CPackForClient //517 } }; +struct NewObject : public CPackForClient //518 +{ + NewObject(){type = 518;}; + void applyCl(CClient *cl); + DLL_EXPORT void applyGs(CGameState *gs); + + ui32 ID, subID; + int3 pos; + + int id; //used internally + + template void serialize(Handler &h, const int version) + { + h & ID & subID & pos; + } +}; + struct NewTurn : public CPackForClient //101 { DLL_EXPORT void applyGs(CGameState *gs); @@ -1118,6 +1135,19 @@ struct HireHero : public CPackForServer } }; +struct BuildBoat : public CPackForServer +{ + BuildBoat(){}; + si32 objid; //where player wants to buy a boat + + bool applyGh(CGameHandler *gh); + template void serialize(Handler &h, const int version) + { + h & objid; + } + +}; + struct QueryReply : public CPackForServer { QueryReply(){}; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index dc4f59e51..f05d79396 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -490,6 +490,32 @@ DLL_EXPORT void GiveHero::applyGs( CGameState *gs ) h->inTownGarrison = false; } +DLL_EXPORT void NewObject::applyGs( CGameState *gs ) +{ + + CGObjectInstance *o = NULL; + switch(ID) + { + case 8: + o = new CGBoat(); + break; + default: + o = new CGObjectInstance(); + break; + } + o->ID = ID; + o->subID = subID; + o->pos = pos; + o->defInfo = VLC->dobjinfo->gobjs[ID][subID]; + id = o->id = gs->map->objects.size(); + o->hoverName = VLC->generaltexth->names[ID]; + + gs->map->objects.push_back(o); + gs->map->addBlockVisTiles(o); + o->initObj(); + assert(o->defInfo); +} + DLL_EXPORT void NewTurn::applyGs( CGameState *gs ) { gs->day = day; diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index 145787615..64b22f6c7 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -45,6 +45,7 @@ void registerTypes1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); } @@ -96,6 +97,7 @@ void registerTypes2(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); @@ -120,6 +122,7 @@ void registerTypes3(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/map.cpp b/lib/map.cpp index 44883f9ed..a8ee626f9 100644 --- a/lib/map.cpp +++ b/lib/map.cpp @@ -1688,7 +1688,6 @@ void Mapa::readObjects( unsigned char * bufor, int &i) break; } case 42: //lighthouse - case 87: //shipyard case 220://mine (?) { nobj = new CGObjectInstance(); @@ -1881,6 +1880,12 @@ void Mapa::readObjects( unsigned char * bufor, int &i) nobj = new CGSirens(); break; } + case 87: //Shipyard + { + nobj = new CGShipyard(); + nobj->setOwner(readNormalNr(bufor,i)); i+=4; + break; + } case 214: //hero placeholder { i+=3; //TODO: handle it more properly @@ -1945,7 +1950,7 @@ void Mapa::readEvents( unsigned char * bufor, int &i ) } } -bool Mapa::isInTheMap( int3 pos ) +bool Mapa::isInTheMap( int3 pos ) const { if(pos.x<0 || pos.y<0 || pos.z<0 || pos.x >= width || pos.y >= height || pos.z > twoLevel) return false; @@ -2034,6 +2039,17 @@ TerrainTile & Mapa::getTile( int3 tile ) { return terrain[tile.x][tile.y][tile.z]; } + +const TerrainTile & Mapa::getTile( int3 tile ) const +{ + return terrain[tile.x][tile.y][tile.z]; +} + +bool Mapa::isWaterTile( int3 pos ) const +{ + return isInTheMap(pos) && getTile(pos).tertype == TerrainTile::water; +} + void CMapInfo::countPlayers() { playerAmnt=humenPlayers=0; diff --git a/lib/map.h b/lib/map.h index 277e9893c..b4e645281 100644 --- a/lib/map.h +++ b/lib/map.h @@ -335,8 +335,10 @@ struct DLL_EXPORT Mapa : public CMapHeader Mapa(); ~Mapa(); TerrainTile &getTile(int3 tile); + const TerrainTile &getTile(int3 tile) const; CGHeroInstance * getHero(int ID, int mode=0); - bool isInTheMap(int3 pos); + bool isInTheMap(int3 pos) const; + bool isWaterTile(int3 pos) const; //out-of-pos safe template void serialize(Handler &h, const int formatVersion) { h & static_cast(*this); diff --git a/mapHandler.cpp b/mapHandler.cpp index ef53338d4..c08d41139 100644 --- a/mapHandler.cpp +++ b/mapHandler.cpp @@ -720,13 +720,13 @@ void CMapHandler::terrainRect(int3 top_tile, unsigned char anim, std::vector< st for(int h=0; h < objects.size(); ++h) { + const CGObjectInstance *obj = objects[h].first; + ui8 color = obj->tempOwner; //checking if object has non-empty graphic on this tile - if(!objects[h].first->coveringAt(objects[h].first->pos.x - (top_tile.x + bx), top_tile.y + by - objects[h].first->pos.y + 5)) + if(!obj->coveringAt(obj->pos.x - (top_tile.x + bx), top_tile.y + by - obj->pos.y + 5)) continue; - //printing object SDL_Rect sr; - sr.x = srx; sr.y = sry; sr.w = sr.h = 32; @@ -735,73 +735,87 @@ void CMapHandler::terrainRect(int3 top_tile, unsigned char anim, std::vector< st pp.h = sr.h; pp.w = sr.w; - const CGHeroInstance * themp = (objects[h].first->ID != HEROI_TYPE + const CGHeroInstance * themp = (obj->ID != HEROI_TYPE ? NULL - : static_cast(objects[h].first)); + : static_cast(obj)); - if(themp && themp->moveDir) //it's hero + //print hero / boat and flag + if(themp && themp->moveDir && themp->type || obj->ID == 8) //it's hero or boat { - int imgVal = 8; - SDL_Surface * tb; - if(themp->type==NULL) - continue; - - //pick graphics of hero (or boat if hero is sailing) - std::vector & iv = (themp->boat) - ? graphics->boatAnims[themp->boat->subID]->ourImages - : graphics->heroAnims[themp->type->heroType]->ourImages; - - //pick appropriate flag set + const int IMGVAL = 8; //frames per group of movement animation + ui8 dir; + std::vector * iv = NULL; std::vector Graphics::*flg = NULL; - if(themp->boat) + SDL_Surface * tb; //surface to blitted + + if(themp) //hero { - switch (themp->boat->subID) + dir = themp->moveDir; + + //pick graphics of hero (or boat if hero is sailing) + iv = (themp->boat) + ? &graphics->boatAnims[themp->boat->subID]->ourImages + : &graphics->heroAnims[themp->type->heroType]->ourImages; + + //pick appropriate flag set + if(themp->boat) { - case 0: flg = &Graphics::flags1; break; - case 1: flg = &Graphics::flags2; break; - case 2: flg = &Graphics::flags3; break; - default: tlog1 << "Not supported boat subtype: " << themp->boat->subID << std::endl; + switch (themp->boat->subID) + { + case 0: flg = &Graphics::flags1; break; + case 1: flg = &Graphics::flags2; break; + case 2: flg = &Graphics::flags3; break; + default: tlog1 << "Not supported boat subtype: " << themp->boat->subID << std::endl; + } + } + else + { + flg = &Graphics::flags4; } } - else + else //boat { - flg = &Graphics::flags4; + const CGBoat *boat = static_cast(obj); + dir = boat->direction; + iv = &graphics->boatAnims[boat->subID]->ourImages; } - //print hero / boat and flag - if(!themp->isStanding) //hero is moving + + if(themp && !themp->isStanding) //hero is moving { size_t gg; - for(gg=0; ggsize(); ++gg) { - if(iv[gg].groupNumber==getHeroFrameNum(themp->moveDir, !themp->isStanding)) + if((*iv)[gg].groupNumber==getHeroFrameNum(dir, true)) { - tb = iv[gg+heroAnim%imgVal].bitmap; + tb = (*iv)[gg+heroAnim%IMGVAL].bitmap; break; } } CSDL_Ext::blit8bppAlphaTo24bpp(tb,&pp,extSurf,&sr); //printing flag - pp.y+=imgVal*2-32; + pp.y+=IMGVAL*2-32; sr.y-=16; - SDL_BlitSurface((graphics->*flg)[themp->getOwner()]->ourImages[gg+heroAnim%imgVal+35].bitmap, &pp, extSurf, &sr); + SDL_BlitSurface((graphics->*flg)[color]->ourImages[gg+heroAnim%IMGVAL+35].bitmap, &pp, extSurf, &sr); } - else //hero stands still + else //hero / boat stands still { size_t gg; - for(gg=0; gg < iv.size(); ++gg) + for(gg=0; gg < iv->size(); ++gg) { - if(iv[gg].groupNumber==getHeroFrameNum(themp->moveDir, !themp->isStanding)) + if((*iv)[gg].groupNumber==getHeroFrameNum(dir, false)) { - tb = iv[gg].bitmap; + tb = (*iv)[gg].bitmap; break; } } CSDL_Ext::blit8bppAlphaTo24bpp(tb,&pp,extSurf,&sr); //printing flag - if(themp->pos.x==top_tile.x+bx && themp->pos.y==top_tile.y+by) + if(flg + && obj->pos.x == top_tile.x + bx + && obj->pos.y == top_tile.y + by) { SDL_Rect bufr = sr; bufr.x-=2*32; @@ -809,19 +823,18 @@ void CMapHandler::terrainRect(int3 top_tile, unsigned char anim, std::vector< st bufr.h = 64; bufr.w = 96; if(bufr.x-extRect->x>-64) - SDL_BlitSurface((graphics->*flg)[themp->getOwner()]->ourImages[ getHeroFrameNum(themp->moveDir, !themp->isStanding) *8+(heroAnim/4)%imgVal].bitmap, NULL, extSurf, &bufr); + SDL_BlitSurface((graphics->*flg)[color]->ourImages[getHeroFrameNum(dir, false) *8+(heroAnim/4)%IMGVAL].bitmap, NULL, extSurf, &bufr); } } } - else //blit object + else //blit normal object { - const CGObjectInstance *obj = objects[h].first; const std::vector &ourImages = obj->defInfo->handler->ourImages; SDL_Surface *bitmap = ourImages[(anim+obj->animPhaseShift)%ourImages.size()].bitmap; //setting appropriate flag color - if(obj->tempOwner<8 || obj->tempOwner==255) - CSDL_Ext::setPlayerColor(bitmap, obj->tempOwner); + if(color < 8 || color==255) + CSDL_Ext::setPlayerColor(bitmap, color); CSDL_Ext::blit8bppAlphaTo24bpp(bitmap,&pp,extSurf,&sr); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5fa2f669d..c783d8a8e 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -662,7 +662,7 @@ void CGameHandler::newTurn() { NewTurn::Hero hth; hth.id = h->id; - hth.move = h->maxMovePoints(true); //TODO: check if hero is really on the land + hth.move = h->maxMovePoints(gs->map->getTile(h->getPosition(false)).tertype != TerrainTile::water); if(h->visitedTown && vstd::contains(h->visitedTown->builtBuildings,0)) //if hero starts turn in town with mage guild hth.mana = h->manaLimit(); //restore all mana @@ -2872,3 +2872,49 @@ void CGameHandler::objectVisited( const CGObjectInstance * obj, const CGHeroInst { obj->onHeroVisit(h); } + +bool CGameHandler::buildBoat( ui32 objid ) +{ + const IShipyard *obj = IShipyard::castFrom(getObj(objid)); + + if(obj->state()) + { + complain("Cannot build boat in this shipyard!"); + return false; + } + else if(obj->o->ID == TOWNI_TYPE + && !vstd::contains((static_cast(obj))->builtBuildings,6)) + { + complain("Cannot build boat in the town - no shipyard!"); + return false; + } + + //TODO use "real" cost via obj->getBoatCost + if(getResource(obj->o->tempOwner, 6) < 1000 || getResource(obj->o->tempOwner, 0) < 10) + { + complain("Not enough resources to build a boat!"); + return false; + } + + int3 tile = obj->bestLocation(); + if(!gs->map->isInTheMap(tile)) + { + complain("Cannot find appropriate tile for a boat!"); + return false; + } + + //take boat cost + SetResources sr; + sr.player = obj->o->tempOwner; + sr.res = gs->getPlayer(obj->o->tempOwner)->resources; + sr.res[0] -= 10; + sr.res[6] -= 1000; + sendAndApply(&sr); + + //create boat + NewObject no; + no.ID = 8; + no.subID = 1; + no.pos = tile + int3(1,0,0); + sendAndApply(&no); +} \ No newline at end of file diff --git a/server/CGameHandler.h b/server/CGameHandler.h index d5a1e74f2..9bc2fec68 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -144,6 +144,7 @@ public: bool makeCustomAction(BattleAction &ba); bool queryReply( ui32 qid, ui32 answer ); bool hireHero( ui32 tid, ui8 hid ); + bool buildBoat( ui32 objid ); bool setFormation( si32 hid, ui8 formation ); bool tradeResources( ui32 val, ui8 player, ui32 id1, ui32 id2 ); bool buyArtifact( ui32 hid, si32 aid ); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 1f8f02192..6d39fbc99 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -122,6 +122,12 @@ bool HireHero::applyGh( CGameHandler *gh ) return gh->hireHero(tid,hid); } +bool BuildBoat::applyGh( CGameHandler *gh ) +{ + ERROR_IF_NOT_OWNS(objid); + return gh->buildBoat(objid); +} + bool QueryReply::applyGh( CGameHandler *gh ) { //TODO - check if player matches the query