1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-17 00:07:41 +02:00

Implemented Summon Boat spell.

This commit is contained in:
Michał W. Urbańczyk
2010-03-10 23:16:30 +00:00
parent 658a560698
commit ad841f7fea
14 changed files with 250 additions and 37 deletions

View File

@ -121,7 +121,7 @@ int CCallback::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) co
return gs->curB->getSpellCost(sp, caster); return gs->curB->getSpellCost(sp, caster);
//if there is no battle //if there is no battle
return sp->costs[caster->getSpellSchoolLevel(sp)]; return caster->getSpellCost(sp);
} }
int CCallback::estimateSpellDamage(const CSpell * sp) const int CCallback::estimateSpellDamage(const CSpell * sp) const
@ -954,6 +954,15 @@ si8 CCallback::battleGetStackLuck( int stackID )
return gs->curB->Luck( gs->curB->getStack(stackID) ); return gs->curB->Luck( gs->curB->getStack(stackID) );
} }
void CCallback::castSpell(const CGHeroInstance *hero, int spellID, const int3 &pos)
{
CastAdvSpell cas;
cas.hid = hero->id;
cas.sid = spellID;
cas.pos = pos;
sendRequest(&cas);
}
InfoAboutTown::InfoAboutTown() InfoAboutTown::InfoAboutTown()
{ {
tType = NULL; tType = NULL;

View File

@ -128,6 +128,7 @@ public:
virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1) =0; virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1) =0;
virtual void recalculatePaths()=0; //updates main, client pathfinder info (should be called when moving hero is over) virtual void recalculatePaths()=0; //updates main, client pathfinder info (should be called when moving hero is over)
virtual void dig(const CGObjectInstance *hero)=0; virtual void dig(const CGObjectInstance *hero)=0;
virtual void castSpell(const CGHeroInstance *hero, int spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell
//map //map
virtual std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos)const =0; virtual std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos)const =0;
@ -227,6 +228,7 @@ public:
void sendMessage(const std::string &mess); void sendMessage(const std::string &mess);
void buildBoat(const IShipyard *obj); void buildBoat(const IShipyard *obj);
void dig(const CGObjectInstance *hero); void dig(const CGObjectInstance *hero);
void castSpell(const CGHeroInstance *hero, int spellID, const int3 &pos = int3(-1, -1, -1));
//get info //get info
bool verifyPath(CPath * path, bool blockSea) const; bool verifyPath(CPath * path, bool blockSea) const;

View File

@ -668,7 +668,8 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
{ {
if(!down && mySpell!=-1) if(!down && mySpell!=-1)
{ {
int spellCost = owner->myInt->cb->getSpellCost(&CGI->spellh->spells[mySpell], owner->myHero); const CSpell *sp = &CGI->spellh->spells[mySpell];
int spellCost = owner->myInt->cb->getSpellCost(sp, owner->myHero);
//we will cast a spell //we will cast a spell
if(owner->myInt->battleInt && owner->myInt->cb->battleCanCastSpell() && spellCost <= owner->myHero->mana) //if battle window is open if(owner->myInt->battleInt && owner->myInt->cb->battleCanCastSpell() && spellCost <= owner->myHero->mana) //if battle window is open
{ {
@ -676,15 +677,35 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
owner->fexitb(); owner->fexitb();
owner->myInt->battleInt->castThisSpell(spell); owner->myInt->battleInt->castThisSpell(spell);
} }
else else //adventure spell
{ {
//insufficient mana //insufficient mana
if(spellCost > owner->myHero->mana) if(spellCost > owner->myHero->mana)
{ {
std::vector<SComponent*> comps;
char msgBuf[500]; char msgBuf[500];
sprintf(msgBuf, CGI->generaltexth->allTexts[206].c_str(), spellCost, owner->myHero->mana); sprintf(msgBuf, CGI->generaltexth->allTexts[206].c_str(), spellCost, owner->myHero->mana);
owner->myInt->showInfoDialog(std::string(msgBuf), comps); owner->myInt->showInfoDialog(std::string(msgBuf));
}
else
{
int spell = mySpell;
const CGHeroInstance *h = owner->myHero;
owner->fexitb();
switch(spell)
{
case 0: //summon boat
{
int3 pos = h->bestLocation();
if(pos.x < 0)
{
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[334]); //There is no place to put the boat.
return;
}
}
}
LOCPLINT->cb->castSpell(h, spell);
} }
} }
} }

View File

@ -511,5 +511,11 @@ void CClient::handlePack( CPack * pack )
delete pack; delete pack;
} }
void CClient::updatePaths()
{
const CGHeroInstance *h = getHero(getSelectedHero());
if (h)//if we have selected hero...
gs->calculatePaths(h, *pathInfo);
}
template void CClient::serialize( CISer<CLoadFile> &h, const int version ); template void CClient::serialize( CISer<CLoadFile> &h, const int version );
template void CClient::serialize( COSer<CSaveFile> &h, const int version ); template void CClient::serialize( COSer<CSaveFile> &h, const int version );

