1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-15 00:05:02 +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);
//if there is no battle
return sp->costs[caster->getSpellSchoolLevel(sp)];
return caster->getSpellCost(sp);
}
int CCallback::estimateSpellDamage(const CSpell * sp) const
@ -954,6 +954,15 @@ si8 CCallback::battleGetStackLuck( int 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()
{
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 recalculatePaths()=0; //updates main, client pathfinder info (should be called when moving hero is over)
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
virtual std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos)const =0;
@ -227,6 +228,7 @@ public:
void sendMessage(const std::string &mess);
void buildBoat(const IShipyard *obj);
void dig(const CGObjectInstance *hero);
void castSpell(const CGHeroInstance *hero, int spellID, const int3 &pos = int3(-1, -1, -1));
//get info
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)
{
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
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->myInt->battleInt->castThisSpell(spell);
}
else
else //adventure spell
{
//insufficient mana
if(spellCost > owner->myHero->mana)
{
std::vector<SComponent*> comps;
char msgBuf[500];
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;
}
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( COSer<CSaveFile> &h, const int version );

View File

@ -113,6 +113,7 @@ public:
void waitForServer();
CPack * retreivePack(); //gets from server next pack (allocates it with new)
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
cl->playerint[player]->tileHidden(tiles);
if (cl->IGameCallback::getSelectedHero(player))//if we have selected hero...
GS(cl)->calculatePaths(cl->IGameCallback::getSelectedHero(player), *cl->pathInfo);
cl->updatePaths();
}
void SetAvailableHeroes::applyCl( CClient *cl )
@ -146,6 +145,8 @@ void ChangeObjPos::applyCl( CClient *cl )
CGObjectInstance *obj = GS(cl)->map->objects[objid];
if(flags & 1)
CGI->mh->printObject(obj);
cl->updatePaths();
}
void PlayerEndsGame::applyCl( CClient *cl )

View File

@ -47,7 +47,6 @@ BankConfig CGPyramid::pyramidConfig;
ui8 CGObelisk::obeliskCount; //how many obelisks are on map
std::map<ui8, ui8> CGObelisk::visited; //map: color_id => how many obelisks has been visited
void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const
{};
@ -687,6 +686,7 @@ int CGHeroInstance::getSpellSecLevel(int spell) const
}
CGHeroInstance::CGHeroInstance()
: IBoatGenerator(this)
{
ID = HEROI_TYPE;
tacticFormationEnabled = inTownGarrison = false;
@ -1226,6 +1226,34 @@ const BonusList * CGHeroInstance::ownerBonuses() const
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()
{
switch(ID)
@ -5308,13 +5336,6 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
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
//{
// std::vector<int3> offsets;
@ -5327,7 +5348,7 @@ void IShipyard::getBoatCost( std::vector<si32> &cost ) const
// return false;
//}
int3 IShipyard::bestLocation() const
int3 IBoatGenerator::bestLocation() const
{
std::vector<int3> offsets;
getOutOffsets(offsets);
@ -5339,12 +5360,7 @@ int3 IShipyard::bestLocation() const
return int3(-1,-1,-1);
}
IShipyard::IShipyard(const CGObjectInstance *O)
: o(O)
{
}
int IShipyard::state() const
int IBoatGenerator::state() const
{
int3 tile = bestLocation();
TerrainTile *t = IObjectInterface::cb->getTile(tile);
@ -5358,6 +5374,30 @@ int IShipyard::state() const
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 )
{
if(obj->ID == TOWNI_TYPE)
@ -5380,12 +5420,6 @@ const IShipyard * IShipyard::castFrom( const CGObjectInstance *obj )
return castFrom(const_cast<CGObjectInstance*>(obj));
}
int IShipyard::getBoatType() const
{
//We make good ships by default
return 1;
}
CGShipyard::CGShipyard()
:IShipyard(this)
{

View File

@ -115,18 +115,23 @@ public:
static void postInit();//caleed after objs receive their initObj
};
class DLL_EXPORT IShipyard
class DLL_EXPORT IBoatGenerator
{
public:
const CGObjectInstance *o;
IShipyard(const CGObjectInstance *O);
virtual void getBoatCost(std::vector<si32> &cost) const;
IBoatGenerator(const CGObjectInstance *O);
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 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
};
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 IShipyard *castFrom(CGObjectInstance *obj);
@ -227,7 +232,7 @@ public:
}
};
class DLL_EXPORT CGHeroInstance : public CArmedInstance
class DLL_EXPORT CGHeroInstance : public CArmedInstance, public IBoatGenerator
{
public:
//////////////////////////////////////////////////////////////////////////
@ -287,6 +292,12 @@ public:
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 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;
@ -298,6 +309,7 @@ public:
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
bool canWalkOnSea() 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 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 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
//enemy stacks increasing it

View File

@ -380,7 +380,11 @@ struct GiveBonus : public CPackForClient //115
struct ChangeObjPos : public CPackForClient //116
{
ChangeObjPos(){type = 116;};
ChangeObjPos()
{
type = 116;
flags = 0;
}
void applyFirstCl(CClient *cl);
void applyCl(CClient *cl);
DLL_EXPORT void applyGs(CGameState *gs);
@ -692,10 +696,11 @@ struct InfoWindow : public CPackForClient //103 - displays simple info window
{
h & text & components & player & soundID;
}
InfoWindow() {
InfoWindow()
{
type = 103;
soundID = 0;
};
}
};
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

View File

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

View File

@ -46,6 +46,8 @@ extern bool end2;
#ifdef max
#undef max
#endif
#define COMPLAIN_RET(txt) {complain(txt); return false;}
#define NEW_ROUND BattleNextRound bnr;\
bnr.round = gs->curB->round + 1;\
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 engageIntoBattle( ui8 player );
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)
{

View File

@ -160,6 +160,12 @@ bool DigWithHero::applyGh( CGameHandler *gh )
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 )
{
if(gh->getPlayerAt(c) != player) ERROR_AND_RETURN;