mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-01 00:45:26 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there. * Declared existing Entity APIs. * Added basic script context caching * Started Lua script module * Started Lua spell effect API * Started script state persistence * Started battle info callback binding * CommitPackage removed * Extracted spells::Caster to own header; Expanded Spell API. * implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C * !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented * Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key) * Re-enabled VERM macros. * !?GM0 added * !?TM implemented * Added !!MF:N * Started !?OB, !!BM, !!HE, !!OW, !!UN * Added basic support of w-variables * Added support for ERM indirect variables * Made !?FU regular trigger * !!re (ERA loop receiver) implemented * Fixed ERM receivers with zero args.
This commit is contained in:
@ -70,11 +70,43 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
|
||||
|
||||
if(type == ART_NAMES)
|
||||
{
|
||||
dst = VLC->arth->artifacts[ser]->Name();
|
||||
auto art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
dst = art->getName();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == ART_DESCR)
|
||||
{
|
||||
auto art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
dst = art->getDescription();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if (type == ART_EVNTS)
|
||||
{
|
||||
auto art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
dst = art->getEventText();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == CRE_PL_NAMES)
|
||||
{
|
||||
dst = VLC->creh->creatures[ser]->namePl;
|
||||
auto cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
if(cre)
|
||||
dst = cre->getPluralName();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == CRE_SING_NAMES)
|
||||
{
|
||||
auto cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
if(cre)
|
||||
dst = cre->getSingularName();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == MINE_NAMES)
|
||||
{
|
||||
@ -86,21 +118,13 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
|
||||
}
|
||||
else if(type == SPELL_NAME)
|
||||
{
|
||||
dst = SpellID(ser).toSpell()->name;
|
||||
auto spell = SpellID(ser).toSpell(VLC->spells());
|
||||
if(spell)
|
||||
dst = spell->getName();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == CRE_SING_NAMES)
|
||||
{
|
||||
dst = VLC->creh->creatures[ser]->nameSing;
|
||||
}
|
||||
else if(type == ART_DESCR)
|
||||
{
|
||||
dst = VLC->arth->artifacts[ser]->Description();
|
||||
}
|
||||
else if (type == ART_EVNTS)
|
||||
{
|
||||
dst = VLC->arth->artifacts[ser]->EventText();
|
||||
}
|
||||
else if (type == OBJ_NAMES)
|
||||
else if(type == OBJ_NAMES)
|
||||
{
|
||||
dst = VLC->objtypeh->getObjectName(ser);
|
||||
}
|
||||
@ -258,8 +282,7 @@ DLL_LINKAGE std::string MetaString::buildList () const
|
||||
return lista;
|
||||
}
|
||||
|
||||
|
||||
void MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing or plural name;
|
||||
void MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing or plural name;
|
||||
{
|
||||
if (!count)
|
||||
addReplacement (CRE_PL_NAMES, id); //no creatures - just empty name (eg. defeat Angels)
|
||||
@ -269,7 +292,7 @@ void MetaString::addCreReplacement(CreatureID id, TQuantity count) //adds sing
|
||||
addReplacement (CRE_PL_NAMES, id);
|
||||
}
|
||||
|
||||
void MetaString::addReplacement(const CStackBasicDescriptor &stack)
|
||||
void MetaString::addReplacement(const CStackBasicDescriptor & stack)
|
||||
{
|
||||
assert(stack.type); //valid type
|
||||
addCreReplacement(stack.type->idNumber, stack.count);
|
||||
@ -282,7 +305,7 @@ static CGObjectInstance * createObject(Obj id, int subid, int3 pos, PlayerColor
|
||||
{
|
||||
case Obj::HERO:
|
||||
{
|
||||
auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->heroes[subid]->heroClass->id);
|
||||
auto handler = VLC->objtypeh->getHandlerFor(id, VLC->heroh->objects[subid]->heroClass->getIndex());
|
||||
nobj = handler->create(handler->getTemplates().front());
|
||||
break;
|
||||
}
|
||||
@ -409,7 +432,7 @@ int CGameState::pickUnusedHeroTypeRandomly(PlayerColor owner)
|
||||
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
|
||||
for(HeroTypeID hid : getUnusedAllowedHeroes())
|
||||
{
|
||||
if(VLC->heroh->heroes[hid.getNum()]->heroClass->faction == ps.castle)
|
||||
if(VLC->heroh->objects[hid.getNum()]->heroClass->faction == ps.castle)
|
||||
factionHeroes.push_back(hid);
|
||||
else
|
||||
otherHeroes.push_back(hid);
|
||||
@ -484,9 +507,9 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
|
||||
{
|
||||
do
|
||||
{
|
||||
f = getRandomGenerator().nextInt((int)VLC->townh->factions.size() - 1);
|
||||
f = getRandomGenerator().nextInt((int)VLC->townh->size() - 1);
|
||||
}
|
||||
while (VLC->townh->factions[f]->town == nullptr); // find playable faction
|
||||
while ((*VLC->townh)[f]->town == nullptr); // find playable faction
|
||||
}
|
||||
return std::make_pair(Obj::TOWN,f);
|
||||
}
|
||||
@ -506,7 +529,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
|
||||
//if castle alignment available
|
||||
if (auto info = dynamic_cast<CCreGenAsCastleInfo*>(dwl->info))
|
||||
{
|
||||
faction = getRandomGenerator().nextInt((int)VLC->townh->factions.size() - 1);
|
||||
faction = getRandomGenerator().nextInt((int)VLC->townh->size() - 1);
|
||||
if(info->asCastle && info->instanceId != "")
|
||||
{
|
||||
auto iter = map->instanceNames.find(info->instanceId);
|
||||
@ -583,7 +606,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
|
||||
dwl->info = nullptr;
|
||||
|
||||
std::pair<Obj, int> result(Obj::NO_OBJ, -1);
|
||||
CreatureID cid = VLC->townh->factions[faction]->town->creatures[level][0];
|
||||
CreatureID cid = (*VLC->townh)[faction]->town->creatures[level][0];
|
||||
|
||||
//NOTE: this will pick last dwelling with this creature (Mantis #900)
|
||||
//check for block map equality is better but more complex solution
|
||||
@ -594,7 +617,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
|
||||
{
|
||||
auto handler = dynamic_cast<const CDwellingInstanceConstructor*>(VLC->objtypeh->getHandlerFor(primaryID, entry).get());
|
||||
|
||||
if (handler->producesCreature(VLC->creh->creatures[cid]))
|
||||
if (handler->producesCreature(VLC->creh->objects[cid]))
|
||||
result = std::make_pair(primaryID, entry);
|
||||
}
|
||||
};
|
||||
@ -605,7 +628,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
|
||||
|
||||
if (result.first == Obj::NO_OBJ)
|
||||
{
|
||||
logGlobal->error("Error: failed to find dwelling for %s of level %d", VLC->townh->factions[faction]->name, int(level));
|
||||
logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->name, int(level));
|
||||
result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), getRandomGenerator()));
|
||||
}
|
||||
|
||||
@ -678,6 +701,7 @@ CGameState::CGameState()
|
||||
globalEffects.setDescription("Global effects");
|
||||
globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
|
||||
day = 0;
|
||||
services = nullptr;
|
||||
}
|
||||
|
||||
CGameState::~CGameState()
|
||||
@ -689,8 +713,14 @@ CGameState::~CGameState()
|
||||
ptr.second.dellNull();
|
||||
}
|
||||
|
||||
void CGameState::preInit(Services * services)
|
||||
{
|
||||
this->services = services;
|
||||
}
|
||||
|
||||
void CGameState::init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap)
|
||||
{
|
||||
preInitAuto();
|
||||
logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed);
|
||||
getRandomGenerator().setSeed(si->seedToBeUsed);
|
||||
scenarioOps = CMemorySerializer::deepCopy(*si).release();
|
||||
@ -759,13 +789,62 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
|
||||
{
|
||||
switch(metatype)
|
||||
{
|
||||
case Metatype::ARTIFACT_INSTANCE:
|
||||
logGlobal->error("Artifact instance update is not implemented");
|
||||
break;
|
||||
case Metatype::CREATURE_INSTANCE:
|
||||
logGlobal->error("Creature instance update is not implemented");
|
||||
break;
|
||||
case Metatype::HERO_INSTANCE:
|
||||
//index is hero type
|
||||
if(index >= 0 && index < map->allHeroes.size())
|
||||
{
|
||||
CGHeroInstance * hero = map->allHeroes.at(index);
|
||||
hero->updateFrom(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Update entity: hero index %s is out of range [%d,%d]", index, 0, map->allHeroes.size());
|
||||
}
|
||||
break;
|
||||
case Metatype::MAP_OBJECT_INSTANCE:
|
||||
if(index >= 0 && index < map->objects.size())
|
||||
{
|
||||
CGObjectInstance * obj = getObjInstance(ObjectInstanceID(index));
|
||||
obj->updateFrom(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Update entity: object index %s is out of range [%d,%d]", index, 0, map->objects.size());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
services->updateEntity(metatype, index, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::updateOnLoad(StartInfo * si)
|
||||
{
|
||||
preInitAuto();
|
||||
scenarioOps->playerInfos = si->playerInfos;
|
||||
for(auto & i : si->playerInfos)
|
||||
gs->players[i.first].human = i.second.isControlledByHuman();
|
||||
}
|
||||
|
||||
void CGameState::preInitAuto()
|
||||
{
|
||||
if(services == nullptr)
|
||||
{
|
||||
logGlobal->error("Game state preinit missing");
|
||||
preInit(VLC);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap)
|
||||
{
|
||||
if(scenarioOps->createRandomMap())
|
||||
@ -1364,7 +1443,7 @@ void CGameState::initHeroes()
|
||||
}
|
||||
|
||||
hero->initHero(getRandomGenerator());
|
||||
getPlayer(hero->getOwner())->heroes.push_back(hero);
|
||||
getPlayerState(hero->getOwner())->heroes.push_back(hero);
|
||||
map->allHeroes[hero->type->ID.getNum()] = hero;
|
||||
}
|
||||
|
||||
@ -1483,7 +1562,7 @@ void CGameState::giveCampaignBonusToHero(CGHeroInstance * hero)
|
||||
break;
|
||||
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
|
||||
{
|
||||
CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2).toSpell());
|
||||
CArtifactInstance * scroll = CArtifactInstance::createScroll(SpellID(curBonus->info2));
|
||||
scroll->putAt(ArtifactLocation(hero, scroll->firstAvailableSlot(hero)));
|
||||
}
|
||||
break;
|
||||
@ -1561,7 +1640,7 @@ void CGameState::initStartingBonus()
|
||||
break;
|
||||
case PlayerSettings::RESOURCE:
|
||||
{
|
||||
int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
|
||||
int res = (*VLC->townh)[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
|
||||
if(res == Res::WOOD_AND_ORE)
|
||||
{
|
||||
int amount = getRandomGenerator().nextInt(5, 10);
|
||||
@ -1581,11 +1660,10 @@ void CGameState::initStartingBonus()
|
||||
logGlobal->error("Cannot give starting artifact - no heroes!");
|
||||
break;
|
||||
}
|
||||
CArtifact *toGive;
|
||||
toGive = VLC->arth->artifacts[VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE)];
|
||||
const Artifact * toGive = VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toArtifact(VLC->artifacts());
|
||||
|
||||
CGHeroInstance *hero = elem.second.heroes[0];
|
||||
giveHeroArtifact(hero, toGive->id);
|
||||
giveHeroArtifact(hero, toGive->getId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1605,7 +1683,7 @@ void CGameState::initTowns()
|
||||
{
|
||||
for (int g=0; g<map->towns.size(); ++g)
|
||||
{
|
||||
PlayerState * owner = getPlayer(map->towns[g]->getOwner());
|
||||
PlayerState * owner = getPlayerState(map->towns[g]->getOwner());
|
||||
if (owner)
|
||||
{
|
||||
PlayerInfo & pi = map->players[owner->color.getNum()];
|
||||
@ -1631,7 +1709,7 @@ void CGameState::initTowns()
|
||||
CGTownInstance * vti =(elem);
|
||||
if(!vti->town)
|
||||
{
|
||||
vti->town = VLC->townh->factions[vti->subID]->town;
|
||||
vti->town = (*VLC->townh)[vti->subID]->town;
|
||||
}
|
||||
if(vti->name.empty())
|
||||
{
|
||||
@ -1738,7 +1816,7 @@ void CGameState::initTowns()
|
||||
}
|
||||
vti->possibleSpells.clear();
|
||||
if(vti->getOwner() != PlayerColor::NEUTRAL)
|
||||
getPlayer(vti->getOwner())->towns.push_back(vti);
|
||||
getPlayerState(vti->getOwner())->towns.push_back(vti);
|
||||
|
||||
}
|
||||
}
|
||||
@ -1906,7 +1984,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
|
||||
if (nid != base->idNumber) //in very specific case the upgrade is available by default (?)
|
||||
{
|
||||
ret.newID.push_back(nid);
|
||||
ret.cost.push_back(VLC->creh->creatures[nid]->cost - base->cost);
|
||||
ret.cost.push_back(VLC->creh->objects[nid]->cost - base->cost);
|
||||
}
|
||||
}
|
||||
t = h->visitedTown;
|
||||
@ -1922,7 +2000,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
|
||||
if(vstd::contains(base->upgrades, upgrID)) //possible upgrade
|
||||
{
|
||||
ret.newID.push_back(upgrID);
|
||||
ret.cost.push_back(VLC->creh->creatures[upgrID]->cost - base->cost);
|
||||
ret.cost.push_back(VLC->creh->objects[upgrID]->cost - base->cost);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1938,7 +2016,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
|
||||
for(auto nid : base->upgrades)
|
||||
{
|
||||
ret.newID.push_back(nid);
|
||||
ret.cost.push_back((VLC->creh->creatures[nid]->cost - base->cost) * costModifier / 100);
|
||||
ret.cost.push_back((VLC->creh->objects[nid]->cost - base->cost) * costModifier / 100);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2171,7 +2249,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
|
||||
return this->checkForVictory(player, condition);
|
||||
};
|
||||
|
||||
const PlayerState *p = CGameInfoCallback::getPlayer(player);
|
||||
const PlayerState *p = CGameInfoCallback::getPlayerState(player);
|
||||
|
||||
//cheater or tester, but has entered the code...
|
||||
if (p->enteredWinningCheatCode)
|
||||
@ -2201,7 +2279,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
|
||||
|
||||
bool CGameState::checkForVictory(PlayerColor player, const EventCondition & condition) const
|
||||
{
|
||||
const PlayerState *p = CGameInfoCallback::getPlayer(player);
|
||||
const PlayerState *p = CGameInfoCallback::getPlayerState(player);
|
||||
switch (condition.condition)
|
||||
{
|
||||
case EventCondition::STANDARD_WIN:
|
||||
@ -2377,7 +2455,7 @@ PlayerColor CGameState::checkForStandardWin() const
|
||||
bool CGameState::checkForStandardLoss( PlayerColor player ) const
|
||||
{
|
||||
//std loss condition is: player lost all towns and heroes
|
||||
const PlayerState &p = *CGameInfoCallback::getPlayer(player);
|
||||
const PlayerState &p = *CGameInfoCallback::getPlayerState(player);
|
||||
return !p.heroes.size() && !p.towns.size();
|
||||
}
|
||||
|
||||
@ -2631,7 +2709,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
|
||||
for(auto it = elem->Slots().begin(); it != elem->Slots().end(); ++it)
|
||||
{
|
||||
int toCmp = it->second->type->idNumber; //ID of creature we should compare with the best one
|
||||
if(bestCre == -1 || VLC->creh->creatures[bestCre]->AIValue < VLC->creh->creatures[toCmp]->AIValue)
|
||||
if(bestCre == -1 || VLC->creh->objects[bestCre]->AIValue < VLC->creh->objects[toCmp]->AIValue)
|
||||
{
|
||||
bestCre = toCmp;
|
||||
}
|
||||
@ -2683,7 +2761,7 @@ void CGameState::buildGlobalTeamPlayerTree()
|
||||
|
||||
for(PlayerColor teamMember : k->second.players)
|
||||
{
|
||||
PlayerState *p = getPlayer(teamMember);
|
||||
PlayerState *p = getPlayerState(teamMember);
|
||||
assert(p);
|
||||
p->attachTo(t);
|
||||
}
|
||||
@ -2701,7 +2779,7 @@ void CGameState::attachArmedObjects()
|
||||
|
||||
void CGameState::giveHeroArtifact(CGHeroInstance *h, ArtifactID aid)
|
||||
{
|
||||
CArtifact * const artifact = VLC->arth->artifacts[aid]; //pointer to constant object
|
||||
CArtifact * const artifact = VLC->arth->objects[aid]; //pointer to constant object
|
||||
CArtifactInstance *ai = CArtifactInstance::createNewArtifactInstance(artifact);
|
||||
map->addNewArtifactInstance(ai);
|
||||
ai->putAt(ArtifactLocation(h, ai->firstAvailableSlot(h)));
|
||||
@ -2713,7 +2791,7 @@ std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllow
|
||||
for(int i = 0; i < map->allowedHeroes.size(); i++)
|
||||
if(map->allowedHeroes[i] || alsoIncludeNotAllowed)
|
||||
ret.insert(HeroTypeID(i));
|
||||
|
||||
|
||||
for(auto & playerSettingPair : scenarioOps->playerInfos) //remove uninitialized yet heroes picked for start by other players
|
||||
{
|
||||
if(playerSettingPair.second.hero != PlayerSettings::RANDOM)
|
||||
@ -2809,14 +2887,14 @@ void CGameState::replaceHeroesPlaceholders(const std::vector<CGameState::Campaig
|
||||
heroToPlace->id = campaignHeroReplacement.heroPlaceholderId;
|
||||
heroToPlace->tempOwner = heroPlaceholder->tempOwner;
|
||||
heroToPlace->pos = heroPlaceholder->pos;
|
||||
heroToPlace->type = VLC->heroh->heroes[heroToPlace->subID];
|
||||
heroToPlace->type = VLC->heroh->objects[heroToPlace->subID];
|
||||
|
||||
for(auto &&i : heroToPlace->stacks)
|
||||
i.second->type = VLC->creh->creatures[i.second->getCreatureID()];
|
||||
i.second->type = VLC->creh->objects[i.second->getCreatureID()];
|
||||
|
||||
auto fixArtifact = [&](CArtifactInstance * art)
|
||||
{
|
||||
art->artType = VLC->arth->artifacts[art->artType->id];
|
||||
art->artType = VLC->arth->objects[art->artType->id];
|
||||
gs->map->artInstances.push_back(art);
|
||||
art->id = ArtifactInstanceID((si32)gs->map->artInstances.size() - 1);
|
||||
};
|
||||
@ -2863,38 +2941,6 @@ CGHeroInstance * CGameState::getUsedHero(HeroTypeID hid) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PlayerState::PlayerState()
|
||||
: color(-1), human(false), enteredWinningCheatCode(false),
|
||||
enteredLosingCheatCode(false), status(EPlayerStatus::INGAME)
|
||||
{
|
||||
setNodeType(PLAYER);
|
||||
}
|
||||
|
||||
PlayerState::PlayerState(PlayerState && other):
|
||||
CBonusSystemNode(std::move(other)),
|
||||
color(other.color),
|
||||
human(other.human),
|
||||
team(other.team),
|
||||
resources(other.resources),
|
||||
enteredWinningCheatCode(other.enteredWinningCheatCode),
|
||||
enteredLosingCheatCode(other.enteredLosingCheatCode),
|
||||
status(other.status),
|
||||
daysWithoutCastle(other.daysWithoutCastle)
|
||||
{
|
||||
std::swap(visitedObjects, other.visitedObjects);
|
||||
std::swap(heroes, other.heroes);
|
||||
std::swap(towns, other.towns);
|
||||
std::swap(availableHeroes, other.availableHeroes);
|
||||
std::swap(dwellings, other.dwellings);
|
||||
std::swap(quests, other.quests);
|
||||
}
|
||||
|
||||
std::string PlayerState::nodeName() const
|
||||
{
|
||||
return "Player " + (color.getNum() < VLC->generaltexth->capColors.size() ? VLC->generaltexth->capColors[color.getNum()] : boost::lexical_cast<std::string>(color));
|
||||
}
|
||||
|
||||
|
||||
bool RumorState::update(int id, int extra)
|
||||
{
|
||||
if(vstd::contains(last, type))
|
||||
|
Reference in New Issue
Block a user