From 25bdcd3cab56476c9d4fc3ec48f2b992ab09c7a8 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Sat, 28 Apr 2012 19:40:27 +0000 Subject: [PATCH] - Fixed wrong creature teleported - Support for Sacrifice --- client/BattleInterface/CBattleInterface.cpp | 44 ++++++++++++++++----- client/BattleInterface/CBattleInterface.h | 3 +- config/spell_info.json | 2 +- lib/BattleAction.h | 4 +- lib/BattleState.cpp | 14 ++++--- lib/BattleState.h | 6 +-- server/CGameHandler.cpp | 17 ++++++-- server/CGameHandler.h | 3 +- 8 files changed, 68 insertions(+), 25 deletions(-) diff --git a/client/BattleInterface/CBattleInterface.cpp b/client/BattleInterface/CBattleInterface.cpp index 87715b06b..f5baba539 100644 --- a/client/BattleInterface/CBattleInterface.cpp +++ b/client/BattleInterface/CBattleInterface.cpp @@ -92,7 +92,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim) CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen) : queue(NULL), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0), - activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1), + activeStack(NULL), stackToActivate(NULL), selectedStack(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1), currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(NULL), sp(NULL), siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0), bfield(GameConstants::BFIELD_SIZE), givenCommand(NULL), myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL) @@ -1385,7 +1385,7 @@ void CBattleInterface::newRound(int number) } -void CBattleInterface::giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional) +void CBattleInterface::giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional, si32 selected) { const CStack *stack = curInt->cb->battleGetStackByID(stackID); if(!stack && action != BattleAction::HERO_SPELL && action != BattleAction::RETREAT && action != BattleAction::SURRENDER) @@ -1402,6 +1402,7 @@ void CBattleInterface::giveCommand(ui8 action, BattleHex tile, ui32 stackID, si3 ba->destinationTile = tile; ba->stackNumber = stackID; ba->additionalInfo = additional; + ba->selectedStack = selected; //some basic validations switch(action) @@ -1591,6 +1592,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) case Spells::RESURRECTION: case Spells::ANIMATE_DEAD: case Spells::DISPEL_HELPFUL_SPELLS: + case Spells::SACRIFICE: //TODO: animation upon killed stack for(std::set::const_iterator it = sc->affectedCres.begin(); it != sc->affectedCres.end(); ++it) { displayEffect(spell.mainEffectAnim, curInt->cb->battleGetStackByID(*it, false)->position); @@ -1600,10 +1602,10 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc ) case Spells::SUMMON_EARTH_ELEMENTAL: case Spells::SUMMON_WATER_ELEMENTAL: case Spells::SUMMON_AIR_ELEMENTAL: - case Spells::CLONE: //TODO: make it smarter? + case Spells::CLONE: case Spells::REMOVE_OBSTACLE: case Spells::CHAIN_LIGHTNING: - addNewAnim(new CDummyAnimation(this, 2)); + addNewAnim(new CDummyAnimation(this, 2)); //interface won't return until animation is played. TODO: make it smarter? break; } //switch(sc->id) @@ -2744,7 +2746,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) legalAction = true; break; case RISING_SPELL: - if (shere && shere->canBeHealed() && isCastingPossibleHere (sactive, shere, myNumber)) //TODO: at least one stack has to be raised by resurrection / animate dead + if (shere && shere->canBeHealed() && ourStack && isCastingPossibleHere (sactive, shere, myNumber)) //TODO: at least one stack has to be raised by resurrection / animate dead legalAction = true; break; case RANDOM_GENIE_SPELL: @@ -2771,13 +2773,17 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) else skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->spells[spellToCast->additionalInfo]); //TODO: explicitely save power, skill - if (curInt->cb->battleCanTeleportTo(activeStack, myNumber, skill)) + if (curInt->cb->battleCanTeleportTo(selectedStack, myNumber, skill)) legalAction = true; else notLegal = true; } break; - case SACRIFICE: //TODO + case SACRIFICE: //choose our living stack to sacrifice + if (shere && shere != selectedStack && ourStack && shere->alive()) + legalAction = true; + else + notLegal = true; break; case CATAPULT: if (isCatapultAttackable(myNumber)) @@ -2899,8 +2905,9 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s switch (sp->id) { - case Spells::TELEPORT: case Spells::SACRIFICE: + case Spells::TELEPORT: + selectedStack = shere; //remember firts target secondaryTarget = true; break; } @@ -2919,6 +2926,12 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) consoleMsg = CGI->generaltexth->allTexts[550]; isCastingPossible = true; break; + case SACRIFICE: + cursorFrame = ECursor::COMBAT_SACRIFICE; + consoleMsg = (boost::format(CGI->generaltexth->allTexts[549]) % shere->getName()).str(); //sacrifice the %s + spellToCast->selectedStack = shere->ID; //sacrificed creature is selected + isCastingPossible = true; + break; case HEAL: cursorFrame = ECursor::COMBAT_HEAL; consoleMsg = (boost::format(CGI->generaltexth->allTexts[419]) % shere->getName()).str(); //Apply first aid to the %s @@ -2970,6 +2983,9 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) case TELEPORT: consoleMsg = CGI->generaltexth->allTexts[24]; //Invalid Teleport Destination break; + case SACRIFICE: + consoleMsg = CGI->generaltexth->allTexts[543]; //choose army to sacrifice + break; default: cursorFrame = ECursor::COMBAT_BLOCKED; break; @@ -2992,6 +3008,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) { case Spells::TELEPORT: //don't cast spell yet, only select target possibleActions.push_back (TELEPORT); + spellToCast->selectedStack = selectedStack->ID; break; case Spells::SACRIFICE: possibleActions.push_back (SACRIFICE); @@ -3013,10 +3030,19 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) } else { - spellToCast->destinationTile = myNumber; + switch (sp->id) + { + case Spells::SACRIFICE: + spellToCast->destinationTile = selectedStack->position; //cast on first creature that will be resurrected + break; + default: + spellToCast->destinationTile = myNumber; + break; + } curInt->cb->battleMakeAction(spellToCast); endCastingSpell(); } + selectedStack = NULL; } }; } diff --git a/client/BattleInterface/CBattleInterface.h b/client/BattleInterface/CBattleInterface.h index 884e69465..659a846cc 100644 --- a/client/BattleInterface/CBattleInterface.h +++ b/client/BattleInterface/CBattleInterface.h @@ -116,6 +116,7 @@ private: ui8 animCount; const CStack * activeStack; //number of active stack; NULL - no one const CStack * stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; NULL of none + const CStack * selectedStack; //for Teleport / Sacrifice void activateStack(); //sets activeStack to stackToActivate etc. int mouseHoveredStack; //stack hovered by mouse; if -1 -> none time_t lastMouseHoveredStackAnimationTime; // time when last mouse hovered animation occurred @@ -157,7 +158,7 @@ private: std::list projectiles; //projectiles flying on battlefield void projectileShowHelper(SDL_Surface * to); //prints projectiles present on the battlefield - void giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional=-1); + void giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional=-1, si32 selectedStack = -1); bool isTileAttackable(const BattleHex & number) const; //returns true if tile 'number' is neighboring any tile from active stack's range or is one of these tiles bool isCatapultAttackable(BattleHex hex) const; //returns true if given tile can be attacked by catapult diff --git a/config/spell_info.json b/config/spell_info.json index 4327b1510..188a5134b 100644 --- a/config/spell_info.json +++ b/config/spell_info.json @@ -48,7 +48,7 @@ { "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": -1, "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" ] }, { "id": 42, "effect": -1, "anim": 40, "ranges": [ "0", "0", "0", "X" ] }, { "id": 43, "effect": 1, "anim": 4, "ranges": [ "0", "0", "0", "X" ] }, diff --git a/lib/BattleAction.h b/lib/BattleAction.h index 3e6f76b93..2e7c51dc6 100644 --- a/lib/BattleAction.h +++ b/lib/BattleAction.h @@ -28,9 +28,11 @@ struct DLL_LINKAGE BattleAction si8 actionType; //use ActionType enum for values BattleHex destinationTile; si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6 + si32 selectedStack; //spell subject for teleport / sacrifice + template void serialize(Handler &h, const int version) { - h & side & stackNumber & actionType & destinationTile & additionalInfo; + h & side & stackNumber & actionType & destinationTile & additionalInfo & selectedStack; } BattleAction(); diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index d4a1c4714..33480dced 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -1168,10 +1168,14 @@ ui32 BattleInfo::calculateSpellDmg( const CSpell * sp, const CGHeroInstance * ca return ret; } -ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const +ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack) const { bool resurrect = resurrects(spell->id); - int healedHealth = caster->getPrimSkillLevel(2) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)]; + int healedHealth; + if (spell->id == Spells::SACRIFICE && sacrificedStack) + healedHealth = (caster->getPrimSkillLevel(2) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count; + else + healedHealth = caster->getPrimSkillLevel(2) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)]; healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack); return std::min(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0)); } @@ -2127,16 +2131,16 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleCanCastThisSpellHere( int //get dead stack if we cast resurrection or animate dead const CStack *deadStack = getStackIf([dest](const CStack *s) { return !s->alive() && s->position == dest; }); - const CStack *aliveStack = getStackIf([dest](const CStack *s) { return s->alive() && s->position == dest; }); + const CStack *aliveStack = getStackIf([dest](const CStack *s) { return s->alive() && s->position == dest;}); if(spell->isRisingSpell()) { - if(!deadStack || aliveStack) + if(!deadStack && !aliveStack) return ESpellCastProblem::NO_APPROPRIATE_TARGET; if(spell->id == Spells::ANIMATE_DEAD && !deadStack->hasBonusOfType(Bonus::UNDEAD)) return ESpellCastProblem::NO_APPROPRIATE_TARGET; - if(deadStack->owner != player) //you can resurrect only your own stacks + if(deadStack && deadStack->owner != player) //you can resurrect only your own stacks //FIXME: it includes alive stacks as well return ESpellCastProblem::NO_APPROPRIATE_TARGET; } else if(spell->getTargetType() == CSpell::CREATURE || spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE) diff --git a/lib/BattleState.h b/lib/BattleState.h index 9a0980875..156d78c7f 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -52,7 +52,7 @@ struct DLL_LINKAGE AttackableTiles struct DLL_LINKAGE BattleInfo : public CBonusSystemNode { ui8 sides[2]; //sides[0] - attacker, sides[1] - defender - si32 round, activeStack; + si32 round, activeStack, selectedStack; ui8 siege; // = 0 ordinary battle = 1 a siege with a Fort = 2 a siege with a Citadel = 3 a siege with a Castle const CGTownInstance * town; //used during town siege - id of attacked town; -1 if not town defence int3 tile; //for background and bonuses @@ -71,7 +71,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode template void serialize(Handler &h, const int version) { - h & sides & round & activeStack & siege & town & tile & stacks & belligerents & obstacles + h & sides & round & activeStack & selectedStack & siege & town & tile & stacks & belligerents & obstacles & castSpells & si & battlefieldType; h & heroes; h & usedSpellsHistory & enchanterCounter; @@ -120,7 +120,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode std::pair getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex) ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const; ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell - ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const; + ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = NULL) const; ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //unused bool resurrects(TSpell spellid) const; //TODO: move it to spellHandler? diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 4032e87d4..e1eaab79f 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3509,7 +3509,8 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message ) } } -void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack) +void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, + int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack) { const CSpell *spell = VLC->spellh->spells[spellID]; @@ -3765,7 +3766,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest { BattleStackMoved bsm; bsm.distance = -1; - bsm.stack = gs->curB->activeStack; + bsm.stack = selectedStack; std::vector tiles; tiles.push_back(destination); bsm.tilesToMove = tiles; @@ -3776,6 +3777,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest case Spells::CURE: case Spells::RESURRECTION: case Spells::ANIMATE_DEAD: + case Spells::SACRIFICE: { int hpGained = 0; if (stack) @@ -3807,12 +3809,18 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest hi.healedHP = gs->curB->calculateHealedHP(spell, usedSpellPower, spellLvl, *it); } else - hi.healedHP = gs->curB->calculateHealedHP(caster, spell, *it); + hi.healedHP = gs->curB->calculateHealedHP(caster, spell, *it, gs->curB->getStack(selectedStack)); hi.lowLevelResurrection = spellLvl <= 1; shr.healedStacks.push_back(hi); } if(!shr.healedStacks.empty()) sendAndApply(&shr); + if (spellID == Spells::SACRIFICE) //remove victim + { + BattleStacksRemoved bsr; + bsr.stackIDs.insert (selectedStack); //somehow it works for teleport? + sendAndApply(&bsr); + } break; } case Spells::SUMMON_FIRE_ELEMENTAL: @@ -4013,7 +4021,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba ) StartAction start_action(ba); sendAndApply(&start_action); //start spell casting - handleSpellCasting (ba.additionalInfo, skill, ba.destinationTile, ba.side, h->tempOwner, h, secondHero, h->getPrimSkillLevel(2), ECastingMode::HERO_CASTING, NULL); + handleSpellCasting (ba.additionalInfo, skill, ba.destinationTile, ba.side, h->tempOwner, h, secondHero, h->getPrimSkillLevel(2), + ECastingMode::HERO_CASTING, NULL, ba.selectedStack); sendAndApply(&end_action); if( !gs->curB->getStack(gs->curB->activeStack, false)->alive() ) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index e1143705e..6057121cb 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -193,7 +193,8 @@ public: void playerMessage( ui8 player, const std::string &message); bool makeBattleAction(BattleAction &ba); - void handleSpellCasting(int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack); + void handleSpellCasting(int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, + int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack = -1); bool makeCustomAction(BattleAction &ba); void stackTurnTrigger(const CStack * stack); bool queryReply( ui32 qid, ui32 answer, ui8 player );