1
0
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:
AlexVinS
2018-03-17 17:58:30 +03:00
committed by AlexVinS
parent 11bb46780a
commit ecaa9f5d0b
475 changed files with 22491 additions and 7123 deletions

View File

@ -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))