View File

@ -113,6 +113,7 @@ public:
void waitForServer(); void waitForServer();
CPack * retreivePack(); //gets from server next pack (allocates it with new) CPack * retreivePack(); //gets from server next pack (allocates it with new)
void handlePack( CPack * pack ); //applies the given pack and deletes it void handlePack( CPack * pack ); //applies the given pack and deletes it
void updatePaths();
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -107,8 +107,7 @@ void FoWChange::applyCl( CClient *cl )
else else
cl->playerint[player]->tileHidden(tiles); cl->playerint[player]->tileHidden(tiles);
if (cl->IGameCallback::getSelectedHero(player))//if we have selected hero... cl->updatePaths();
GS(cl)->calculatePaths(cl->IGameCallback::getSelectedHero(player), *cl->pathInfo);
} }
void SetAvailableHeroes::applyCl( CClient *cl ) void SetAvailableHeroes::applyCl( CClient *cl )
@ -146,6 +145,8 @@ void ChangeObjPos::applyCl( CClient *cl )
CGObjectInstance *obj = GS(cl)->map->objects[objid]; CGObjectInstance *obj = GS(cl)->map->objects[objid];
if(flags & 1) if(flags & 1)
CGI->mh->printObject(obj); CGI->mh->printObject(obj);
cl->updatePaths();
} }
void PlayerEndsGame::applyCl( CClient *cl ) void PlayerEndsGame::applyCl( CClient *cl )

View File

