diff --git a/client/CCursorHandler.cpp b/client/CCursorHandler.cpp index 736d74c06..e5ffcb4c7 100644 --- a/client/CCursorHandler.cpp +++ b/client/CCursorHandler.cpp @@ -20,6 +20,7 @@ extern SDL_Surface * screen; void CCursorHandler::initCursor() { mode = number = xpos = ypos = 0; + dndImage = NULL; help = CSDL_Ext::newSurface(40,40); cursors.push_back(CDefHandler::giveDef("CRADVNTR.DEF")); cursors.push_back(CDefHandler::giveDef("CRCOMBAT.DEF")); @@ -34,6 +35,17 @@ void CCursorHandler::changeGraphic(const int & type, const int & no) number = no; } +/** + * Replaces the cursor with a custom image. + * + * @param image Image to replace cursor with or NULL to use the normal + * cursor. + */ +void CCursorHandler::dragAndDropCursor(SDL_Surface* image) +{ + dndImage = image; +} + void CCursorHandler::cursorMove(const int & x, const int & y) { xpos = x; @@ -45,7 +57,10 @@ void CCursorHandler::draw1() int x = xpos, y = ypos; shiftPos(x, y); SDL_BlitSurface(screen, &genRect(40,40,x,y), help, &genRect(40,40,0,0)); - blitAt(cursors[mode]->ourImages[number].bitmap,x,y); + if (dndImage) + blitAt(dndImage, x - dndImage->w/2, y - dndImage->h/2); + else + blitAt(cursors[mode]->ourImages[number].bitmap,x,y); } void CCursorHandler::draw2() { diff --git a/client/CCursorHandler.h b/client/CCursorHandler.h index 1196cac12..a558e1a5b 100644 --- a/client/CCursorHandler.h +++ b/client/CCursorHandler.h @@ -21,6 +21,7 @@ class CCursorHandler //handles cursor public: int mode, number; SDL_Surface * help; + SDL_Surface * dndImage; bool Show; std::vector cursors; @@ -28,6 +29,7 @@ public: void initCursor(); //inits cursorHandler - run only once, it's not memleak-proof (rev 1333) void cursorMove(const int & x, const int & y); //change cursor's positions to (x, y) void changeGraphic(const int & type, const int & no); //changes cursor graphic for type type (0 - adventure, 1 - combat, 2 - default, 3 - spellbook) and frame no (not used for type 3) + void dragAndDropCursor (SDL_Surface* image); // Replace cursor with a custom image. void draw1(); void shiftPos( int &x, int &y ); diff --git a/client/CHeroWindow.cpp b/client/CHeroWindow.cpp index 821617e61..bbba345ca 100644 --- a/client/CHeroWindow.cpp +++ b/client/CHeroWindow.cpp @@ -54,6 +54,7 @@ CHeroWindow::CHeroWindow(int playerColor): artifs = new CArtifactsOfHero(pos); artifs->commonInfo = new CArtifactsOfHero::SCommonPart; + artifs->commonInfo->participants.insert(artifs); artifs->commonInfo->activeArtPlace = NULL; garr = NULL; diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index 71610ad65..cc37c3a0f 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -27,6 +27,7 @@ #include "../mapHandler.h" #include "../timeHandler.h" #include +#include #include #include #include @@ -3130,18 +3131,21 @@ void CTavernWindow::HeroPortrait::clickLeft(tribool down, bool previousState) as(); //ClickableL::clickLeft(down); } + void CTavernWindow::HeroPortrait::activate() { activateLClick(); activateRClick(); activateHover(); } + void CTavernWindow::HeroPortrait::deactivate() { deactivateLClick(); deactivateRClick(); deactivateHover(); } + void CTavernWindow::HeroPortrait::clickRight(tribool down, bool previousState) { if(down) @@ -3150,6 +3154,7 @@ void CTavernWindow::HeroPortrait::clickRight(tribool down, bool previousState) GH.pushInt(new CRClickPopupInt(LOCPLINT->adventureInt->heroWindow,false)); } } + CTavernWindow::HeroPortrait::HeroPortrait(int &sel, int id, int x, int y, const CGHeroInstance *H) :as(sel,id) { @@ -3229,7 +3234,6 @@ void CInGameConsole::show(SDL_Surface * to) texts_mx.unlock(); } - void CInGameConsole::print(const std::string &txt) { texts_mx.lock(); @@ -3406,7 +3410,6 @@ CInGameConsole::CInGameConsole() : prevEntDisp(-1), defaultTimeout(10000), maxDi { } - void CGarrisonWindow::close() { GH.popIntTotally(this); @@ -3493,12 +3496,14 @@ CRClickPopupInt::~CRClickPopupInt() CGI->curh->show(); } - -CArtPlace::CArtPlace(const CArtifact* Art): active(false), clicked(false), ourArt(Art)/*, +CArtPlace::CArtPlace(const CArtifact* Art): active(false), clicked(false), marked(false), ourArt(Art)/*, spellBook(false), warMachine1(false), warMachine2(false), warMachine3(false), warMachine4(false),misc1(false), misc2(false), misc3(false), misc4(false), misc5(false), feet(false), lRing(false), rRing(false), torso(false), - lHand(false), rHand(false), neck(false), shoulders(false), head(false) */{} + lHand(false), rHand(false), neck(false), shoulders(false), head(false) */ +{ +} + void CArtPlace::activate() { if(!active) @@ -3508,11 +3513,13 @@ void CArtPlace::activate() active = true; } } + void CArtPlace::clickLeft(tribool down, bool previousState) { //LRClickableAreaWTextComp::clickLeft(down); - if(ourArt && !down && previousState) //we are spellbook + // If clicked on spellbook, open it only if no artifact is held at the moment. + if(ourArt && !down && previousState && !ourOwner->commonInfo->activeArtPlace) { if(ourArt->id == 0) { @@ -3520,7 +3527,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState) GH.pushInt(spellWindow); } } - if(!down && !clicked && previousState) //not clicked before + if(!down && (!clicked || ourOwner->commonInfo->srcSlotID >= 19) && previousState) //not clicked before { if(ourArt && ourArt->id == 0) return; //this is handled separately @@ -3534,20 +3541,20 @@ void CArtPlace::clickLeft(tribool down, bool previousState) LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); //The Catapult must be equipped. return; } - clicked = true; - ourOwner->commonInfo->activeArtPlace = this; + select(); } } else //perform artifact substitution { if (slotID >= 19) // Backpack slot - Remove active artifact and insert it into the designated position in backpack. { - const CArtifact *cur = ourOwner->commonInfo->activeArtPlace->ourArt; - assert(cur); //there is highlighted slot, it must contain an art + const CArtifact * cur = ourOwner->commonInfo->srcArtifact; + switch(cur->id) { case 3: //should not happen, catapult cannot be selected + assert(cur->id != 3); break; case 4: case 5: case 6: { @@ -3557,28 +3564,32 @@ void CArtPlace::clickLeft(tribool down, bool previousState) } break; default: + ourOwner->commonInfo->destAOH = ourOwner; + ourOwner->commonInfo->destSlotID = slotID; + LOCPLINT->cb->swapArtifacts( ourOwner->commonInfo->activeArtPlace->ourOwner->curHero, - ourOwner->commonInfo->activeArtPlace->slotID, + ourOwner->commonInfo->srcSlotID, ourOwner->curHero, slotID); + + ourOwner->commonInfo->activeArtPlace->deselect(); break; } } //check if swap is possible - else if(this->fitsHere(ourOwner->commonInfo->activeArtPlace->ourArt) && ourOwner->commonInfo->activeArtPlace->fitsHere(this->ourArt)) + else if(this->fitsHere(ourOwner->commonInfo->srcArtifact) && ourOwner->commonInfo->activeArtPlace->fitsHere(this->ourArt)) { - int srcSlot = ourOwner->commonInfo->activeArtPlace->slotID; - int destSlot = slotID; + ourOwner->commonInfo->destAOH = ourOwner; + ourOwner->commonInfo->destSlotID = slotID; LOCPLINT->cb->swapArtifacts( ourOwner->commonInfo->activeArtPlace->ourOwner->curHero, - srcSlot, + ourOwner->commonInfo->srcSlotID, ourOwner->curHero, - destSlot); + slotID); - ourOwner->commonInfo->activeArtPlace->clicked = false; - ourOwner->commonInfo->activeArtPlace = NULL; + ourOwner->commonInfo->activeArtPlace->deselect(); } } } @@ -3586,16 +3597,54 @@ void CArtPlace::clickLeft(tribool down, bool previousState) { if(ourArt && ourArt->id == 0) return; //this is handled separately - clicked = false; - ourOwner->commonInfo->activeArtPlace = NULL; + deselect(); } //ClickableL::clickLeft(down); } + void CArtPlace::clickRight(tribool down, bool previousState) { if(text.size()) //if there is no description, do nothing ;] LRClickableAreaWTextComp::clickRight(down, previousState); } + +/** + * Selects artifact slot so that the containing artifact looks like it's picked up. + */ +void CArtPlace::select () +{ + CGI->curh->dragAndDropCursor(graphics->artDefs->ourImages[ourArt->id].bitmap); + clicked = true; + ourOwner->markPossibleSlots(ourArt); + + ourOwner->commonInfo->activeArtPlace = this; + ourOwner->commonInfo->srcArtifact = ourArt; + ourOwner->commonInfo->srcSlotID = slotID; + ourOwner->commonInfo->srcAOH = ourOwner; + + if (slotID >= 19) { + // Updates backpack, possibly correcting the position. + ourOwner->scrollBackpack(-(slotID - 19 < ourOwner->backpackPos)); + } else { + ourOwner->eraseSlotData(this, slotID); + } +} + +/** + * Deselects the artifact slot. + */ +void CArtPlace::deselect () +{ + clicked = false; + CGI->curh->dragAndDropCursor(NULL); + ourOwner->unmarkSlots(); + ourOwner->commonInfo->activeArtPlace = NULL; + + // If a worn artifact is deselected, restore it's picture. + if (slotID < 19 && !ourOwner->commonInfo->destAOH) + ourOwner->setSlotData(this, slotID); +} + void CArtPlace::deactivate() { if(active) @@ -3605,31 +3654,37 @@ void CArtPlace::deactivate() LRClickableAreaWTextComp::deactivate(); } } + void CArtPlace::show(SDL_Surface *to) { - if(ourArt) + if(ourArt && (!clicked || slotID >= 19)) { blitAt(graphics->artDefs->ourImages[ourArt->id].bitmap, pos.x, pos.y, to); } - if(clicked && active) + if(marked && active) { - for(int i=0; i 18 && !(art->id >= 3 && art->id <= 6) // Anything can be placed in the backpack, except War Machines. + return true; + + // Anything can be placed in the backpack, except War Machines. + if(slotID > 18 && !(art->id >= 3 && art->id <= 6) || vstd::contains(art->possibleSlots,slotID)) { return true; @@ -3637,9 +3692,14 @@ bool CArtPlace::fitsHere(const CArtifact * art) return false; } + CArtPlace::~CArtPlace() { deactivate(); + + // Make sure a currently held artifact does not affect the outside. + if (clicked) + CGI->curh->dragAndDropCursor(NULL); } void LClickableArea::activate() @@ -3804,21 +3864,37 @@ void CArtifactsOfHero::show(SDL_Surface * to) void CArtifactsOfHero::setHero(const CGHeroInstance * hero) { - curHero = hero; - char bufor[400]; + // An update is made, rather than initialization. + if (curHero == hero) { + // Compensate backpack pos if an artifact is insertad before it. + if (commonInfo->destSlotID >= 19 && commonInfo->destAOH == this + && commonInfo->destSlotID - 19 < backpackPos) + { + backpackPos++; + } + } + commonInfo->srcAOH = NULL; + commonInfo->srcArtifact = NULL; + commonInfo->srcSlotID = 0; // Can be anything that's not in backpack range. + commonInfo->destAOH = NULL; + commonInfo->destSlotID = 0; + + curHero = hero; + backpackSize = curHero->artifacts.size(); + + // Remove any previously allocated slots. for(size_t g=0; g slotPos; - backpackPos = 0; + if (curHero->artifacts.size() > 0) + backpackPos %= curHero->artifacts.size(); + else + backpackPos = 0; slotPos += genRect(44,44,pos.x+509,pos.y+30), genRect(44,44,pos.x+567,pos.y+240), genRect(44,44,pos.x+509,pos.y+80), genRect(44,44,pos.x+383,pos.y+68), genRect(44,44,pos.x+564,pos.y+183), genRect(44,44,pos.x+509,pos.y+130), @@ -3828,63 +3904,36 @@ void CArtifactsOfHero::setHero(const CGHeroInstance * hero) genRect(44,44,pos.x+610,pos.y+76), genRect(44,44,pos.x+610,pos.y+122), genRect(44,44,pos.x+610,pos.y+310), genRect(44,44,pos.x+381,pos.y+296); + // Fill the slots for worn artifacts. for (int g = 0; g < 19 ; g++) { artWorn[g] = new CArtPlace(hero->getArt(g)); artWorn[g]->pos = slotPos[g]; - if(hero->getArt(g)) - artWorn[g]->text = hero->getArt(g)->Description(); artWorn[g]->ourOwner = this; + setSlotData(artWorn[g], g); } - for(size_t g=0; gslotID = g; - if(artWorn[g]->ourArt) - { - sprintf(bufor, CGI->generaltexth->heroscrn[1].c_str(), artWorn[g]->ourArt->Name().c_str()); - artWorn[g]->hoverText = std::string(bufor); - } - else - { - artWorn[g]->hoverText = CGI->generaltexth->allTexts[507]; - } - } - + // Fill the slots for the backpack. for(size_t s=0; s<5; ++s) { - CArtPlace * add; - if( s < curHero->artifacts.size() ) - { - add = new CArtPlace(&CGI->arth->artifacts[curHero->artifacts[(s+backpackPos) % curHero->artifacts.size() ]]); - sprintf(bufor, CGI->generaltexth->heroscrn[1].c_str(), add->ourArt->Name().c_str()); - add->hoverText = bufor; - } - else - { - add = new CArtPlace(NULL); - add->hoverText = CGI->generaltexth->allTexts[507]; - } + CArtPlace * add = new CArtPlace(NULL); + + add->ourOwner = this; add->pos.x = pos.x + 403 + 46*s; add->pos.y = pos.y + 365; add->pos.h = add->pos.w = 44; - if(sartifacts.size() && hero->artifacts[s]) - { - add->text = hero->getArt(19+s)->Description(); - } + + if (s < curHero->artifacts.size()) + setSlotData(add, 19 + (s + backpackPos)%curHero->artifacts.size()); else - { - add->text = std::string(); - } - add->ourOwner = this; - add->slotID = 19+s; + setSlotData(add, 19 + s); backpack.push_back(add); } commonInfo->activeArtPlace = NULL; //blocking scrolling if there is not enough artifacts to scroll - leftArtRoll->block(hero->artifacts.size()<6); - rightArtRoll->block(hero->artifacts.size()<6); + leftArtRoll->block(curHero->artifacts.size() <= backpack.size()); + rightArtRoll->block(curHero->artifacts.size() <= backpack.size()); } void CArtifactsOfHero::dispose() @@ -3907,25 +3956,94 @@ void CArtifactsOfHero::dispose() void CArtifactsOfHero::scrollBackpack(int dir) { - backpackPos += dir + curHero->artifacts.size(); - backpackPos %= curHero->artifacts.size(); - - for(size_t s=0; s<5 && sartifacts.size(); ++s) //set new data - { - CArtPlace *cur = backpack[s]; - cur->slotID = 19+((s+backpackPos)%curHero->artifacts.size()); - cur->ourArt = curHero->getArt(cur->slotID); - - if(cur->ourArt) - cur->text = cur->ourArt->Description(); - else - cur->text = std::string(); - - // Set hover text. - char bufor[400]; - sprintf(bufor, CGI->generaltexth->heroscrn[1].c_str(), cur->ourArt->Name().c_str()); - cur->hoverText = bufor; + backpackPos += dir; + if (backpackPos < 0) { // No guarantee of modulus behavior with negative operands. + do { + backpackPos += curHero->artifacts.size(); + } while (backpackPos < 0); + } else { + backpackPos %= curHero->artifacts.size(); } + + const int tempBackpackSize = curHero->artifacts.size() - (commonInfo->srcSlotID >= 19); + + //set new data + for (size_t s = 0; s < backpack.size(); ++s) { + int slotID = 19 + (s + backpackPos)%tempBackpackSize; + + // Don't show the held artifact, skip it. + if (commonInfo->srcAOH == this && commonInfo->srcSlotID >= 19 && slotID >= commonInfo->srcSlotID) + slotID++; + + if (s < tempBackpackSize) + setSlotData(backpack[s], slotID); + else + eraseSlotData(backpack[s], slotID); + } + + // Activate/deactivate sliders. + leftArtRoll->block(tempBackpackSize <= backpack.size()); + rightArtRoll->block(tempBackpackSize <= backpack.size()); +} + +/** + * Marks possible slots where a given artifact can be placed, except backpack. + * + * @param art Artifact checked against. + */ +void CArtifactsOfHero::markPossibleSlots (const CArtifact* art) +{ + for (std::set::iterator it = commonInfo->participants.begin(); + it != commonInfo->participants.end(); + ++it) + { + for (int i = 0; i < (*it)->artWorn.size(); i++) { + if ((*it)->artWorn[i]->fitsHere(art)) + (*it)->artWorn[i]->marked = true; + } + } +} + +/** + * Unamarks all slots. + */ +void CArtifactsOfHero::unmarkSlots () +{ + for (std::set::iterator it = commonInfo->participants.begin(); + it != commonInfo->participants.end(); + ++it) + { + for (int i = 0; i < (*it)->artWorn.size(); i++) { + (*it)->artWorn[i]->marked = false; + } + } +} + +/** + * Assigns an artifacts to an artifact place depending on it's new slot ID. + */ +void CArtifactsOfHero::setSlotData (CArtPlace* artPlace, int slotID) +{ + artPlace->slotID = slotID; + artPlace->ourArt = curHero->getArt(slotID); + + if (artPlace->ourArt) { + artPlace->text = artPlace->ourArt->Description(); + artPlace->hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1].c_str()) % artPlace->ourArt->Name().c_str()); + } else { + eraseSlotData(artPlace, slotID); + } +} + +/** + * Makes given artifact slot appear as empty with a certain slot ID. + */ +void CArtifactsOfHero::eraseSlotData (CArtPlace* artPlace, int slotID) +{ + artPlace->slotID = slotID; + artPlace->ourArt = NULL; + artPlace->text = std::string(); + artPlace->hoverText = CGI->generaltexth->allTexts[507]; } CArtifactsOfHero::CArtifactsOfHero(const SDL_Rect & position) : @@ -4053,6 +4171,7 @@ void CExchangeWindow::show(SDL_Surface * to) void CExchangeWindow::questlog(int whichHero) { + CGI->curh->dragAndDropCursor(NULL); } void CExchangeWindow::prepareBackground() @@ -4140,10 +4259,12 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2) : bg(NULL) artifs[0] = new CArtifactsOfHero(genRect(600, 800, pos.x + -334, pos.y + 150)); artifs[0]->commonInfo = new CArtifactsOfHero::SCommonPart; - artifs[0]->setHero(heroInst[0]); + artifs[0]->commonInfo->participants.insert(artifs[0]); artifs[0]->commonInfo->activeArtPlace = NULL; + artifs[0]->setHero(heroInst[0]); artifs[1] = new CArtifactsOfHero(genRect(600, 800, pos.x + 96, pos.y + 150)); artifs[1]->commonInfo = artifs[0]->commonInfo; + artifs[1]->commonInfo->participants.insert(artifs[1]); artifs[1]->setHero(heroInst[1]); diff --git a/client/GUIClasses.h b/client/GUIClasses.h index 3e453aa61..6b03b96e3 100644 --- a/client/GUIClasses.h +++ b/client/GUIClasses.h @@ -646,11 +646,14 @@ public: ui16 slotID; //0 head 1 shoulders 2 neck 3 right hand 4 left hand 5 torso 6 right ring 7 left ring 8 feet 9 misc. slot 1 10 misc. slot 2 11 misc. slot 3 12 misc. slot 4 13 ballista (war machine 1) 14 ammo cart (war machine 2) 15 first aid tent (war machine 3) 16 catapult 17 spell book 18 misc. slot 5 19+ backpack slots bool clicked; + bool marked; CArtifactsOfHero * ourOwner; const CArtifact * ourArt; CArtPlace(const CArtifact * Art); //c-tor void clickLeft(tribool down, bool previousState); void clickRight(tribool down, bool previousState); + void select (); + void deselect (); void activate(); void deactivate(); void show(SDL_Surface * to); @@ -663,6 +666,7 @@ class CArtifactsOfHero : public CIntObject { const CGHeroInstance * curHero; + size_t backpackSize; // Used to check differences in backpack sizes. std::vector artWorn; // 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 std::vector backpack; //hero's visible backpack (only 5 elements!) int backpackPos; //unmber of first art visible in backpack (in hero's vector) @@ -670,10 +674,15 @@ class CArtifactsOfHero : public CIntObject public: struct SCommonPart { + std::set participants; // Needed to mark slots. CArtPlace * activeArtPlace; + const CArtifact * srcArtifact; // Held artifact., technically superfluous right now. + const CArtifactsOfHero * srcAOH; // Following two needed to uniquely identify the source. + int srcSlotID; // + const CArtifactsOfHero * destAOH; // For swapping. (i.e. changing what is held) + int destSlotID; // Needed to determine what kind of action was last taken in setHero } * commonInfo; //when we have more than one CArtifactsOfHero in one window with exchange possibility, we use this (eg. in exchange window); to be provided externally - AdventureMapButton * leftArtRoll, * rightArtRoll; void activate(); @@ -682,7 +691,11 @@ public: void setHero(const CGHeroInstance * hero); void dispose(); //free resources not needed after closing windows and reset state - void scrollBackpack(int dir); //dir==-1 => to left; dir==-2 => to right + void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right + void markPossibleSlots (const CArtifact* art); + void unmarkSlots (); + void setSlotData (CArtPlace* artPlace, int slotID); + void eraseSlotData (CArtPlace* artPlace, int slotID); CArtifactsOfHero(const SDL_Rect & position); //c-tor ~CArtifactsOfHero(); //d-tor