1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

* counterattacks

* spells learning
* no tooltips for objects under FoW
* working resource silo
* new system for simple unit abilities/states
* neutral monster army disappears when defeated
* synchronization between client and server processes
* fixed battle ending
This commit is contained in:
Michał W. Urbańczyk 2008-09-12 08:51:46 +00:00
parent 3b16d67b2e
commit e71b40ccc5
26 changed files with 309 additions and 83 deletions

View File

@ -320,13 +320,13 @@ void CBattleInterface::show(SDL_Surface * to)
//double loop because dead stacks should be printed first
for(std::map<int, CStack>::iterator j=stacks.begin(); j!=stacks.end(); ++j)
{
if(j->second.alive)
if(j->second.alive())
stackAliveByHex[j->second.position].push_back(j->second.ID);
}
std::vector<int> stackDeadByHex[187];
for(std::map<int, CStack>::iterator j=stacks.begin(); j!=stacks.end(); ++j)
{
if(!j->second.alive)
if(!j->second.alive())
stackDeadByHex[j->second.position].push_back(j->second.ID);
}
@ -336,7 +336,7 @@ void CBattleInterface::show(SDL_Surface * to)
{
for(int v=0; v<stackDeadByHex[b].size(); ++v)
{
creAnims[stackDeadByHex[b][v]]->nextFrame(to, creAnims[stackDeadByHex[b][v]]->pos.x, creAnims[stackDeadByHex[b][v]]->pos.y, creDir[stackDeadByHex[b][v]], (animCount%4==0 || creAnims[stackDeadByHex[b][v]]->getType()!=2) && stacks[stackDeadByHex[b][v]].alive, stackDeadByHex[b][v]==activeStack); //increment always when moving, never if stack died
creAnims[stackDeadByHex[b][v]]->nextFrame(to, creAnims[stackDeadByHex[b][v]]->pos.x, creAnims[stackDeadByHex[b][v]]->pos.y, creDir[stackDeadByHex[b][v]], (animCount%4==0 || creAnims[stackDeadByHex[b][v]]->getType()!=2) && stacks[stackDeadByHex[b][v]].alive(), stackDeadByHex[b][v]==activeStack); //increment always when moving, never if stack died
//printing amount
if(stacks[stackDeadByHex[b][v]].amount > 0) //don't print if stack is not alive
{
@ -353,7 +353,7 @@ void CBattleInterface::show(SDL_Surface * to)
{
for(int v=0; v<stackAliveByHex[b].size(); ++v)
{
creAnims[stackAliveByHex[b][v]]->nextFrame(to, creAnims[stackAliveByHex[b][v]]->pos.x, creAnims[stackAliveByHex[b][v]]->pos.y, creDir[stackAliveByHex[b][v]], (animCount%4==0) && stacks[stackAliveByHex[b][v]].alive, stackAliveByHex[b][v]==activeStack); //increment always when moving, never if stack died
creAnims[stackAliveByHex[b][v]]->nextFrame(to, creAnims[stackAliveByHex[b][v]]->pos.x, creAnims[stackAliveByHex[b][v]]->pos.y, creDir[stackAliveByHex[b][v]], (animCount%4==0) && stacks[stackAliveByHex[b][v]].alive(), stackAliveByHex[b][v]==activeStack); //increment always when moving, never if stack died
//printing amount
if(stacks[stackAliveByHex[b][v]].amount > 0) //don't print if stack is not alive
{
@ -847,7 +847,7 @@ void CBattleInterface::hexLclicked(int whichOne)
return; //we are not permit to do anything
CStack* dest = LOCPLINT->cb->battleGetStackByPos(whichOne); //creature at destination tile; -1 if there is no one
if(!dest || !dest->alive) //no creature at that tile
if(!dest || !dest->alive()) //no creature at that tile
{
if(std::find(shadedHexes.begin(),shadedHexes.end(),whichOne)!=shadedHexes.end())// and it's in our range
giveCommand(2,whichOne,activeStack);
@ -1321,7 +1321,7 @@ void CBattleHex::mouseMoved(SDL_MouseMotionEvent &sEvent)
{
if(myInterface->console->alterTxt.size() == 0 && LOCPLINT->cb->battleGetStack(myNumber) != -1 &&
LOCPLINT->cb->battleGetStackByPos(myNumber)->owner != LOCPLINT->playerID &&
LOCPLINT->cb->battleGetStackByPos(myNumber)->alive)
LOCPLINT->cb->battleGetStackByPos(myNumber)->alive())
{
char tabh[160];
CStack attackedStack = *LOCPLINT->cb->battleGetStackByPos(myNumber);
@ -1352,7 +1352,7 @@ void CBattleHex::clickRight(boost::logic::tribool down)
if(hovered && strictHovered && stID!=-1)
{
CStack myst = *LOCPLINT->cb->battleGetStackByID(stID); //stack info
if(!myst.alive) return;
if(!myst.alive()) return;
StackState *pom = NULL;
if(down)
{

View File

@ -234,6 +234,8 @@ std::vector < std::string > CCallback::getObjDescriptions(int3 pos)
{
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
std::vector<std::string> ret;
if(!isVisible(pos,player))
return ret;
BOOST_FOREACH(const CGObjectInstance * obj, gs->map->terrain[pos.x][pos.y][pos.z].blockingObjects)
ret.push_back(obj->hoverName);
return ret;
@ -521,7 +523,7 @@ bool CCallback::battleCanShoot(int ID, int dest) //TODO: finish
if(battleGetStackByID(ID)->creature->isShooting()
&& battleGetStack(dest) != -1
&& battleGetStackByPos(dest)->owner != battleGetStackByID(ID)->owner
&& battleGetStackByPos(dest)->alive)
&& battleGetStackByPos(dest)->alive())
return true;
return false;
}

View File

@ -522,6 +522,11 @@ void CCastleInterface::buildingClicked(int building)
cmw->activate();
break;
}
case 15:
{
LOCPLINT->showInfoDialog(CGI->buildh->buildings[town->subID][15]->description,std::vector<SComponent*>());
break;
}
case 16:
{
const CGHeroInstance *hero = town->visitingHero;
@ -1432,8 +1437,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner)
blitAt(view,332,76,bg);
for(int i=0; i<owner->town->town->mageLevel; i++)
{
int sp = 5 - i; //how many spells are available at this level
if(owner->town->subID==2 && vstd::contains(owner->town->builtBuildings,22)) sp++; //magic library in tower
int sp = owner->town->spellsAtLevel(i+1,false);
for(int j=0; j<sp; j++)
{
if(i < owner->town->mageGuildLevel())

View File

@ -28,7 +28,6 @@ public:
void clickRight (tribool down);
void mouseMoved (SDL_MouseMotionEvent & sEvent);
};
class CHeroGSlot : public ClickableL, public ClickableR, public Hoverable
{
public:

View File

@ -112,7 +112,7 @@ CStack * BattleInfo::getStackT(int tileID)
|| (stacks[g]->creature->isDoubleWide() && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
|| (stacks[g]->creature->isDoubleWide() && !stacks[g]->attackerOwned && stacks[g]->position+1 == tileID))
{
if(stacks[g]->alive)
if(stacks[g]->alive())
{
return stacks[g];
}
@ -125,7 +125,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, int stackToOmmit)
memset(accessibility,1,187); //initialize array with trues
for(int g=0; g<stacks.size(); ++g)
{
if(!stacks[g]->alive || stacks[g]->ID==stackToOmmit) //we don't want to lock position of this stack
if(!stacks[g]->alive() || stacks[g]->ID==stackToOmmit) //we don't want to lock position of this stack
continue;
accessibility[stacks[g]->position] = false;
@ -254,8 +254,9 @@ std::vector<int> BattleInfo::getPath(int start, int dest, bool*accessibility)
}
CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S)
:creature(C),amount(A), baseAmount(A), owner(O), alive(true), position(-1), ID(I), attackerOwned(AO), firstHPleft(C->hitPoints), slot(S)
:creature(C),amount(A), baseAmount(A), owner(O), position(-1), ID(I), attackerOwned(AO), firstHPleft(C->hitPoints), slot(S), counterAttacks(0)
{
state.insert(ALIVE);
}
void CGameState::applyNL(IPack * pack)
{
@ -372,6 +373,18 @@ void CGameState::applyNL(IPack * pack)
}
break;
}
case 109:
{
ChangeSpells *rh = static_cast<ChangeSpells*>(pack);
CGHeroInstance *hero = getHero(rh->hid);
if(rh->learn)
BOOST_FOREACH(ui32 sid, rh->spells)
hero->spells.insert(sid);
else
BOOST_FOREACH(ui32 sid, rh->spells)
hero->spells.erase(sid);
break;
}
case 500:
{
RemoveObject *rh = static_cast<RemoveObject*>(pack);
@ -519,6 +532,8 @@ void CGameState::applyNL(IPack * pack)
{
BattleNextRound *ns = static_cast<BattleNextRound*>(pack);
curB->round = ns->round;
for(int i=0; i<curB->stacks.size();i++)
curB->stacks[i]->counterAttacks = 0;
break;
}
case 3002:
@ -551,12 +566,15 @@ void CGameState::applyNL(IPack * pack)
CStack * at = curB->getStack(br->stackAttacked);
at->amount = br->newAmount;
at->firstHPleft = br->newHP;
at->alive = !br->killed();
if(br->killed())
at->state -= ALIVE;
break;
}
case 3006:
{
BattleAttack *br = static_cast<BattleAttack*>(pack);
if(br->counter())
curB->getStack(br->stackAttacking)->counterAttacks++;
applyNL(&br->bsa);
break;
}
@ -1325,7 +1343,7 @@ void BattleInfo::calculateCasualties( std::set<std::pair<ui32,si32> > *casualtie
{
for(int i=0; i<stacks.size();i++)//setting casualties
{
if(!stacks[i]->alive)
if(!stacks[i]->alive())
{
casualties[!stacks[i]->attackerOwned].insert(std::pair<ui32,si32>(stacks[i]->creature->idNumber,stacks[i]->baseAmount));
}
@ -1334,24 +1352,4 @@ void BattleInfo::calculateCasualties( std::set<std::pair<ui32,si32> > *casualtie
casualties[!stacks[i]->attackerOwned].insert(std::pair<ui32,si32>(stacks[i]->creature->idNumber,stacks[i]->baseAmount - stacks[i]->amount));
}
}
//if(br->killedAmount>0)
//{
// bool found = false;
// for(std::set<std::pair<ui32,si32> >::iterator it = curB->cas[1 - at->attackerOwned].begin(); it!=curB->cas[1 - at->attackerOwned].end(); ++it)
// {
// if(it->first == at->creature->idNumber)
// {
// found = true;
// std::pair<ui32,si32> mod = *it;
// mod.second += br->killedAmount;
// curB->cas[1 - at->attackerOwned].insert(it, mod);
// curB->cas[1 - at->attackerOwned].erase(it);
// }
// }
// if(!found)
// {
// curB->cas[1 - at->attackerOwned].insert(std::make_pair(at->creature->idNumber, br->killedAmount));
// }
//}
}

View File

@ -78,19 +78,22 @@ struct DLL_EXPORT BattleInfo
};
class DLL_EXPORT CStack
{
{
public:
ui32 ID; //unique ID of stack
CCreature * creature;
ui32 amount, baseAmount;
ui32 firstHPleft; //HP of first creature in stack
ui8 owner, slot; //owner - player colour (255 for neutrals), slot - position in garrison (255 for neutrals/called creatures)
ui8 owner, slot; //owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures)
ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
ui16 position; //position on battlefield
ui8 alive; //true if it is alive
ui8 counterAttacks; //how many counter attacks has this stack perfomed in current round
std::set<EAbilities> abilities;
std::set<ECombatInfo> state;
CStack(CCreature * C, int A, int O, int I, bool AO, int S);
CStack() : creature(NULL),amount(-1),owner(255), alive(true), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1){};
CStack() : creature(NULL),amount(-1),owner(255), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1), counterAttacks(0){};
template <typename Handler> void save(Handler &h, const int version)
{
@ -101,15 +104,20 @@ public:
ui32 id;
h & id;
creature = &VLC->creh->creatures[id];
abilities = creature->abilities;
}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & ID & amount & baseAmount & firstHPleft & owner & attackerOwned & position & alive;
h & ID & amount & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks;
if(h.saving)
save(h,version);
else
load(h,version);
}
bool alive()
{
return vstd::contains(state,ALIVE);
}
};
struct UpgradeInfo

View File

@ -720,7 +720,7 @@ std::vector<int> CPickable::yourObjects() //returns IDs of objects which are han
void CTownScript::onHeroVisit(int objid, int heroID)
{
DEFOS;
if(cb->getOwner(objid)!=cb->getOwner(heroID))
{
return;
@ -774,7 +774,8 @@ void CHeroScript::onHeroVisit(int objid, int heroID)
&vis->army,
my->pos,
my,
vis);
vis,
0);
}
}
std::vector<int> CHeroScript::yourObjects() //returns IDs of objects which are handled by script
@ -834,7 +835,7 @@ void CMonsterS::onHeroVisit(int objid, int heroID)
CCreatureSet set;
//TODO: zrobic secik w sposob wyrafinowany
set.slots[0] = std::pair<ui32,si32>(os->subID,amounts[objid]);
cb->startBattle(heroID,set,os->pos);
cb->startBattle(heroID,set,os->pos,boost::bind(&CMonsterS::endBattleWith,this,os,_1));
}
std::vector<int> CMonsterS::yourObjects() //returns IDs of objects which are handled by script
{
@ -843,6 +844,17 @@ std::vector<int> CMonsterS::yourObjects() //returns IDs of objects which are han
return ret;
}
void CMonsterS::endBattleWith(const CGObjectInstance *monster, BattleResult *result )
{
if(result->winner==0)
{
cb->removeObject(monster->id);
}
else
{
//TODO: remove casualties
}
}
void CCreatureGen::newObject(int objid)
{

2
CLua.h
View File

@ -25,6 +25,7 @@ class CSelectableComponent;
class CGameState;
struct Mapa;
struct lua_State;
struct BattleResult;
enum ESLan{UNDEF=-1,CPP,ERM,LUA};
class CObjectScript
{
@ -175,6 +176,7 @@ public:
void newObject(int objid);
void onHeroVisit(int objid, int heroID);
std::vector<int> yourObjects(); //returns IDs of objects which are handled by script
void endBattleWith(const CGObjectInstance *monster, BattleResult *result);
};
class CCreatureGen : public CCPPObjectScript

30
CMT.cpp
View File

@ -6,6 +6,8 @@
#include <vector>
#include <queue>
#include <cmath>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/thread.hpp>
#include "SDL_ttf.h"
#include "SDL_mixer.h"
@ -37,6 +39,7 @@
#include "client/Graphics.h"
#include "client/Client.h"
#include "lib/Connection.h"
#include "lib/Interprocess.h"
#include "lib/VCMI_Lib.h"
std::string NAME = NAME_VER + std::string(" (client)");
DLL_EXPORT void initDLL(CLodHandler *b);
@ -45,6 +48,7 @@ extern SDL_Surface * CSDL_Ext::std32bppSurface;
std::queue<SDL_Event> events;
boost::mutex eventsM;
TTF_Font * TNRB16, *TNR, *GEOR13, *GEORXX, *GEORM, *GEOR16;
namespace intpr = boost::interprocess;
#ifndef __GNUC__
int _tmain(int argc, _TCHAR* argv[])
#else
@ -149,16 +153,23 @@ int main(int argc, char** argv)
THC std::cout<<"Initialization CPreGame (together): "<<tmh.getDif()<<std::endl;
THC std::cout<<"Initialization of VCMI (togeter): "<<total.getDif()<<std::endl;
cpg->mush = mush;
StartInfo *options = new StartInfo(cpg->runLoop());
///////////////////////////////////////////////////////////////////////////////////////
tmh.getDif();
////////////////////////SERVER STARTING/////////////////////////////////////////////////
char portc[10]; SDL_itoa(port,portc,10);
intpr::shared_memory_object smo(intpr::open_or_create,"vcmi_memory",intpr::read_write);
smo.truncate(sizeof(ServerReady));
intpr::mapped_region mr(smo,intpr::read_write);
ServerReady *sr = new(mr.get_address())ServerReady();
std::string comm = std::string(SERVER_NAME) + " " + portc + " > server_log.txt";
boost::thread servthr(boost::bind(system,comm.c_str())); //runs server executable;
//TODO: will it work on non-windows platforms?
boost::thread servthr(boost::bind(system,comm.c_str())); //runs server executable; //TODO: will it work on non-windows platforms?
THC std::cout<<"Preparing shared memory and starting server: "<<tmh.getDif()<<std::endl;
///////////////////////////////////////////////////////////////////////////////////////
THC tmh.getDif();pomtime.getDif();//reset timers
cgi->pathf = new CPathfinder();
THC std::cout<<"\tPathfinder: "<<pomtime.getDif()<<std::endl;
if(argc>1)
if(argc>2)
{
std::cout << "Special mode without support for console!" << std::endl;
}
@ -171,6 +182,17 @@ int main(int argc, char** argv)
std::ofstream lll("client_log.txt");
CConnection *c=NULL;
//wait until server is ready
std::cout<<"Waiting for server... " << std::flush;
{
intpr::scoped_lock<intpr::interprocess_mutex> slock(sr->mutex);
while(!sr->ready)
{
sr->cond.wait(slock);
}
}
intpr::shared_memory_object::remove("vcmi_memory");
std::cout << tmh.getDif()<<std::endl;
while(!c)
{
try

View File

@ -1,11 +1,11 @@
#include "CSpellWindow.h"
#include "client/Graphics.h"
#include "hch/CDefHandler.h"
#include "hch/CObjectHandler.h"
#include "hch/CPreGameTextHandler.h"
#include "CAdvmapInterface.h"
#include "CGameInfo.h"
#include "SDL_Extensions.h"
#include "Graphics.h"
#include "../hch/CDefHandler.h"
#include "../hch/CObjectHandler.h"
#include "../hch/CPreGameTextHandler.h"
#include "../CAdvmapInterface.h"
#include "../CGameInfo.h"
#include "../SDL_Extensions.h"
#include <boost/bind.hpp>
#include <sstream>

View File

@ -1,6 +1,6 @@
#pragma once
#include "global.h"
#include "CPlayerInterface.h"
#include "../global.h"
#include "../CPlayerInterface.h"
struct SDL_Surface;
class CDefHandler;

View File

@ -255,6 +255,14 @@ void CClient::process(int what)
}
break;
}
case 109:
{
ChangeSpells vc;
*serv >> vc;
std::cout << "Changing spells of hero "<<vc.hid<<std::endl;
gs->apply(&vc);
break;
}
case 500:
{
RemoveObject rh;

View File

@ -365,6 +365,10 @@
RelativePath="..\hch\CSndHandler.cpp"
>
</File>
<File
RelativePath=".\CSpellWindow.cpp"
>
</File>
<File
RelativePath="..\CThreadHelper.cpp"
>
@ -511,6 +515,10 @@
RelativePath="..\hch\CSndHandler.h"
>
</File>
<File
RelativePath=".\CSpellWindow.h"
>
</File>
<File
RelativePath="..\CThreadHelper.h"
>

View File

@ -40,6 +40,12 @@ enum ElossCon {lossCastle, lossHero, timeExpires, lossStandard=255};
enum EHeroClasses {HERO_KNIGHT, HERO_CLERIC, HERO_RANGER, HERO_DRUID, HERO_ALCHEMIST, HERO_WIZARD,
HERO_DEMONIAC, HERO_HERETIC, HERO_DEATHKNIGHT, HERO_NECROMANCER, HERO_WARLOCK, HERO_OVERLORD,
HERO_BARBARIAN, HERO_BATTLEMAGE, HERO_BEASTMASTER, HERO_WITCH, HERO_PLANESWALKER, HERO_ELEMENTALIST};
enum EAbilities {DOUBLE_WIDE, FLYING, SHOOTER, TWO_HEX_ATTACK, SIEGE_ABILITY, SIEGE_WEAPON,
KING1, KING2, KING3, MIND_IMMUNITY, NO_OBSTACLE_PENALTY, NO_CLOSE_COMBAT_PENALTY,
JOUSTING, FIRE_IMMUNITY, TWICE_ATTACK, NO_ENEMY_RETALIATION, NO_MORAL_PENALTY,
UNDEAD, MULTI_HEAD_ATTACK, EXTENDED_RADIOUS_SHOOTER, GHOST, RAISES_MORALE,
LOWERS_MORALE, DRAGON, STRIKE_AND_RETURN, FEARLESS, REBIRTH}; //some flags are used only for battles
enum ECombatInfo{ALIVE = REBIRTH+1, SUMMONED, CLONED, HAD_MORALE, WAITING, MOVED, DEFENDING};
class CGameInfo;
extern CGameInfo* CGI;
@ -111,8 +117,8 @@ namespace vstd
{
return std::find(c.begin(),c.end(),i) != c.end();
}
template <typename V, typename Item>
bool contains(const std::map<Item,V> & c, const Item &i)
template <typename V, typename Item, typename Item2>
bool contains(const std::map<Item,V> & c, const Item2 &i)
{
return c.find(i)!=c.end();
}

View File

@ -40,16 +40,16 @@ int CCreature::getQuantityID(int quantity)
bool CCreature::isDoubleWide()
{
return boost::algorithm::find_first(abilityRefs, "DOUBLE_WIDE");
return vstd::contains(abilities,DOUBLE_WIDE);
}
bool CCreature::isFlying()
{
return boost::algorithm::find_first(abilityRefs, "FLYING_ARMY");
return vstd::contains(abilities,FLYING);
}
bool CCreature::isShooting()
{
return boost::algorithm::find_first(abilityRefs, "SHOOTING_ARMY");
return vstd::contains(abilities,SHOOTER);
}
si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatures can be bought
{
@ -306,6 +306,12 @@ void CCreatureHandler::loadCreatures()
}
ncre.abilityRefs = buf.substr(befi, i-befi);
i+=2;
if(boost::algorithm::find_first(ncre.abilityRefs, "DOUBLE_WIDE"))
ncre.abilities.insert(DOUBLE_WIDE);
if(boost::algorithm::find_first(ncre.abilityRefs, "FLYING_ARMY"))
ncre.abilities.insert(FLYING);
if(boost::algorithm::find_first(ncre.abilityRefs, "SHOOTING_ARMY"))
ncre.abilities.insert(SHOOTER);
if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string(""))
{
ncre.idNumber = creatures.size();
@ -443,8 +449,10 @@ void CCreatureHandler::loadCreatures()
}
inp2.close();
creatures[122].abilityRefs += "DOUBLE_WIDE"; //water elemental should be treated as double-wide
creatures[123].abilityRefs += "DOUBLE_WIDE"; //ice elemental should be treated as double-wide
creatures[122].abilities.insert(DOUBLE_WIDE);//water elemental should be treated as double-wide
creatures[123].abilities.insert(DOUBLE_WIDE);//ice elemental should be treated as double-wide
}
void CCreatureHandler::loadAnimationInfo()

View File

@ -22,6 +22,7 @@ public:
std::string abilityRefs; //references to abilities, in textformat
std::string animDefName;
ui32 idNumber;
std::set<EAbilities> abilities;
int faction; //-1 = neutral
///animation info

View File

@ -412,6 +412,16 @@ CGHeroInstance::~CGHeroInstance()
CGTownInstance::~CGTownInstance()
{}
int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const
{
if(checkGuild && mageGuildLevel() < level)
return 0;
int ret = 6 - level; //how many spells are available at this level
if(subID == 2 && vstd::contains(builtBuildings,22)) //magic library in Tower
ret++;
return ret;
}
CGObjectInstance::CGObjectInstance(const CGObjectInstance & right)
{
pos = right.pos;

View File

@ -168,6 +168,7 @@ public:
bool hasFort() const;
bool hasCapitol() const;
int dailyIncome() const;
int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
CGTownInstance();
virtual ~CGTownInstance();

22
lib/Interprocess.h Normal file
View File

@ -0,0 +1,22 @@
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
struct ServerReady
{
bool ready;
boost::interprocess::interprocess_mutex mutex;
boost::interprocess::interprocess_condition cond;
ServerReady()
{
ready = false;
}
void setToTrueAndNotify()
{
mutex.lock();
ready = true;
mutex.unlock();
cond.notify_all();
}
};

View File

@ -96,6 +96,18 @@ struct HeroVisitCastle : public CPack<HeroVisitCastle> //108
h & flags & tid & hid;
}
};
struct ChangeSpells : public CPack<ChangeSpells> //109
{
ChangeSpells(){type = 109;};
ui8 learn; //1 - gives spell, 0 - takes
ui32 hid;
std::set<ui32> spells;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & learn & hid & spells;
}
};
struct RemoveObject : public CPack<RemoveObject> //500
{
RemoveObject(){type = 500;};
@ -442,6 +454,10 @@ struct BattleAttack : public CPack<BattleAttack>//3006
{
return flags & 1;
}
bool counter()//is it counterattack?
{
return flags & 2;
}
bool killed() //if target stack was killed
{
return bsa.killed();

View File

@ -372,6 +372,10 @@
RelativePath="..\hch\CTownHandler.h"
>
</File>
<File
RelativePath=".\Interprocess.h"
>
</File>
<File
RelativePath="..\map.h"
>

View File

@ -240,11 +240,12 @@ void CGameHandler::changePrimSkill(int ID, int which, int val, bool abs)
}
}
void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2)
void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb)
{
BattleInfo *curB = new BattleInfo;
setupBattle(curB, tile, army1, army2, hero1, hero2); //battle start
setupBattle(curB, tile, army1, army2, hero1, hero2); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
NEW_ROUND;
//TODO: pre-tactic stuff, call scripts etc.
//tactic round
@ -267,15 +268,15 @@ void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile
//stack loop
for(unsigned i=0;i<stacks.size() && !battleResult.get();i++)
{
if(!stacks[i]->alive) continue;//indicates imposiibility of making action for this dead unit
if(!stacks[i]->alive()) continue;//indicates imposiibility of making action for this dead unit
BattleSetActiveStack sas;
sas.stack = stacks[i]->ID;
sendAndApply(&sas);
boost::unique_lock<boost::mutex> lock(battleMadeAction.mx);
while(!battleMadeAction.data)
while(!battleMadeAction.data && !battleResult.get()) //active stack hasn't made its action and battle is still going
battleMadeAction.cond.wait(lock);
battleMadeAction.data = false;
checkForBattleEnd(stacks);
checkForBattleEnd(stacks); //check if this action ended the battle
}
}
@ -287,6 +288,8 @@ void CGameHandler::startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile
//end battle, remove all info, free memory
sendAndApply(battleResult.data);
if(cb)
cb(battleResult.data);
delete battleResult.data;
//for(int i=0;i<stacks.size();i++)
// delete stacks[i];
@ -565,6 +568,14 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
sr.res[i]-=b->resources[i];
sendAndApply(&sr);
if(bid<5) //it's mage guild
{
if(t->visitingHero)
giveSpells(t,t->visitingHero);
if(t->garrisonHero)
giveSpells(t,t->garrisonHero);
}
break;
}
case 506: //recruit creature
@ -770,7 +781,7 @@ upgend:
c >> hid >> aid;
CGHeroInstance *hero = gs->getHero(hid);
CGTownInstance *town = hero->visitedTown;
if(aid==0)
if(aid==0) //spellbok
{
if(!vstd::contains(town->builtBuildings,si32(0)))
break;
@ -786,6 +797,8 @@ upgend:
sha.artifWorn = hero->artifWorn;
sha.artifWorn[17] = 0;
sendAndApply(&sha);
giveSpells(town,hero);
}
else if(aid < 7 && aid > 3) //war machine
{
@ -883,6 +896,14 @@ upgend:
BattleAttack bat;
prepareAttack(bat,curStack,stackAtEnd);
sendAndApply(&bat);
//counterattack
if(!vstd::contains(curStack->abilities,NO_ENEMY_RETALIATION)
&& !stackAtEnd->counterAttacks ) //TODO: support for multiple retaliatons per turn
{
prepareAttack(bat,stackAtEnd,curStack);
bat.flags |= 2;
sendAndApply(&bat);
}
break;
}
case 7: //shoot
@ -942,7 +963,7 @@ void CGameHandler::moveStack(int stack, int dest)
else
gs->curB->getAccessibilityMap(accessibility,curStack->ID);
if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive) || !accessibility[dest])
if((stackAtEnd && stackAtEnd!=curStack && stackAtEnd->alive()) || !accessibility[dest])
return;
//if(dists[dest] > curStack->creature->speed && !(stackAtEnd && dists[dest] == curStack->creature->speed+1)) //we can attack a stack if we can go to adjacent hex
@ -1050,6 +1071,18 @@ void CGameHandler::newTurn()
}
for(std::vector<CGTownInstance *>::iterator j=i->second.towns.begin();j!=i->second.towns.end();j++)//handle towns
{
if(vstd::contains((**j).builtBuildings,15)) //there is resource silo
{
if((**j).town->primaryRes == 127) //we'll give wood and ore
{
r.res[0] += 1;
r.res[2] += 1;
}
else
{
r.res[(**j).town->primaryRes] += 1;
}
}
if(gs->getDate(1)==7) //first day of week
{
SetAvailableCreatures sac;
@ -1318,17 +1351,36 @@ void CGameHandler::checkForBattleEnd( std::vector<CStack*> &stacks )
hasStack[0] = hasStack[1] = false;
for(int b = 0; b<stacks.size(); ++b)
{
if(stacks[b]->alive)
if(stacks[b]->alive())
{
hasStack[1-stacks[b]->attackerOwned] = true;
}
}
if(!hasStack[0] || !hasStack[1]) //somebody has won
{
BattleResult *br = new BattleResult;
BattleResult *br = new BattleResult; //will be deleted at the end of startBattle(...)
br->result = 0;
br->winner = hasStack[1]; //fleeing side loses
gs->curB->calculateCasualties(br->casualties);
battleResult.set(br);
}
}
void CGameHandler::giveSpells( const CGTownInstance *t, const CGHeroInstance *h )
{
if(!vstd::contains(h->artifWorn,17))
return; //hero hasn't spellbok
ChangeSpells cs;
cs.hid = h->id;
cs.learn = true;
for(int i=0; i<std::min(t->mageGuildLevel(),h->getSecSkillLevel(7)+4);i++)
{
for(int j=0; j<t->spellsAtLevel(i+1,true); j++)
{
if(!vstd::contains(h->spells,t->spells[i][j]))
cs.spells.insert(t->spells[i][j]);
}
}
if(cs.spells.size())
sendAndApply(&cs);
}

View File

@ -11,6 +11,7 @@ class CGameState;
struct StartInfo;
class CCPPObjectScript;
class CScriptCallback;
struct BattleResult;
template <typename T> struct CPack;
template <typename T> struct Query;
class CGHeroInstance;
@ -52,8 +53,9 @@ class CGameHandler
void handleCPPObjS(std::map<int,CCPPObjectScript*> * mapa, CCPPObjectScript * script);
void changePrimSkill(int ID, int which, int val, bool abs=false);
void changeSecSkill(int ID, ui16 which, int val, bool abs=false);
void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
void moveStack(int stack, int dest);
void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2); //use hero=NULL for no hero
void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
void checkForBattleEnd( std::vector<CStack*> &stacks );
void setupBattle( BattleInfo * curB, int3 tile, CCreatureSet &army1, CCreatureSet &army2, CGHeroInstance * hero1, CGHeroInstance * hero2 );

