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:
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
|
@ -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();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -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 )
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>();
|
||||
|
@ -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;
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user