From 60c90af39c94fbe354611c62621c50c9dfd3ec2c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 30 Jun 2010 19:27:35 +0000 Subject: [PATCH] #41 - Implemented Castle Gates #375, #376 - fixed recruit window #362, #377 - fixed town window --- CCallback.cpp | 7 +++ CCallback.h | 1 + client/CCastleInterface.cpp | 102 ++++++++++++++++++++---------------- client/GUIClasses.cpp | 32 ++++++----- client/GUIClasses.h | 4 +- lib/CGameState.cpp | 6 ++- lib/NetPacks.h | 15 ++++++ lib/RegisterTypes.cpp | 1 + server/CGameHandler.cpp | 24 +++++++++ server/CGameHandler.h | 1 + server/NetPacksServer.cpp | 7 +++ 11 files changed, 139 insertions(+), 61 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index 76f3d901a..365f3e7d7 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -46,6 +46,13 @@ template bool isType(CPack *pack) return pack->getType() == N; } +bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *where) +{ + CastleTeleportHero pack(who->id, where->id, 1); + sendRequest(&pack); + return true; +} + bool CCallback::moveHero(const CGHeroInstance *h, int3 dst) { MoveHero pack(dst,h->id); diff --git a/CCallback.h b/CCallback.h index fe2f86054..3e14939e7 100644 --- a/CCallback.h +++ b/CCallback.h @@ -211,6 +211,7 @@ protected: public: //commands bool moveHero(const CGHeroInstance *h, int3 dst); //dst must be free, neighbouring tile (this function can move hero only by one tile) + bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where); void selectionMade(int selection, int asker); int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2); int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2); //first goes to the second diff --git a/client/CCastleInterface.cpp b/client/CCastleInterface.cpp index e79910648..6a805b954 100644 --- a/client/CCastleInterface.cpp +++ b/client/CCastleInterface.cpp @@ -37,6 +37,19 @@ using namespace CSDL_Ext; * */ +int hordeToDwellingID(int bid)//helper, converts horde buiding ID into corresponding dwelling ID +{ + const CGTownInstance * t = LOCPLINT->castleInt->town; + switch (bid) + { + case 18: return t->town->hordeLvl[0] + 30; + case 19: return t->town->hordeLvl[0] + 37; + case 24: return t->town->hordeLvl[1] + 30; + case 25: return t->town->hordeLvl[1] + 37; + default: return bid; + } +} + CBuildingRect::CBuildingRect(Structure *Str) :moi(false), offset(0), str(Str) { @@ -154,7 +167,9 @@ void CBuildingRect::clickRight(tribool down, bool previousState) return; if( !CSDL_Ext::isTransparent(area, GH.current->motion.x-pos.x, GH.current->motion.y-pos.y) ) //inside building image { - CBuilding *bld = CGI->buildh->buildings[str->townID][str->ID]; + int bid = hordeToDwellingID(str->ID); + + CBuilding *bld = CGI->buildh->buildings[str->townID][bid]; assert(bld); CInfoPopup *vinya = new CInfoPopup(); @@ -162,13 +177,28 @@ void CBuildingRect::clickRight(tribool down, bool previousState) vinya->bitmap = CMessage::drawBoxTextBitmapSub (LOCPLINT->playerID, bld->Description(), - LOCPLINT->castleInt->bicons->ourImages[str->ID].bitmap, + LOCPLINT->castleInt->bicons->ourImages[bid].bitmap, bld->Name()); vinya->pos.x = screen->w/2 - vinya->bitmap->w/2; vinya->pos.y = screen->h/2 - vinya->bitmap->h/2; GH.pushInt(vinya); } } + +std::string getBuildingSubtitle(int tid, int bid)//hover text for building +{ + const CGTownInstance * t = LOCPLINT->castleInt->town; + bid = hordeToDwellingID(bid); + + if (bid<30)//non-dwellings - only buiding name + return CGI->buildh->buildings[tid][bid]->Name(); + else//dwellings - recruit %creature% + { + int creaID = t->creatures[(bid-30)%CREATURES_PER_TOWN].second.back();//taking last of available creatures + return CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures[creaID]->namePl; + } +} + void CBuildingRect::mouseMoved (const SDL_MouseMotionEvent & sEvent) { if(area && isItIn(&pos,sEvent.x, sEvent.y)) @@ -188,19 +218,13 @@ void CBuildingRect::mouseMoved (const SDL_MouseMotionEvent & sEvent) if((*LOCPLINT->castleInt->hBuild)<(*this)) //set if we are on top { LOCPLINT->castleInt->hBuild = this; - if(CGI->buildh->buildings[str->townID][str->ID] && CGI->buildh->buildings[str->townID][str->ID]->Name().length()) - GH.statusbar->print(CGI->buildh->buildings[str->townID][str->ID]->Name()); - else - GH.statusbar->print(str->name); + GH.statusbar->print(getBuildingSubtitle(str->townID, str->ID)); } } else //no building hovered { LOCPLINT->castleInt->hBuild = this; - if(CGI->buildh->buildings[str->townID][str->ID] && CGI->buildh->buildings[str->townID][str->ID]->Name().length()) - GH.statusbar->print(CGI->buildh->buildings[str->townID][str->ID]->Name()); - else - GH.statusbar->print(str->name); + GH.statusbar->print(getBuildingSubtitle(str->townID, str->ID)); } } } @@ -549,14 +573,7 @@ void CCastleInterface::splitF() void CCastleInterface::buildingClicked(int building) { tlog5<<"You've clicked on "<town->hordeLvl[0] + 30; - } - else if(building==24 || building==25) - { - building = town->town->hordeLvl[1] + 30; - } + building = hordeToDwellingID(building); const CBuilding *b = CGI->buildh->buildings[town->subID][building]; @@ -708,7 +725,7 @@ void CCastleInterface::buildingClicked(int building) { const CGTownInstance *t = Towns[i]; if (t->id != this->town->id && t->visitingHero == NULL && //another town, empty and this is - t->subID == 3 && vstd::contains(t->builtBuildings, 26))//inferno with castle gate + t->subID == 3 && vstd::contains(t->builtBuildings, 22))//inferno with castle gate { availableTowns.push_back(t->id);//add to the list } @@ -718,8 +735,7 @@ void CCastleInterface::buildingClicked(int building) LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[126], std::vector(), soundBase::sound_todo); break;//only visiting hero can use castle gates } - tlog4<<"Warning: implementation is unfinished\n"; - CPicture *titlePic = new CPicture (bicons->ourImages[building].bitmap, 0,0, false); + CPicture *titlePic = new CPicture (bicons->ourImages[building].bitmap, 0,0, false);//will be deleted by selection window GH.pushInt (new CObjectListWindow(availableTowns, titlePic, CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41], boost::bind (&CCastleInterface::castleTeleport, this, _1))); break; @@ -752,9 +768,9 @@ void CCastleInterface::buildingClicked(int building) void CCastleInterface::castleTeleport(int where) { - //TODO: send message to move hero, - //find a way to do this without new message type - //and update interface + const CGTownInstance * dest = dynamic_cast(CGI->state->map->objects[where]); + LOCPLINT->cb->teleportHero(town->visitingHero, dest); + close();//close this window, interface with new town will be called by town::onVisit } void CCastleInterface::defaultBuildingClicked(int building) @@ -1017,7 +1033,7 @@ void CCastleInterface::recreateBuildings() if(vstd::contains(town->builtBuildings,6)) { std::vector vobjs = LOCPLINT->cb->getVisitableObjs(town->bestLocation()); - if(vobjs.size() && (vobjs.front()->ID == 8 || vobjs.front()->ID == HEROI_TYPE)) //there is visitable obj at shipyard output tile and it's a boat or hero (on boat) + if(!vobjs.empty() && (vobjs.front()->ID == 8 || vobjs.front()->ID == HEROI_TYPE)) //there is visitable obj at shipyard output tile and it's a boat or hero (on boat) { Structure * st = CGI->townh->structures[town->subID][20]; buildings.push_back(new CBuildingRect(st)); @@ -1237,19 +1253,20 @@ void CCastleInterface::CCreaInfo::clickRight(tribool down, bool previousState) GH.pushInt(mess); } } + void CCastleInterface::CCreaInfo::show(SDL_Surface * to) { blitAt(graphics->smallImgs[crid],pos.x+8,pos.y,to); std::ostringstream oss; oss << '+' << LOCPLINT->castleInt->town->creatureGrowth((bid-30)%CREATURES_PER_TOWN); - CSDL_Ext::printAtMiddle(oss.str(),pos.x+24,pos.y+37,FONT_TINY,zwykly,to); + CSDL_Ext::printAtMiddle(oss.str(),pos.x+24,pos.y+40,FONT_SMALL,zwykly,to); } CCastleInterface::CTownInfo::~CTownInfo() { - if (pic) delete pic; } + CCastleInterface::CTownInfo::CTownInfo(int BID) { used = LCLICK | RCLICK | HOVER; @@ -1289,12 +1306,14 @@ void CCastleInterface::CTownInfo::hover(bool on) else GH.statusbar->clear(); } + void CCastleInterface::CTownInfo::clickLeft(tribool down, bool previousState) { if(previousState && (!down)) if (LOCPLINT->castleInt->town->builtBuildings.find(bid)!=LOCPLINT->castleInt->town->builtBuildings.end()) LOCPLINT->castleInt->buildingClicked(bid);//activate building } + void CCastleInterface::CTownInfo::clickRight(tribool down, bool previousState) { if(down) @@ -1314,13 +1333,14 @@ void CCastleInterface::CTownInfo::clickRight(tribool down, bool previousState) GH.pushInt(mess); } } + void CCastleInterface::CTownInfo::show(SDL_Surface * to) { if ( bid == 14 )//marketplace/income { std::ostringstream oss; oss << LOCPLINT->castleInt->town->dailyIncome(); - CSDL_Ext::printAtMiddle(oss.str(),pos.x+32,pos.y+32,FONT_SMALL,zwykly,to); + CSDL_Ext::printAtMiddle(oss.str(),pos.x+33,pos.y+34,FONT_SMALL,zwykly,to); } else if ( bid == 6 )//no fort blitAt(pic->ourImages[3].bitmap,pos.x,pos.y,to); @@ -1347,7 +1367,7 @@ CRecruitmentWindow * CCastleInterface::showRecruitmentWindow( int building ) // crs.push_back(std::make_pair((int)cres[i],amount)); //CRecruitmentWindow *rw = new CRecruitmentWindow(crs,boost::bind(&CCallback::recruitCreatures,LOCPLINT->cb,town,_1,_2)); - CRecruitmentWindow *rw = new CRecruitmentWindow(town, level, town, boost::bind(&CCallback::recruitCreatures,LOCPLINT->cb,town,_1,_2)); + CRecruitmentWindow *rw = new CRecruitmentWindow(town, level, town, boost::bind(&CCallback::recruitCreatures,LOCPLINT->cb,town,_1,_2), -87); GH.pushInt(rw); return rw; } @@ -1519,33 +1539,27 @@ CHallInterface::CHallInterface(CCastleInterface * owner) for(size_t j=0; jtown->builtBuildings,bid)) + //if building not build or this is unupgraded horde + if(!vstd::contains(owner->town->builtBuildings,bid) || bid==18 || bid == 24) { int x = 34 + 194*j, y = 37 + 104*i, ID = bid; + + if (( bid == 18 && vstd::contains(owner->town->builtBuildings, owner->town->town->hordeLvl[0]+37)) + || ( bid == 24 && vstd::contains(owner->town->builtBuildings, owner->town->town->hordeLvl[1]+37))) + continue;//we have upgraded dwelling, horde description should be for upgraded creatures + if(boxList[i].size() == 2) //only two boxes in this row x+=194; else if(boxList[i].size() == 3) //only three boxes in this row x+=97; boxes[i].push_back(new CBuildingBox(bid,pos.x+x,pos.y+y)); - - //if this is horde dwelling for upgraded creatures and we already have one for basic creatures - if((bid == 25 && vstd::contains(owner->town->builtBuildings,24)) - || (bid == 19 && vstd::contains(owner->town->builtBuildings,18)) - ) - { - boxes[i].back()->state = 4; //already built - } - else - { - boxes[i].back()->state = LOCPLINT->cb->canBuildStructure(owner->town,ID); - } - + boxes[i].back()->state = LOCPLINT->cb->canBuildStructure(owner->town,ID); break; } } diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 101acd8ae..0d193f504 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -1931,21 +1931,21 @@ void CRecruitmentWindow::show(SDL_Surface * to) char pom[15]; SDL_itoa(creatures[which].amount-slider->value,pom,10); //available - printAtMiddle(pom,pos.x+205,pos.y+254,FONT_SMALL,zwykly,to); + printAtMiddle(pom,pos.x+205,pos.y+253,FONT_SMALL,zwykly,to); SDL_itoa(slider->value,pom,10); //recruit - printAtMiddle(pom,pos.x+279,pos.y+254,FONT_SMALL,zwykly,to); + printAtMiddle(pom,pos.x+279,pos.y+253,FONT_SMALL,zwykly,to); printAtMiddle(CGI->generaltexth->allTexts[16] + " " + CGI->creh->creatures[creatures[which].ID]->namePl,pos.x+243,pos.y+32,FONT_BIG,tytulowy,to); //eg "Recruit Dragon flies" - int curx = pos.x+115-creatures[which].res.size()*16; - for(int i=0;i=0; i--)// decrement used to make gold displayed as first res { blitAt(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx,pos.y+243,to); blitAt(graphics->resources32->ourImages[creatures[which].res[i].first].bitmap,curx+258,pos.y+243,to); SDL_itoa(creatures[which].res[i].second,pom,10); - printAtMiddle(pom,curx+14,pos.y+288,FONT_SMALL,zwykly,to); + printAtMiddle(pom,curx+15,pos.y+287,FONT_SMALL,zwykly,to); SDL_itoa(creatures[which].res[i].second * slider->value,pom,10); - printAtMiddle(pom,curx+12+258,pos.y+286,FONT_SMALL,zwykly,to); - curx+=32; + printAtMiddle(pom,curx+15+258,pos.y+287,FONT_SMALL,zwykly,to); + curx+=32+16;//size of bitmap + distance between them } curx = pos.x + 192 + CREATURE_WIDTH - (CREATURE_WIDTH*creatures.size()/2) - (SPACE_BETWEEN*(creatures.size()-1)/2); @@ -1959,7 +1959,7 @@ void CRecruitmentWindow::show(SDL_Surface * to) bar->show(to); } -CRecruitmentWindow::CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const boost::function &Recruit) +CRecruitmentWindow::CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const boost::function &Recruit, int y_offset) :recruit(Recruit), dwelling(Dwelling), dst(Dst), level(Level) { which = 0; @@ -1969,7 +1969,7 @@ CRecruitmentWindow::CRecruitmentWindow(const CGDwelling *Dwelling, int Level, co SDL_SetColorKey(bitmap,SDL_SRCCOLORKEY,SDL_MapRGB(bitmap->format,0,255,255)); SDL_FreeSurface(hhlp); pos.x = screen->w/2 - bitmap->w/2; - pos.y = screen->h/2 - bitmap->h/2; + pos.y = screen->h/2 - bitmap->h/2+y_offset; pos.w = bitmap->w; pos.h = bitmap->h; bar = new CStatusBar(pos.x+8, pos.y+370, "APHLFTRT.bmp", 471); @@ -1980,10 +1980,10 @@ CRecruitmentWindow::CRecruitmentWindow(const CGDwelling *Dwelling, int Level, co initCres(); - printAtMiddle(CGI->generaltexth->allTexts[346],113,233,FONT_SMALL,zwykly,bitmap); //cost per troop t + printAtMiddle(CGI->generaltexth->allTexts[346],113,232,FONT_SMALL,zwykly,bitmap); //cost per troop t printAtMiddle(CGI->generaltexth->allTexts[465],205,233,FONT_SMALL,zwykly,bitmap); //available t printAtMiddle(CGI->generaltexth->allTexts[16],279,233,FONT_SMALL,zwykly,bitmap); //recruit t - printAtMiddle(CGI->generaltexth->allTexts[466],373,233,FONT_SMALL,zwykly,bitmap); //total cost t + printAtMiddle(CGI->generaltexth->allTexts[466],371,232,FONT_SMALL,zwykly,bitmap); //total cost t drawBorder(bitmap,172,222,67,42,int3(239,215,123)); drawBorder(bitmap,246,222,67,42,int3(239,215,123)); drawBorder(bitmap,64,222,99,76,int3(239,215,123)); @@ -2618,8 +2618,10 @@ CObjectListWindow::~CObjectListWindow() void CObjectListWindow::elementSelected() { - onSelect(items[slider->value]); - GH.popIntTotally(this); + boost::function toCall = onSelect;//save + int where = items[slider->value]; //required variables + GH.popIntTotally(this);//then destroy window + toCall(where);//and send selected object } void CObjectListWindow::moveList(int which) @@ -2671,9 +2673,11 @@ void CObjectListWindow::keyPressed (const SDL_KeyboardEvent & key) default: return; } + if (sel<-1)//nothing was selected & list was moved up + return; if (sel<0)//start of list reached sel = 0; - else if ( sel >= slider->amount )//end of list reached + if ( sel >= slider->amount )//end of list reached sel = slider->amount-1; if ( sel >= items.size() ) sel = items.size()-1; diff --git a/client/GUIClasses.h b/client/GUIClasses.h index 1b343eb80..94f4bbb64 100644 --- a/client/GUIClasses.h +++ b/client/GUIClasses.h @@ -424,7 +424,7 @@ public: class CRecruitmentWindow : public CIntObject { public: - static const int SPACE_BETWEEN = 8; + static const int SPACE_BETWEEN = 18; static const int CREATURE_WIDTH = 102; static const int TOTAL_CREATURE_WIDTH = SPACE_BETWEEN + CREATURE_WIDTH; @@ -461,7 +461,7 @@ public: void showAll(SDL_Surface * to){show(to);}; void cleanCres(); void initCres(); - CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const boost::function & Recruit); //creatures - pairs //c-tor + CRecruitmentWindow(const CGDwelling *Dwelling, int Level, const CArmedInstance *Dst, const boost::function & Recruit, int y_offset = 0); //creatures - pairs //c-tor ~CRecruitmentWindow(); //d-tor }; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 036f9e3ba..e9142f2b0 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -1483,7 +1483,11 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed ) vti->builtBuildings.insert(30); if(ran()%2) vti->builtBuildings.insert(31); - }//init hordes + } + if (vstd::contains(vti->builtBuildings,(6)) && vti->state()==3) + vti->builtBuildings.erase(6);//if we have harbor without water - erase it (this is H3 behaviour) + + //init hordes for (int i = 0; ibuiltBuildings,(-31-i))) //if we have horde for this level { diff --git a/lib/NetPacks.h b/lib/NetPacks.h index e38eb064c..a0615dcc7 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1238,6 +1238,21 @@ struct MoveHero : public CPackForServer } }; +struct CastleTeleportHero : public CPackForServer +{ + CastleTeleportHero(){}; + CastleTeleportHero(const si32 HID, si32 Dest, ui8 Source ) : dest(Dest), hid(HID), source(Source){}; + si32 dest; + si32 hid; + si8 source;//who give teleporting, 1=castle gate + + bool applyGh(CGameHandler *gh); + template void serialize(Handler &h, const int version) + { + h & dest & hid; + } +}; + struct ArrangeStacks : public CPackForServer { ArrangeStacks(){}; diff --git a/lib/RegisterTypes.cpp b/lib/RegisterTypes.cpp index 7ef713749..988797ee6 100644 --- a/lib/RegisterTypes.cpp +++ b/lib/RegisterTypes.cpp @@ -158,6 +158,7 @@ void registerTypes3(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index f84739888..c686d1248 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1807,6 +1807,30 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255* return true; } } + +bool CGameHandler::teleportHero(si32 hid, si32 dstid, ui8 source, ui8 asker/* = 255*/) +{ + const CGHeroInstance *h = getHero(hid); + const CGTownInstance *t = getTown(dstid); + + if ( !h || !t || h->getOwner() != gs->currentPlayer ) + tlog1<<"Invalid call to teleportHero!"; + + const CGTownInstance *from = h->visitedTown; + if((h->getOwner() != t->getOwner()) + && complain("Cannot teleport hero to another player") + || (!from || from->subID!=3 || !vstd::contains(from->builtBuildings, 22)) + && complain("Hero must be in town with Castle gate for teleporting") + || (t->subID!=3 || !vstd::contains(t->builtBuildings, 22)) + && complain("Cannot teleport hero to town without Castle gate in it")) + return false; + int3 pos = t->visitablePos(); + pos.x++;//FIXME: get correct visitable position + stopHeroVisitCastle(from->id, hid); + moveHero(hid,pos,1); + heroVisitCastle(dstid, hid); +} + void CGameHandler::setOwner(int objid, ui8 owner) { ui8 oldOwner = getOwner(objid); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 15195cec1..1d58f89be 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -144,6 +144,7 @@ public: void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, boost::function cb = 0, bool creatureBank = false); //if any of armies is hero, hero will be used void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, boost::function cb = 0, bool creatureBank = false); //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function cb); //for hero<=>neutral army void setAmount(int objid, ui32 val); + bool teleportHero(si32 hid, si32 dstid, ui8 source, ui8 asker = 255); bool moveHero(si32 hid, int3 dst, ui8 instant, ui8 asker = 255); bool tryAttackingGuard(const int3 &guardPos, const CGHeroInstance * h); void visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index af802e6e2..9f9f2df66 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -59,6 +59,13 @@ bool MoveHero::applyGh( CGameHandler *gh ) return gh->moveHero(hid,dest,0,gh->getPlayerAt(c)); } +bool CastleTeleportHero::applyGh( CGameHandler *gh ) +{ + ERROR_IF_NOT_OWNS(hid); + + return gh->teleportHero(hid,dest,source,gh->getPlayerAt(c)); +} + bool ArrangeStacks::applyGh( CGameHandler *gh ) { //checks for owning in the gh func