View File

@ -117,6 +117,7 @@ void CScriptCallback::heroVisitCastle(int obj, int heroID)
vc.tid = obj;
vc.flags |= 1;
gh->sendAndApply(&vc);
gh->giveSpells(getTown(obj),getHero(heroID));
}
void CScriptCallback::stopHeroVisitCastle(int obj, int heroID)
@ -144,16 +145,25 @@ void CScriptCallback::giveHeroArtifact(int artid, int hid, int position) //pos==
gh->sendAndApply(&sha);
}
void CScriptCallback::startBattle(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) //use hero=NULL for no hero
void CScriptCallback::startBattle(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb) //use hero=NULL for no hero
{
boost::thread(boost::bind(&CGameHandler::startBattle,gh,*(CCreatureSet *)army1,*(CCreatureSet *)army2,tile,(CGHeroInstance *)hero1,(CGHeroInstance *)hero2));
boost::thread(boost::bind(&CGameHandler::startBattle,gh,*(CCreatureSet *)army1,*(CCreatureSet *)army2,tile,(CGHeroInstance *)hero1,(CGHeroInstance *)hero2,cb));
}
void CScriptCallback::startBattle(int heroID, CCreatureSet army, int3 tile) //for hero<=>neutral army
void CScriptCallback::startBattle(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) //for hero<=>neutral army
{
CGHeroInstance* h = const_cast<CGHeroInstance*>(getHero(heroID));
startBattle(&h->army,&army,tile,h,NULL);
startBattle(&h->army,&army,tile,h,NULL,cb);
//gh->gs->battle(&h->army,army,tile,h,NULL);
}
void CScriptCallback::changeSpells( int hid, bool give, const std::set<ui32> &spells )
{
ChangeSpells cs;
cs.hid = hid;
cs.spells = spells;
cs.learn = give;
gh->sendAndApply(&cs);
}
void CLuaCallback::registerFuncs(lua_State * L)
{
// lua_newtable(L);

View File

@ -1,6 +1,7 @@
#pragma once
#include "../global.h"
#include <vector>
#include <set>
#include "../client/FunctionList.h"
class CVCMIServer;
class CGameHandler;
@ -19,6 +20,7 @@ struct InfoWindow;
struct ShowInInfobox;
struct SelectionDialog;
struct YesNoDialog;
struct BattleResult;
class CScriptCallback
{
CScriptCallback(void);
@ -37,6 +39,7 @@ public:
const CGTownInstance* getTown(int objid);
//do sth
void changeSpells(int hid, bool give, const std::set<ui32> &spells);
void removeObject(int objid);
void setBlockVis(int objid, bool bv);
void setOwner(int objid, ui8 owner);
@ -50,8 +53,8 @@ public:
void heroVisitCastle(int obj, int heroID);
void stopHeroVisitCastle(int obj, int heroID);
void giveHeroArtifact(int artid, int hid, int position); //pos==-1 - first free slot in backpack
void startBattle(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //use hero=NULL for no hero
void startBattle(int heroID, CCreatureSet army, int3 tile); //for hero<=>neutral army
void startBattle(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
void startBattle(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
//friends
friend class CGameHandler;

View File

@ -9,21 +9,28 @@
#endif
#include "CVCMIServer.h"
#include <boost/crc.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include "../StartInfo.h"
#include "../map.h"
#include "../hch/CLodHandler.h"
#include "../lib/Interprocess.h"
#include "../lib/VCMI_Lib.h"
#include "CGameHandler.h"
std::string NAME = NAME_VER + std::string(" (server)");
using boost::asio::ip::tcp;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
namespace intpr = boost::interprocess;
bool end2 = false;
int port = 3030;
void vaccept(tcp::acceptor *ac, tcp::socket *s, boost::system::error_code *error)
{
ac->accept(*s,*error);
}
CVCMIServer::CVCMIServer()
: io(new io_service()), acceptor(new tcp::acceptor(*io, tcp::endpoint(tcp::v4(), port)))
{
@ -90,10 +97,29 @@ void CVCMIServer::newGame(CConnection *c)
}
void CVCMIServer::start()
{
ServerReady *sr = NULL;
intpr::mapped_region *mr;
try
{
intpr::shared_memory_object smo(intpr::open_only,"vcmi_memory",intpr::read_write);
mr = new intpr::mapped_region(smo,intpr::read_write);
sr = (ServerReady*)mr->get_address();
}
catch(...)
{
intpr::shared_memory_object smo(intpr::create_only,"vcmi_memory",intpr::read_write);
smo.truncate(sizeof(ServerReady));
mr = new intpr::mapped_region(smo,intpr::read_write);
sr = new(mr->get_address())ServerReady();
}
boost::system::error_code error;
std::cout<<"Listening for connections at port " << acceptor->local_endpoint().port() << std::endl;
tcp::socket * s = new tcp::socket(acceptor->io_service());
acceptor->accept(*s,error);
boost::thread acc(boost::bind(vaccept,acceptor,s,&error));
sr->setToTrueAndNotify();
delete mr;
acc.join();
if (error)
{
std::cout<<"Got connection but there is an error " << std::endl << error;
@ -147,7 +173,9 @@ int main(int argc, char** argv)
io_service io_service;
CVCMIServer server;
while(!end2)
{
server.start();
}
io_service.run();
} HANDLE_EXCEPTION
return 0;