@ -47,7 +47,6 @@ BankConfig CGPyramid::pyramidConfig;
ui8 CGObelisk::obeliskCount; //how many obelisks are on map ui8 CGObelisk::obeliskCount; //how many obelisks are on map
std::map<ui8, ui8> CGObelisk::visited; //map: color_id => how many obelisks has been visited std::map<ui8, ui8> CGObelisk::visited; //map: color_id => how many obelisks has been visited
void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const
{}; {};
@ -687,6 +686,7 @@ int CGHeroInstance::getSpellSecLevel(int spell) const
} }
CGHeroInstance::CGHeroInstance() CGHeroInstance::CGHeroInstance()
: IBoatGenerator(this)
{ {
ID = HEROI_TYPE; ID = HEROI_TYPE;
tacticFormationEnabled = inTownGarrison = false; tacticFormationEnabled = inTownGarrison = false;
@ -1226,6 +1226,34 @@ const BonusList * CGHeroInstance::ownerBonuses() const
return &p->bonuses; return &p->bonuses;
} }
int CGHeroInstance::getBoatType() const
{
int alignment = type->heroType / 6;
switch(alignment)
{
case 0:
return 1; //good
case 1:
return 0; //evil
case 2:
return 2;
default:
assert(0);
}
}
void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
{
static int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0), int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };
for (size_t i = 0; i < ARRAY_COUNT(dirs); i++)
offsets += dirs[i];
}
int CGHeroInstance::getSpellCost(const CSpell *sp) const
{
return sp->costs[getSpellSchoolLevel(sp)];
}
void CGDwelling::initObj() void CGDwelling::initObj()
{ {
switch(ID) switch(ID)
@ -5308,13 +5336,6 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
cb->showInfoDialog(&iw); cb->showInfoDialog(&iw);
} }
void IShipyard::getBoatCost( std::vector<si32> &cost ) const
{
cost.resize(RESOURCE_QUANTITY);
cost[0] = 10;
cost[6] = 1000;
}
//bool IShipyard::validLocation() const //bool IShipyard::validLocation() const
//{ //{
// std::vector<int3> offsets; // std::vector<int3> offsets;
@ -5327,7 +5348,7 @@ void IShipyard::getBoatCost( std::vector<si32> &cost ) const
// return false; // return false;
//} //}
int3 IShipyard::bestLocation() const int3 IBoatGenerator::bestLocation() const
{ {
std::vector<int3> offsets; std::vector<int3> offsets;
getOutOffsets(offsets); getOutOffsets(offsets);
@ -5339,12 +5360,7 @@ int3 IShipyard::bestLocation() const
return int3(-1,-1,-1); return int3(-1,-1,-1);
} }
IShipyard::IShipyard(const CGObjectInstance *O) int IBoatGenerator::state() const
: o(O)
{
}
int IShipyard::state() const
{ {
int3 tile = bestLocation(); int3 tile = bestLocation();
TerrainTile *t = IObjectInterface::cb->getTile(tile); TerrainTile *t = IObjectInterface::cb->getTile(tile);
@ -5358,6 +5374,30 @@ int IShipyard::state() const
return 2; //blocked return 2; //blocked
} }
int IBoatGenerator::getBoatType() const
{
//We make good ships by default
return 1;
}
IBoatGenerator::IBoatGenerator(const CGObjectInstance *O)
: o(O)
{
}
void IShipyard::getBoatCost( std::vector<si32> &cost ) const
{
cost.resize(RESOURCE_QUANTITY);
cost[0] = 10;
cost[6] = 1000;
}
IShipyard::IShipyard(const CGObjectInstance *O)
: IBoatGenerator(O)
{
}
IShipyard * IShipyard::castFrom( CGObjectInstance *obj ) IShipyard * IShipyard::castFrom( CGObjectInstance *obj )
{ {
if(obj->ID == TOWNI_TYPE) if(obj->ID == TOWNI_TYPE)
@ -5380,12 +5420,6 @@ const IShipyard * IShipyard::castFrom( const CGObjectInstance *obj )
return castFrom(const_cast<CGObjectInstance*>(obj)); return castFrom(const_cast<CGObjectInstance*>(obj));
} }
int IShipyard::getBoatType() const
{
//We make good ships by default
return 1;
}
CGShipyard::CGShipyard() CGShipyard::CGShipyard()
:IShipyard(this) :IShipyard(this)
{ {

View File

@ -115,18 +115,23 @@ public:
static void postInit();//caleed after objs receive their initObj static void postInit();//caleed after objs receive their initObj
}; };
class DLL_EXPORT IShipyard class DLL_EXPORT IBoatGenerator
{ {
public: public:
const CGObjectInstance *o; const CGObjectInstance *o;
IShipyard(const CGObjectInstance *O); IBoatGenerator(const CGObjectInstance *O);
virtual void getBoatCost(std::vector<si32> &cost) const;
virtual int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral virtual int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
virtual void getOutOffsets(std::vector<int3> &offsets) const =0; //offsets to obj pos when we boat can be placed virtual void getOutOffsets(std::vector<int3> &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 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 int state() const; //0 - can buid, 1 - there is already a boat at dest tile, 2 - dest tile is blocked, 3 - no water
};
class DLL_EXPORT IShipyard : public IBoatGenerator
{
public:
IShipyard(const CGObjectInstance *O);
virtual void getBoatCost(std::vector<si32> &cost) const;
static const IShipyard *castFrom(const CGObjectInstance *obj); static const IShipyard *castFrom(const CGObjectInstance *obj);
static IShipyard *castFrom(CGObjectInstance *obj); static IShipyard *castFrom(CGObjectInstance *obj);
@ -227,7 +232,7 @@ public:
} }
}; };
class DLL_EXPORT CGHeroInstance : public CArmedInstance class DLL_EXPORT CGHeroInstance : public CArmedInstance, public IBoatGenerator
{ {
public: public:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -287,6 +292,12 @@ public:
int3 getSightCenter() const; //"center" tile from which the sight distance is calculated int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
int getSightRadious() const; //sight distance (should be used if player-owned structure) int getSightRadious() const; //sight distance (should be used if player-owned structure)
//////////////////////////////////////////////////////////////////////////
int getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
void getOutOffsets(std::vector<int3> &offsets) const; //offsets to obj pos when we boat can be placed
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
const std::string &getBiography() const; const std::string &getBiography() const;
@ -298,6 +309,7 @@ public:
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
bool canWalkOnSea() const; bool canWalkOnSea() const;
int getCurrentLuck(int stack=-1, bool town=false) const; int getCurrentLuck(int stack=-1, bool town=false) const;
int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored
const BonusList *ownerBonuses() const; const BonusList *ownerBonuses() const;
const HeroBonus *getBonus(int from, int id) const; const HeroBonus *getBonus(int from, int id) const;

View File

@ -2740,7 +2740,7 @@ CStack * BattleInfo::generateNewStack(const CGHeroInstance * owner, int creature
ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const
{ {
ui32 ret = VLC->spellh->spells[sp->id].costs[caster->getSpellSchoolLevel(sp)]; ui32 ret = caster->getSpellCost(sp);
//checking for friendly stacks reducing cost of the spell and //checking for friendly stacks reducing cost of the spell and
//enemy stacks increasing it //enemy stacks increasing it

View File

@ -380,7 +380,11 @@ struct GiveBonus : public CPackForClient //115
struct ChangeObjPos : public CPackForClient //116 struct ChangeObjPos : public CPackForClient //116
{ {
ChangeObjPos(){type = 116;}; ChangeObjPos()
{
type = 116;
flags = 0;
}
void applyFirstCl(CClient *cl); void applyFirstCl(CClient *cl);
void applyCl(CClient *cl); void applyCl(CClient *cl);
DLL_EXPORT void applyGs(CGameState *gs); DLL_EXPORT void applyGs(CGameState *gs);
@ -692,10 +696,11 @@ struct InfoWindow : public CPackForClient //103 - displays simple info window
{ {
h & text & components & player & soundID; h & text & components & player & soundID;
} }
InfoWindow() { InfoWindow()
{
type = 103; type = 103;
soundID = 0; soundID = 0;
}; }
}; };
struct SetObjectProperty : public CPackForClient//1001 struct SetObjectProperty : public CPackForClient//1001
@ -1424,6 +1429,20 @@ struct DigWithHero : public CPackForServer
} }
}; };
struct CastAdvSpell : public CPackForServer
{
CastAdvSpell(){}
si32 hid; //hero id
ui32 sid; //spell id
int3 pos; //selected tile (not always used)
bool applyGh(CGameHandler *gh);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & hid & sid & pos;
}
};
/***********************************************************************************************************/ /***********************************************************************************************************/
struct SaveGame : public CPackForClient, public CPackForServer struct SaveGame : public CPackForClient, public CPackForServer

View File

@ -153,6 +153,7 @@ void registerTypes3(Serializer &s)
s.template registerType<MakeAction>(); s.template registerType<MakeAction>();
s.template registerType<MakeCustomAction>(); s.template registerType<MakeCustomAction>();
s.template registerType<DigWithHero>(); s.template registerType<DigWithHero>();
s.template registerType<CastAdvSpell>();
s.template registerType<SaveGame>(); s.template registerType<SaveGame>();
s.template registerType<SetSelection>(); s.template registerType<SetSelection>();

View File

@ -46,6 +46,8 @@ extern bool end2;
#ifdef max #ifdef max
#undef max #undef max
#endif #endif
#define COMPLAIN_RET(txt) {complain(txt); return false;}
#define NEW_ROUND BattleNextRound bnr;\ #define NEW_ROUND BattleNextRound bnr;\
bnr.round = gs->curB->round + 1;\ bnr.round = gs->curB->round + 1;\
sendAndApply(&bnr); sendAndApply(&bnr);
@ -4197,3 +4199,101 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
} }
} }
} }
bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &pos)
{
const CSpell *s = &VLC->spellh->spells[spellID];
int cost = h->getSpellCost(s);
int schoolLevel = h->getSpellSchoolLevel(s);
if(!h->canCastThisSpell(s))
COMPLAIN_RET("Hero cannot cast this spell!");
if(h->mana < cost)
COMPLAIN_RET("Hero doesn't have enough spell points to cast this spell!");
if(s->combatSpell)
COMPLAIN_RET("This function can be used only for adventure map spells!");
switch(spellID)
{
case 0: //Summon Boat
{
//check if spell works at all
if(rand() % 100 >= s->powers[schoolLevel]) //power is % chance of success
{
InfoWindow iw;
iw.player = h->tempOwner;
iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
iw.text.addReplacement(h->name);
sendAndApply(&iw);
return true; //TODO? or should it be false? request was correct and realized, but spell failed...
}
//try to find unoccupied boat to summon
const CGBoat *nearest = NULL;
double dist = 0;
int3 summonPos = h->bestLocation();
if(summonPos.x < 0)
COMPLAIN_RET("There is no water tile available!");
BOOST_FOREACH(const CGObjectInstance *obj, gs->map->objects)
{
if(obj && obj->ID == 8)
{
const CGBoat *b = static_cast<const CGBoat*>(obj);
if(b->hero) continue; //we're looking for unoccupied boat
double nDist = distance(b->pos, h->getPosition());
if(!nearest || nDist < dist) //it's first boat or closer than previous
{
nearest = b;
dist = nDist;
}
}
}
if(nearest) //we found boat to summon
{
ChangeObjPos cop;
cop.objid = nearest->id;
cop.nPos = summonPos + int3(1,0,0);;
cop.flags = 1;
sendAndApply(&cop);
}
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
{
InfoWindow iw;
iw.player = h->tempOwner;
iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon.
sendAndApply(&iw);
}
else //create boat
{
NewObject no;
no.ID = 8;
no.subID = h->getBoatType();
no.pos = summonPos + int3(1,0,0);;
sendAndApply(&no);
}
break;
}
case 1: //Scuttle Boat
case 2: //Visions
case 3: //View Earth
case 4: //Disguise
case 5: //View Air
case 6: //Fly
case 7: //Water Walk
case 8: //Dimension Door
case 9: //Town Portal
default:
COMPLAIN_RET("This spell is not implemented yet!");
}
SetMana sm;
sm.hid = h->id;
sm.val = h->mana - cost;
sendAndApply(&sm);
return true;
}

View File

@ -181,6 +181,7 @@ public:
void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h ); void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
void engageIntoBattle( ui8 player ); void engageIntoBattle( ui8 player );
bool dig(const CGHeroInstance *h); bool dig(const CGHeroInstance *h);
bool castSpell(const CGHeroInstance *h, int spellID, const int3 &pos);
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

View File

@ -160,6 +160,12 @@ bool DigWithHero::applyGh( CGameHandler *gh )
return gh->dig(gh->getHero(id)); return gh->dig(gh->getHero(id));
} }
bool CastAdvSpell::applyGh( CGameHandler *gh )
{
ERROR_IF_NOT_OWNS(hid);
return gh->castSpell(gh->getHero(hid), sid, pos);
}
bool PlayerMessage::applyGh( CGameHandler *gh ) bool PlayerMessage::applyGh( CGameHandler *gh )
{ {
if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN; if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;