mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Large rewrite of adventure map objects:
- replaced CDefObjInfo with ObjectTemplate class - ObjectTempate is a direct member of objects instead of pointer with shared ownership across CMap, handler and game objects - simplified handling of objects that can change appearance (e.g. towns) - all object queries regarding object appearance/blockmaps use h3m pos instead of relative positions - removed need of modhandler::reload - cleanup of some old code
This commit is contained in:
parent
2b9e074d54
commit
2c4c964a45
@ -706,7 +706,7 @@ void VCAI::makeTurnInternal()
|
||||
boost::sort (vec, isCloser);
|
||||
for (auto obj : vec)
|
||||
{
|
||||
if(!obj || !obj->defInfo || !cb->getObj(obj->id))
|
||||
if(!obj || !cb->getObj(obj->id))
|
||||
{
|
||||
logAi->errorStream() << "Error: there is wrong object on list for hero " << hero.first->name;
|
||||
continue;
|
||||
|
@ -954,11 +954,6 @@ void endGame()
|
||||
{
|
||||
client->endGame();
|
||||
vstd::clear_pointer(client);
|
||||
|
||||
delete CGI->dobjinfo.get();
|
||||
const_cast<CGameInfo*>(CGI)->dobjinfo = new CDefObjInfoHandler;
|
||||
|
||||
const_cast<CGameInfo*>(CGI)->modh->reload(); //add info about new creatures to dobjinfo
|
||||
}
|
||||
|
||||
void handleQuit()
|
||||
|
@ -335,12 +335,12 @@ void Graphics::loadFonts()
|
||||
|
||||
CDefEssential * Graphics::getDef( const CGObjectInstance * obj )
|
||||
{
|
||||
return advmapobjGraphics[obj->defInfo->name];
|
||||
return advmapobjGraphics[obj->appearance.animationFile];
|
||||
}
|
||||
|
||||
CDefEssential * Graphics::getDef( const CGDefInfo * info )
|
||||
CDefEssential * Graphics::getDef( const ObjectTemplate & info )
|
||||
{
|
||||
return advmapobjGraphics[info->name];
|
||||
return advmapobjGraphics[info.animationFile];
|
||||
}
|
||||
|
||||
void Graphics::loadErmuToPicture()
|
||||
|
@ -25,7 +25,7 @@ struct SDL_Color;
|
||||
struct InfoAboutHero;
|
||||
struct InfoAboutTown;
|
||||
class CGObjectInstance;
|
||||
class CGDefInfo;
|
||||
class ObjectTemplate;
|
||||
|
||||
enum EFonts
|
||||
{
|
||||
@ -60,7 +60,7 @@ public:
|
||||
|
||||
std::map<std::string, CDefEssential *> advmapobjGraphics;
|
||||
CDefEssential * getDef(const CGObjectInstance * obj);
|
||||
CDefEssential * getDef(const CGDefInfo * info);
|
||||
CDefEssential * getDef(const ObjectTemplate & info);
|
||||
//towns
|
||||
std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type
|
||||
//for battles
|
||||
|
@ -410,14 +410,8 @@ void NewStructures::applyCl( CClient *cl )
|
||||
CGTownInstance *town = GS(cl)->getTown(tid);
|
||||
for(const auto & id : bid)
|
||||
{
|
||||
if(id == BuildingID::CAPITOL) //fort or capitol
|
||||
{
|
||||
town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->capitols.at(town->subID).get());
|
||||
}
|
||||
if(id == BuildingID::FORT)
|
||||
{
|
||||
town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get());
|
||||
}
|
||||
town->updateAppearance();
|
||||
|
||||
if(vstd::contains(cl->playerint,town->tempOwner))
|
||||
cl->playerint[town->tempOwner]->buildChanged(town,id,1);
|
||||
}
|
||||
@ -427,10 +421,8 @@ void RazeStructures::applyCl (CClient *cl)
|
||||
CGTownInstance *town = GS(cl)->getTown(tid);
|
||||
for(const auto & id : bid)
|
||||
{
|
||||
if (id == BuildingID::CAPITOL) //fort or capitol
|
||||
{
|
||||
town->defInfo = const_cast<CGDefInfo*>(CGI->dobjinfo->gobjs.at(Obj::TOWN).at(town->subID).get());
|
||||
}
|
||||
town->updateAppearance();
|
||||
|
||||
if(vstd::contains (cl->playerint,town->tempOwner))
|
||||
cl->playerint[town->tempOwner]->buildChanged (town,id,2);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "mapHandler.h"
|
||||
|
||||
#include "CBitmapHandler.h"
|
||||
#include "gui/SDL_Extensions.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "../lib/CDefObjInfoHandler.h"
|
||||
@ -229,26 +230,26 @@ void CMapHandler::borderAndTerrainBitmapInit()
|
||||
delete bord;
|
||||
}
|
||||
|
||||
static void processDef (const CGDefInfo* def)
|
||||
static void processDef (const ObjectTemplate & objTempl)
|
||||
{
|
||||
if(def->id == Obj::EVENT)
|
||||
if(objTempl.id == Obj::EVENT)
|
||||
{
|
||||
graphics->advmapobjGraphics[def->name] = nullptr;
|
||||
graphics->advmapobjGraphics[objTempl.animationFile] = nullptr;
|
||||
return;
|
||||
}
|
||||
CDefEssential * ourDef = graphics->getDef(def);
|
||||
CDefEssential * ourDef = graphics->getDef(objTempl);
|
||||
if(!ourDef) //if object has already set handler (eg. heroes) it should not be overwritten
|
||||
{
|
||||
if(def->name.size())
|
||||
if(objTempl.animationFile.size())
|
||||
{
|
||||
graphics->advmapobjGraphics[def->name] = CDefHandler::giveDefEss(def->name);
|
||||
graphics->advmapobjGraphics[objTempl.animationFile] = CDefHandler::giveDefEss(objTempl.animationFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->warnStream() << "No def name for " << def->id << " " << def->subid;
|
||||
logGlobal->warnStream() << "No def name for " << objTempl.id << " " << objTempl.subid;
|
||||
return;
|
||||
}
|
||||
ourDef = graphics->getDef(def);
|
||||
ourDef = graphics->getDef(objTempl);
|
||||
|
||||
}
|
||||
//alpha transformation
|
||||
@ -266,44 +267,40 @@ void CMapHandler::initObjectRects()
|
||||
const CGObjectInstance *obj = elem;
|
||||
if( !obj
|
||||
|| (obj->ID==Obj::HERO && static_cast<const CGHeroInstance*>(obj)->inTownGarrison) //garrisoned hero
|
||||
|| (obj->ID==Obj::BOAT && static_cast<const CGBoat*>(obj)->hero) //boat with hero (hero graphics is used)
|
||||
|| !obj->defInfo )
|
||||
|| (obj->ID==Obj::BOAT && static_cast<const CGBoat*>(obj)->hero)) //boat with hero (hero graphics is used)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!graphics->getDef(obj)) //try to load it
|
||||
processDef(obj->defInfo);
|
||||
processDef(obj->appearance);
|
||||
if (!graphics->getDef(obj)) // stil no graphics? exit
|
||||
continue;
|
||||
|
||||
const SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap;
|
||||
for(int fx=0; fx<bitmap->w>>5; ++fx) //bitmap->w/32
|
||||
for(int fx=0; fx < obj->getWidth(); ++fx)
|
||||
{
|
||||
for(int fy=0; fy<bitmap->h>>5; ++fy) //bitmap->h/32
|
||||
for(int fy=0; fy < obj->getHeight(); ++fy)
|
||||
{
|
||||
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
|
||||
SDL_Rect cr;
|
||||
cr.w = 32;
|
||||
cr.h = 32;
|
||||
cr.x = fx<<5; //fx*32
|
||||
cr.y = fy<<5; //fy*32
|
||||
cr.x = bitmap->w - fx * 32 - 32;
|
||||
cr.y = bitmap->h - fy * 32 - 32;
|
||||
std::pair<const CGObjectInstance*,SDL_Rect> toAdd = std::make_pair(obj,cr);
|
||||
|
||||
if( (obj->pos.x + fx - bitmap->w/32+1) >= 0
|
||||
&& (obj->pos.x + fx - bitmap->w/32+1) < ttiles.size() - frameW
|
||||
&& (obj->pos.y + fy - bitmap->h/32+1) >= 0
|
||||
&& (obj->pos.y + fy - bitmap->h/32+1) < ttiles[0].size() - frameH
|
||||
|
||||
if( map->isInTheMap(currTile) && // within map
|
||||
cr.x + cr.w > 0 && // image has data on this tile
|
||||
cr.y + cr.h > 0 &&
|
||||
obj->coveringAt(currTile.x, currTile.y) // object is visible here
|
||||
)
|
||||
{
|
||||
//TerrainTile2 & curt =
|
||||
// ttiles
|
||||
// [obj->pos.x + fx - bitmap->w/32]
|
||||
//[obj->pos.y + fy - bitmap->h/32]
|
||||
//[obj->pos.z];
|
||||
ttiles[obj->pos.x + fx - bitmap->w/32+1][obj->pos.y + fy - bitmap->h/32+1][obj->pos.z].objects.push_back(toAdd);
|
||||
ttiles[currTile.x][currTile.y][currTile.z].objects.push_back(toAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // for(int fy=0; fy<bitmap->h/32; ++fy)
|
||||
} //for(int fx=0; fx<bitmap->w/32; ++fx)
|
||||
} // for(int f=0; f<map->objects.size(); ++f)
|
||||
|
||||
for(int ix=0; ix<ttiles.size()-frameW; ++ix)
|
||||
{
|
||||
@ -319,7 +316,7 @@ void CMapHandler::initObjectRects()
|
||||
|
||||
void CMapHandler::initHeroDef(const CGHeroInstance * h)
|
||||
{
|
||||
graphics->advmapobjGraphics[h->defInfo->name] = graphics->flags1[0];
|
||||
graphics->advmapobjGraphics[h->appearance.animationFile] = graphics->flags1[0];
|
||||
}
|
||||
|
||||
void CMapHandler::init()
|
||||
@ -367,9 +364,6 @@ void CMapHandler::init()
|
||||
}
|
||||
}
|
||||
|
||||
std::for_each(map->customDefs.begin(),map->customDefs.end(),processDef); //load h3m defs
|
||||
logGlobal->infoStream()<<"\tUnpacking and handling defs: "<<th.getDiff();
|
||||
|
||||
prepareFOWDefs();
|
||||
roadsRiverTerrainInit(); //road's and river's DefHandlers; and simple values initialization
|
||||
borderAndTerrainBitmapInit();
|
||||
@ -510,12 +504,16 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std::
|
||||
{
|
||||
const CGObjectInstance *obj = object.first;
|
||||
if (!graphics->getDef(obj))
|
||||
processDef(obj->defInfo);
|
||||
processDef(obj->appearance);
|
||||
if (!graphics->getDef(obj) && !obj->appearance.animationFile.empty())
|
||||
{
|
||||
logGlobal->errorStream() << "Failed to load image " << obj->appearance.animationFile;
|
||||
}
|
||||
|
||||
PlayerColor color = obj->tempOwner;
|
||||
|
||||
//checking if object has non-empty graphic on this tile
|
||||
if(obj->ID != Obj::HERO && !obj->coveringAt(top_tile.x + bx - obj->pos.x, top_tile.y + by - obj->pos.y))
|
||||
if(obj->ID != Obj::HERO && !obj->coveringAt(top_tile.x + bx, top_tile.y + by))
|
||||
continue;
|
||||
|
||||
static const int notBlittedInPuzzleMode[] = {124};
|
||||
@ -640,10 +638,7 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std::
|
||||
if(color < PlayerColor::PLAYER_LIMIT || color==PlayerColor::NEUTRAL)
|
||||
CSDL_Ext::setPlayerColor(bitmap, color);
|
||||
|
||||
if( obj->hasShadowAt(top_tile.x + bx - obj->pos.x, top_tile.y + by - obj->pos.y) )
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(bitmap,&pp,extSurf,&sr2);
|
||||
else
|
||||
CSDL_Ext::blitSurface(bitmap,&pp,extSurf,&sr2);
|
||||
}
|
||||
}
|
||||
//objects blitted
|
||||
@ -708,33 +703,41 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std::
|
||||
|
||||
//FoW blitted
|
||||
|
||||
// TODO: these should be activable by the console
|
||||
#ifdef MARK_BLOCKED_POSITIONS
|
||||
SDL_Rect tileRect = genRect(sr.h, sr.w, 0, 0);
|
||||
|
||||
if (settings["session"]["showBlock"].Bool())
|
||||
{
|
||||
if(map->getTile(int3(pos.x, pos.y, top_tile.z)).blocked) //temporary hiding blocked positions
|
||||
{
|
||||
static SDL_Surface * block = nullptr;
|
||||
if (!block)
|
||||
block = BitmapHandler::loadBitmap("blocked");
|
||||
|
||||
SDL_Rect sr;
|
||||
|
||||
sr.x=srx;
|
||||
sr.y=sry;
|
||||
sr.h=sr.w=32;
|
||||
|
||||
memset(rSurf->pixels, 128, rSurf->pitch * rSurf->h);
|
||||
CSDL_Ext::blitSurface(rSurf,&genRect(sr.h, sr.w, 0, 0),extSurf,&sr);
|
||||
CSDL_Ext::blitSurface(block, &tileRect, extSurf, &sr);
|
||||
}
|
||||
#endif
|
||||
#ifdef MARK_VISITABLE_POSITIONS
|
||||
}
|
||||
if (settings["session"]["showVisit"].Bool())
|
||||
{
|
||||
if(map->getTile(int3(pos.x, pos.y, top_tile.z)).visitable) //temporary hiding visitable positions
|
||||
{
|
||||
static SDL_Surface * visit = nullptr;
|
||||
if (!visit)
|
||||
visit = BitmapHandler::loadBitmap("visitable");
|
||||
|
||||
SDL_Rect sr;
|
||||
|
||||
sr.x=srx;
|
||||
sr.y=sry;
|
||||
sr.h=sr.w=32;
|
||||
|
||||
memset(rSurf->pixels, 128, rSurf->pitch * rSurf->h);
|
||||
CSDL_Ext::blitSurface(rSurf,&genRect(sr.h, sr.w, 0, 0),extSurf,&sr);
|
||||
CSDL_Ext::blitSurface(visit, &tileRect, extSurf, &sr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -849,7 +852,7 @@ std::pair<SDL_Surface *, bool> CMapHandler::getVisBitmap( const int3 & pos, cons
|
||||
bool CMapHandler::printObject(const CGObjectInstance *obj)
|
||||
{
|
||||
if (!graphics->getDef(obj))
|
||||
processDef(obj->defInfo);
|
||||
processDef(obj->appearance);
|
||||
|
||||
const SDL_Surface *bitmap = graphics->getDef(obj)->ourImages[0].bitmap;
|
||||
const int tilesW = bitmap->w/32;
|
||||
|
@ -657,6 +657,22 @@ void CArtHandler::afterLoadFinalization()
|
||||
bonus->sid = art->id;
|
||||
}
|
||||
}
|
||||
|
||||
//Note: "10" is used here because H3 text files don't define any template for art with ID 0
|
||||
ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::ARTIFACT, 10).front();
|
||||
for (CArtifact * art : artifacts)
|
||||
{
|
||||
if (!art->advMapDef.empty())
|
||||
{
|
||||
base.animationFile = art->advMapDef;
|
||||
base.subid = art->id;
|
||||
|
||||
// replace existing (if any) and add new template.
|
||||
// Necessary for objects added via mods that don't have any templates in H3
|
||||
VLC->dobjinfo->eraseAll(Obj::ARTIFACT, art->id);
|
||||
VLC->dobjinfo->registerTemplate(base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CArtifactInstance::CArtifactInstance()
|
||||
|
@ -1112,6 +1112,24 @@ void CCreatureHandler::buildBonusTreeForTiers()
|
||||
b.attachTo(&allCreatures);
|
||||
}
|
||||
|
||||
void CCreatureHandler::afterLoadFinalization()
|
||||
{
|
||||
ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::MONSTER, 0).front();
|
||||
for (CCreature * crea : creatures)
|
||||
{
|
||||
if (!crea->advMapDef.empty())
|
||||
{
|
||||
base.animationFile = crea->advMapDef;
|
||||
base.subid = crea->idNumber;
|
||||
|
||||
// replace existing (if any) and add new template.
|
||||
// Necessary for objects added via mods that don't have any templates in H3
|
||||
VLC->dobjinfo->eraseAll(Obj::MONSTER, crea->idNumber);
|
||||
VLC->dobjinfo->registerTemplate(base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCreatureHandler::deserializationFix()
|
||||
{
|
||||
buildBonusTreeForTiers();
|
||||
|
@ -198,6 +198,8 @@ public:
|
||||
/// generates tier-specific bonus tree entries
|
||||
void buildBonusTreeForTiers();
|
||||
|
||||
void afterLoadFinalization();
|
||||
|
||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
||||
|
||||
void loadObject(std::string scope, std::string name, const JsonNode & data) override;
|
||||
|
@ -2,9 +2,14 @@
|
||||
#include "CDefObjInfoHandler.h"
|
||||
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "../client/CGameInfo.h"
|
||||
#include "filesystem/CBinaryReader.h"
|
||||
//#include "../client/CGameInfo.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "GameConstants.h"
|
||||
#include "StringConstants.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CModHandler.h"
|
||||
#include "JsonNode.h"
|
||||
|
||||
/*
|
||||
* CDefObjInfoHandler.cpp, part of VCMI engine
|
||||
@ -16,104 +21,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
bool CGDefInfo::isVisitable() const
|
||||
static bool isVisitableFromTop(int identifier, int type)
|
||||
{
|
||||
for (auto & elem : visitMap)
|
||||
{
|
||||
if (elem)
|
||||
if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
CGDefInfo::CGDefInfo()
|
||||
{
|
||||
visitDir = (8|16|32|64|128); //4,5,6,7,8 - any not-from-up direction
|
||||
|
||||
width = height = -1;
|
||||
}
|
||||
|
||||
void CGDefInfo::fetchInfoFromMSK()
|
||||
{
|
||||
ResourceID resID("SPRITES/" + name, EResType::MASK);
|
||||
|
||||
if (CResourceHandler::get()->existsResource(resID))
|
||||
{
|
||||
auto msk = CResourceHandler::get()->load(resID)->readAll();
|
||||
|
||||
width = msk.first.get()[0];
|
||||
height = msk.first.get()[1];
|
||||
for(int i=0; i<6; ++i)
|
||||
{
|
||||
coverageMap[i] = msk.first.get()[i+2];
|
||||
shadowCoverage[i] = msk.first.get()[i+8];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//maximum possible size of H3 object
|
||||
//TODO: remove hardcode and move this data into modding system
|
||||
width = 8;
|
||||
height = 6;
|
||||
}
|
||||
}
|
||||
|
||||
CDefObjInfoHandler::CDefObjInfoHandler()
|
||||
{
|
||||
VLC->dobjinfo = this;
|
||||
|
||||
auto textFile = CResourceHandler::get()->load(ResourceID("DATA/OBJECTS.TXT"))->readAll();
|
||||
std::istringstream inp(std::string((char*)textFile.first.get(), textFile.second));
|
||||
int objNumber;
|
||||
inp>>objNumber;
|
||||
std::string mapStr;
|
||||
for(int hh=0; hh<objNumber; ++hh)
|
||||
{
|
||||
auto nobj = new CGDefInfo();
|
||||
std::string dump;
|
||||
inp>>nobj->name;
|
||||
|
||||
std::transform(nobj->name.begin(), nobj->name.end(), nobj->name.begin(), (int(*)(int))toupper);
|
||||
|
||||
for(int o=0; o<6; ++o)
|
||||
{
|
||||
nobj->blockMap[o] = 0xff;
|
||||
nobj->visitMap[o] = 0x00;
|
||||
nobj->coverageMap[o] = 0x00;
|
||||
nobj->shadowCoverage[o] = 0x00;
|
||||
}
|
||||
inp>>mapStr;
|
||||
std::reverse(mapStr.begin(), mapStr.end());
|
||||
for(int v=0; v<mapStr.size(); ++v)
|
||||
{
|
||||
if(mapStr[v]=='0')
|
||||
{
|
||||
nobj->blockMap[v/8] &= 255 - (128 >> (v%8));
|
||||
}
|
||||
}
|
||||
inp>>mapStr;
|
||||
std::reverse(mapStr.begin(), mapStr.end());
|
||||
for(int v=0; v<mapStr.size(); ++v)
|
||||
{
|
||||
if(mapStr[v]=='1')
|
||||
{
|
||||
nobj->visitMap[v/8] |= (128 >> (v%8));
|
||||
}
|
||||
}
|
||||
|
||||
for(int yy=0; yy<2; ++yy) //first - on which types of terrain object can be placed;
|
||||
inp>>dump; //second -in which terrains' menus object in the editor will be available (?)
|
||||
si32 id; inp >> id; nobj->id = Obj(id);
|
||||
inp>>nobj->subid;
|
||||
inp>>nobj->type;
|
||||
|
||||
nobj->visitDir = (8|16|32|64|128); //disabled visiting from the top
|
||||
|
||||
if(nobj->type == 2 || nobj->type == 3 || nobj->type == 4 || nobj->type == 5) //creature, hero, artifact, resource
|
||||
{
|
||||
nobj->visitDir = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const Obj visitableFromTop[] =
|
||||
{Obj::FLOTSAM,
|
||||
Obj::SEA_CHEST,
|
||||
@ -128,52 +40,368 @@ CDefObjInfoHandler::CDefObjInfoHandler()
|
||||
Obj::BORDERGUARD,
|
||||
Obj::BORDER_GATE,
|
||||
Obj::QUEST_GUARD,
|
||||
Obj::CORPSE};
|
||||
|
||||
for(auto & elem : visitableFromTop)
|
||||
{
|
||||
if(elem == nobj->id)
|
||||
{
|
||||
nobj->visitDir = 0xff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
inp >> nobj->printPriority;
|
||||
|
||||
//coverageMap calculating
|
||||
nobj->fetchInfoFromMSK();
|
||||
|
||||
auto dest = nobj->id.toDefObjInfo();
|
||||
if (dest.find(nobj->subid) != dest.end() && dest[nobj->subid] != nullptr)
|
||||
{
|
||||
// there is just too many of these. Note that this data is almost unused
|
||||
// exceptions are: town(village-capitol) and creation of new objects (holes, creatures, heroes, etc)
|
||||
//logGlobal->warnStream() << "Warning: overwriting def info for " << dest[nobj->subid]->name << " with " << nobj->name;
|
||||
dest[nobj->subid].dellNull(); // do not leak
|
||||
}
|
||||
|
||||
nobj->id.toDefObjInfo()[nobj->subid] = nobj;
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8 ; i++)
|
||||
{
|
||||
|
||||
static const char *holeDefs[] = {"AVLHOLD0.DEF", "AVLHLDS0.DEF", "AVLHOLG0.DEF", "AVLHLSN0.DEF",
|
||||
"AVLHOLS0.DEF", "AVLHOLR0.DEF", "AVLHOLX0.DEF", "AVLHOLL0.DEF"};
|
||||
|
||||
if(i)
|
||||
{
|
||||
gobjs[Obj::HOLE][i] = new CGDefInfo(*gobjs[Obj::HOLE][0]);
|
||||
}
|
||||
gobjs[Obj::HOLE][i]->name = holeDefs[i];
|
||||
}
|
||||
Obj::CORPSE
|
||||
};
|
||||
if (vstd::find_pos(visitableFromTop, identifier) != -1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
CDefObjInfoHandler::~CDefObjInfoHandler()
|
||||
ObjectTemplate::ObjectTemplate():
|
||||
visitDir(8|16|32|64|128), // all but top
|
||||
id(Obj::NO_OBJ),
|
||||
subid(0),
|
||||
printPriority(0)
|
||||
{
|
||||
for(auto & elem : gobjs)
|
||||
for(auto j=elem.second.begin(); j!=elem.second.end(); j++)
|
||||
j->second.dellNull();
|
||||
}
|
||||
|
||||
void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
|
||||
{
|
||||
std::string data = parser.readString();
|
||||
std::vector<std::string> strings;
|
||||
boost::split(strings, data, boost::is_any_of(" "));
|
||||
assert(strings.size() == 9);
|
||||
|
||||
animationFile = strings[0];
|
||||
|
||||
std::string & blockStr = strings[1]; //block map, 0 = blocked, 1 = unblocked
|
||||
std::string & visitStr = strings[2]; //visit map, 1 = visitable, 0 = not visitable
|
||||
|
||||
assert(blockStr.size() == 6*8);
|
||||
assert(visitStr.size() == 6*8);
|
||||
|
||||
setSize(8, 6);
|
||||
for (size_t i=0; i<6; i++) // 6 rows
|
||||
{
|
||||
for (size_t j=0; j<8; j++) // 8 columns
|
||||
{
|
||||
auto & tile = usedTiles[i][j];
|
||||
tile |= VISIBLE; // assume that all tiles are visible
|
||||
if (blockStr[i*8 + j] == '0')
|
||||
tile |= BLOCKED;
|
||||
|
||||
if (visitStr[i*8 + j] == '1')
|
||||
tile |= VISITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
// strings[3] most likely - terrains on which this object can be placed in editor.
|
||||
// e.g. Whirpool can be placed manually only on water while mines can be placed everywhere despite terrain-specific gfx
|
||||
// so these two fields can be interpreted as "strong affinity" and "weak affinity" towards terrains
|
||||
std::string & terrStr = strings[4]; // allowed terrains, 1 = object can be placed on this terrain
|
||||
|
||||
assert(terrStr.size() == 9); // all terrains but rock
|
||||
for (size_t i=0; i<9; i++)
|
||||
{
|
||||
if (terrStr[8-i] == '1')
|
||||
allowedTerrains.insert(ETerrainType(i));
|
||||
}
|
||||
|
||||
id = Obj(boost::lexical_cast<int>(strings[5]));
|
||||
subid = boost::lexical_cast<int>(strings[6]);
|
||||
int type = boost::lexical_cast<int>(strings[7]);
|
||||
printPriority = boost::lexical_cast<int>(strings[8]) * 100; // to have some space in future
|
||||
|
||||
if (isVisitableFromTop(id, type))
|
||||
visitDir = 0xff;
|
||||
else
|
||||
visitDir = (8|16|32|64|128);
|
||||
|
||||
readMsk();
|
||||
}
|
||||
|
||||
void ObjectTemplate::readMsk()
|
||||
{
|
||||
ResourceID resID("SPRITES/" + animationFile, EResType::MASK);
|
||||
|
||||
if (CResourceHandler::get()->existsResource(resID))
|
||||
{
|
||||
auto msk = CResourceHandler::get()->load(resID)->readAll();
|
||||
setSize(msk.first.get()[0], msk.first.get()[1]);
|
||||
}
|
||||
else //maximum possible size of H3 object //TODO: remove hardcode and move this data into modding system
|
||||
{
|
||||
setSize(8, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectTemplate::readMap(CBinaryReader & reader)
|
||||
{
|
||||
animationFile = reader.readString();
|
||||
|
||||
setSize(8, 6);
|
||||
ui8 blockMask[6];
|
||||
ui8 visitMask[6];
|
||||
for(auto & byte : blockMask)
|
||||
byte = reader.readUInt8();
|
||||
for(auto & byte : visitMask)
|
||||
byte = reader.readUInt8();
|
||||
|
||||
for (size_t i=0; i<6; i++) // 6 rows
|
||||
{
|
||||
for (size_t j=0; j<8; j++) // 8 columns
|
||||
{
|
||||
auto & tile = usedTiles[5 - i][7 - j];
|
||||
tile |= VISIBLE; // assume that all tiles are visible
|
||||
if (((blockMask[i] >> j) & 1 ) == 0)
|
||||
tile |= BLOCKED;
|
||||
|
||||
if (((visitMask[i] >> j) & 1 ) != 0)
|
||||
tile |= VISITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
reader.readUInt16();
|
||||
ui16 terrMask = reader.readUInt16();
|
||||
for (size_t i=0; i<9; i++)
|
||||
{
|
||||
if (((terrMask >> i) & 1 ) != 0)
|
||||
allowedTerrains.insert(ETerrainType(i));
|
||||
}
|
||||
|
||||
id = Obj(reader.readUInt32());
|
||||
subid = reader.readUInt32();
|
||||
int type = reader.readUInt8();
|
||||
printPriority = reader.readUInt8() * 100; // to have some space in future
|
||||
|
||||
if (isVisitableFromTop(id, type))
|
||||
visitDir = 0xff;
|
||||
else
|
||||
visitDir = (8|16|32|64|128);
|
||||
|
||||
reader.skip(16);
|
||||
readMsk();
|
||||
|
||||
if (id == Obj::EVENT)
|
||||
{
|
||||
setSize(1,1);
|
||||
usedTiles[0][0] = VISITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectTemplate::readJson(const JsonNode &node)
|
||||
{
|
||||
id = Obj(node["basebase"].Float()); // temporary, should be replaced
|
||||
subid = node["base"].Float();
|
||||
animationFile = node["animation"].String();
|
||||
|
||||
const JsonVector & visitDirs = node["visitableFrom"].Vector();
|
||||
if (!visitDirs.empty())
|
||||
{
|
||||
if (visitDirs[0].String()[0] == '+') visitDir |= 1;
|
||||
if (visitDirs[0].String()[1] == '+') visitDir |= 2;
|
||||
if (visitDirs[0].String()[2] == '+') visitDir |= 4;
|
||||
if (visitDirs[1].String()[2] == '+') visitDir |= 8;
|
||||
if (visitDirs[2].String()[2] == '+') visitDir |= 16;
|
||||
if (visitDirs[2].String()[1] == '+') visitDir |= 32;
|
||||
if (visitDirs[2].String()[0] == '+') visitDir |= 64;
|
||||
if (visitDirs[1].String()[0] == '+') visitDir |= 128;
|
||||
}
|
||||
else
|
||||
visitDir = 0xff;
|
||||
|
||||
for (auto & entry : node["allowedTerrains"].Vector())
|
||||
allowedTerrains.insert(ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, entry.String())));
|
||||
|
||||
auto charToTile = [&](const char & ch) -> ui8
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '0' : return 0;
|
||||
case 'V' : return VISIBLE;
|
||||
case 'B' : return VISIBLE | BLOCKED;
|
||||
case 'H' : return BLOCKED;
|
||||
case 'A' : return VISIBLE | BLOCKED | VISITABLE;
|
||||
case 'T' : return VISIBLE | VISITABLE;
|
||||
default:
|
||||
logGlobal->errorStream() << "Unrecognized char " << ch << " in template mask";
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
size_t maxHeight = 0, maxWidth = 0;
|
||||
size_t width = node["mask"].Vector()[0].String().size();
|
||||
size_t height = node["mask"].Vector().size();
|
||||
setSize(width, height);
|
||||
|
||||
for (size_t i=0; i<height; i++)
|
||||
{
|
||||
const std::string & line = node["mask"].Vector()[i].String();
|
||||
assert(line.size() == width);
|
||||
for (size_t j=0; j < width; j++)
|
||||
{
|
||||
ui8 tile = charToTile(line[j]);
|
||||
if (tile != 0)
|
||||
{
|
||||
vstd::amax(maxHeight, i);
|
||||
vstd::amax(maxWidth, j);
|
||||
usedTiles[i][j] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
setSize(maxWidth, maxHeight);
|
||||
|
||||
printPriority = node["zIndex"].Float();
|
||||
}
|
||||
|
||||
ui32 ObjectTemplate::getWidth() const
|
||||
{
|
||||
return usedTiles.empty() ? 0 : usedTiles.front().size();
|
||||
}
|
||||
|
||||
ui32 ObjectTemplate::getHeight() const
|
||||
{
|
||||
return usedTiles.size();
|
||||
}
|
||||
|
||||
void ObjectTemplate::setSize(ui32 width, ui32 height)
|
||||
{
|
||||
usedTiles.resize(height);
|
||||
for (auto & line : usedTiles)
|
||||
line.resize(width);
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isVisitable() const
|
||||
{
|
||||
for (auto & line : usedTiles)
|
||||
for (auto & tile : line)
|
||||
if (tile & VISITABLE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isWithin(si32 X, si32 Y) const
|
||||
{
|
||||
if (X < 0 || Y < 0)
|
||||
return false;
|
||||
if (X >= getWidth() || Y >= getHeight())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isVisitableAt(si32 X, si32 Y) const
|
||||
{
|
||||
if (isWithin(X, Y))
|
||||
return usedTiles[Y][X] & VISITABLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isVisibleAt(si32 X, si32 Y) const
|
||||
{
|
||||
if (isWithin(X, Y))
|
||||
return usedTiles[Y][X] & VISIBLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isBlockedAt(si32 X, si32 Y) const
|
||||
{
|
||||
if (isWithin(X, Y))
|
||||
return usedTiles[Y][X] & BLOCKED;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const
|
||||
{
|
||||
// visitDir uses format
|
||||
// 1 2 3
|
||||
// 8 4
|
||||
// 7 6 5
|
||||
int dirMap[3][3] =
|
||||
{
|
||||
{ visitDir & 1, visitDir & 2, visitDir & 4 },
|
||||
{ visitDir & 128, 1 , visitDir & 8 },
|
||||
{ visitDir & 64, visitDir & 32, visitDir & 16 }
|
||||
};
|
||||
// map input values to range 0..2
|
||||
int dx = X < 0 ? 0 : X == 0 ? 1 : 2;
|
||||
int dy = Y < 0 ? 0 : Y == 0 ? 1 : 2;
|
||||
|
||||
return dirMap[dy][dx] != 0;
|
||||
}
|
||||
|
||||
bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const
|
||||
{
|
||||
return allowedTerrains.count(terrain) != 0;
|
||||
}
|
||||
|
||||
void CDefObjInfoHandler::readTextFile(std::string path)
|
||||
{
|
||||
CLegacyConfigParser parser(path);
|
||||
size_t totalNumber = parser.readNumber(); // first line contains number of objects to read and nothing else
|
||||
parser.endLine();
|
||||
|
||||
for (size_t i=0; i<totalNumber; i++)
|
||||
{
|
||||
ObjectTemplate templ;
|
||||
templ.readTxt(parser);
|
||||
parser.endLine();
|
||||
objects.push_back(templ);
|
||||
}
|
||||
}
|
||||
|
||||
CDefObjInfoHandler::CDefObjInfoHandler()
|
||||
{
|
||||
readTextFile("Data/Objects.txt");
|
||||
readTextFile("Data/Heroes.txt");
|
||||
/*
|
||||
const JsonNode node = JsonUtils::assembleFromFiles("config/objectTemplates.json");
|
||||
std::vector<ObjectTemplate> newTemplates;
|
||||
newTemplates.reserve(node.Struct().size());
|
||||
|
||||
// load all new templates
|
||||
for (auto & entry : node.Struct())
|
||||
{
|
||||
ObjectTemplate templ;
|
||||
templ.readJson(entry.second);
|
||||
newTemplates.push_back(templ);
|
||||
}
|
||||
|
||||
// erase old ones to avoid conflicts
|
||||
for (auto & entry : newTemplates)
|
||||
eraseAll(entry.id, entry.subid);
|
||||
|
||||
// merge new templates into storage
|
||||
objects.insert(objects.end(), newTemplates.begin(), newTemplates.end());
|
||||
*/
|
||||
}
|
||||
|
||||
void CDefObjInfoHandler::eraseAll(Obj type, si32 subtype)
|
||||
{
|
||||
auto it = std::remove_if(objects.begin(), objects.end(), [&](const ObjectTemplate & obj)
|
||||
{
|
||||
return obj.id == type && obj.subid == subtype;
|
||||
});
|
||||
objects.erase(it, objects.end());
|
||||
}
|
||||
|
||||
void CDefObjInfoHandler::registerTemplate(ObjectTemplate obj)
|
||||
{
|
||||
objects.push_back(obj);
|
||||
}
|
||||
|
||||
std::vector<ObjectTemplate> CDefObjInfoHandler::pickCandidates(Obj type, si32 subtype) const
|
||||
{
|
||||
std::vector<ObjectTemplate> ret;
|
||||
|
||||
std::copy_if(objects.begin(), objects.end(), std::back_inserter(ret), [&](const ObjectTemplate & obj)
|
||||
{
|
||||
return obj.id == type && obj.subid == subtype;
|
||||
});
|
||||
if (ret.empty())
|
||||
logGlobal->errorStream() << "Failed to find template for " << type << ":" << subtype;
|
||||
assert(!ret.empty()); // Can't create object of this type/subtype
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<ObjectTemplate> CDefObjInfoHandler::pickCandidates(Obj type, si32 subtype, ETerrainType terrain) const
|
||||
{
|
||||
std::vector<ObjectTemplate> ret = pickCandidates(type, subtype);
|
||||
std::vector<ObjectTemplate> filtered;
|
||||
|
||||
std::copy_if(ret.begin(), ret.end(), std::back_inserter(filtered), [&](const ObjectTemplate & obj)
|
||||
{
|
||||
return obj.canBePlacedAt(terrain);
|
||||
});
|
||||
// it is possible that there are no templates usable on specific terrain. In this case - return list before filtering
|
||||
return filtered.empty() ? ret : filtered;
|
||||
}
|
||||
|
@ -13,52 +13,96 @@
|
||||
*
|
||||
*/
|
||||
|
||||
class CDefEssential;
|
||||
class DLL_LINKAGE CGDefInfo
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
class CBinaryReader;
|
||||
class CLegacyConfigParser;
|
||||
class JsonNode;
|
||||
|
||||
ui8 visitMap[6];
|
||||
ui8 blockMap[6];
|
||||
ui8 coverageMap[6], shadowCoverage[6]; //to determine which tiles are covered by picture of this object
|
||||
ui8 visitDir; //directions from which object can be entered, format same as for moveDir in CGHeroInstance(but 0 - 7)
|
||||
Obj id;
|
||||
si32 subid; //of object described by this defInfo
|
||||
si32 terrainAllowed, //on which terrain it is possible to place object
|
||||
terrainMenu; //in which menus in map editor object will be showed
|
||||
si32 width, height; //tiles
|
||||
si32 type; //(0- ground, 1- towns, 2-creatures, 3- heroes, 4-artifacts, 5- resources)
|
||||
si32 printPriority;
|
||||
bool isVisitable() const;
|
||||
bool operator<(const CGDefInfo& por) const
|
||||
class DLL_LINKAGE ObjectTemplate
|
||||
{
|
||||
enum EBlockMapBits
|
||||
{
|
||||
if(id!=por.id)
|
||||
return id<por.id;
|
||||
else
|
||||
return subid<por.subid;
|
||||
}
|
||||
VISIBLE = 1,
|
||||
VISITABLE = 2,
|
||||
BLOCKED = 4
|
||||
};
|
||||
|
||||
/// tiles that are covered by this object, uses EBlockMapBits enum as flags
|
||||
std::vector<std::vector<ui8>> usedTiles;
|
||||
/// directions from which object can be entered, format same as for moveDir in CGHeroInstance(but 0 - 7)
|
||||
ui8 visitDir;
|
||||
/// list of terrains on which this object can be placed
|
||||
std::set<ETerrainType> allowedTerrains;
|
||||
|
||||
public:
|
||||
/// H3 ID/subID of this object
|
||||
Obj id;
|
||||
si32 subid;
|
||||
/// print priority, objects with higher priority will be print first, below everything else
|
||||
si32 printPriority;
|
||||
/// animation file that should be used to display object
|
||||
std::string animationFile;
|
||||
|
||||
ui32 getWidth() const;
|
||||
ui32 getHeight() const;
|
||||
void setSize(ui32 width, ui32 height);
|
||||
|
||||
bool isVisitable() const;
|
||||
|
||||
// Checks object used tiles
|
||||
// Position is relative to bottom-right corner of the object, can not be negative
|
||||
bool isWithin(si32 X, si32 Y) const;
|
||||
bool isVisitableAt(si32 X, si32 Y) const;
|
||||
bool isVisibleAt(si32 X, si32 Y) const;
|
||||
bool isBlockedAt(si32 X, si32 Y) const;
|
||||
|
||||
// Checks if object is visitable from certain direction. X and Y must be between -1..+1
|
||||
bool isVisitableFrom(si8 X, si8 Y) const;
|
||||
|
||||
// Checks if object can be placed on specific terrain
|
||||
bool canBePlacedAt(ETerrainType terrain) const;
|
||||
|
||||
ObjectTemplate();
|
||||
|
||||
void readTxt(CLegacyConfigParser & parser);
|
||||
void readMsk();
|
||||
void readMap(CBinaryReader & reader);
|
||||
void readJson(const JsonNode & node);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & name & visitMap & blockMap & visitDir & id & subid &terrainAllowed
|
||||
& terrainMenu & width & height & type & printPriority & coverageMap & shadowCoverage;
|
||||
h & usedTiles & allowedTerrains & animationFile;
|
||||
h & id & subid & printPriority & visitDir;
|
||||
}
|
||||
CGDefInfo();
|
||||
void fetchInfoFromMSK();
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CDefObjInfoHandler
|
||||
{
|
||||
public:
|
||||
std::map<int, std::map<int, ConstTransitivePtr<CGDefInfo> > > gobjs;
|
||||
/// list of all object templates loaded from text files
|
||||
/// actual object have ObjectTemplate as member "appearance"
|
||||
std::vector<ObjectTemplate> objects;
|
||||
|
||||
std::map<TFaction, ConstTransitivePtr<CGDefInfo> > capitols;
|
||||
std::map<TFaction, ConstTransitivePtr<CGDefInfo> > villages;
|
||||
/// reads one of H3 text files that contain object templates description
|
||||
void readTextFile(std::string path);
|
||||
public:
|
||||
|
||||
CDefObjInfoHandler();
|
||||
~CDefObjInfoHandler();
|
||||
|
||||
/// Erases all templates with given type/subtype
|
||||
void eraseAll(Obj type, si32 subtype);
|
||||
|
||||
/// Add new template into the list
|
||||
void registerTemplate(ObjectTemplate obj);
|
||||
|
||||
/// picks all possible candidates for specific pair <type, subtype>
|
||||
std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype) const;
|
||||
/// picks all candidates for <type, subtype> and of possible - also filters them by terrain
|
||||
std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain) const;
|
||||
|
||||
// TODO: as above, but also filters out templates that are not applicable according to accepted test
|
||||
// std::vector<ObjectTemplate> pickCandidates(Obj type, si32 subtype, ETerrainType terrain, std::function<bool(ObjectTemplate &)> accept) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & gobjs & capitols & villages;
|
||||
h & objects;
|
||||
}
|
||||
};
|
||||
|
@ -343,36 +343,23 @@ static CGObjectInstance * createObject(Obj id, int subid, int3 pos, PlayerColor
|
||||
switch(id)
|
||||
{
|
||||
case Obj::HERO:
|
||||
{
|
||||
auto nobj = new CGHeroInstance();
|
||||
nobj->pos = pos;
|
||||
nobj->tempOwner = owner;
|
||||
nobj->subID = subid;
|
||||
//nobj->initHero(ran);
|
||||
return nobj;
|
||||
}
|
||||
nobj = new CGHeroInstance();
|
||||
nobj->appearance = VLC->dobjinfo->pickCandidates(id, VLC->heroh->heroes[subid]->heroClass->id).front();
|
||||
break;
|
||||
case Obj::TOWN:
|
||||
nobj = new CGTownInstance;
|
||||
break;
|
||||
default: //rest of objects
|
||||
nobj = new CGObjectInstance;
|
||||
nobj->defInfo = id.toDefObjInfo()[subid];
|
||||
break;
|
||||
}
|
||||
nobj->ID = id;
|
||||
nobj->subID = subid;
|
||||
if(!nobj->defInfo)
|
||||
logGlobal->warnStream() <<"No def declaration for " <<id <<" "<<subid;
|
||||
nobj->pos = pos;
|
||||
//nobj->state = nullptr;//new CLuaObjectScript();
|
||||
nobj->tempOwner = owner;
|
||||
nobj->defInfo->id = id;
|
||||
nobj->defInfo->subid = subid;
|
||||
if (id != Obj::HERO)
|
||||
nobj->appearance = VLC->dobjinfo->pickCandidates(id, subid).front();
|
||||
|
||||
//assigning defhandler
|
||||
if(nobj->ID==Obj::HERO || nobj->ID==Obj::TOWN)
|
||||
return nobj;
|
||||
nobj->defInfo = id.toDefObjInfo()[subid];
|
||||
return nobj;
|
||||
}
|
||||
|
||||
@ -641,14 +628,11 @@ void CGameState::randomizeObject(CGObjectInstance *cur)
|
||||
{
|
||||
if(cur->ID==Obj::TOWN) //town - set def
|
||||
{
|
||||
const TerrainTile &tile = map->getTile(cur->pos);
|
||||
CGTownInstance *t = dynamic_cast<CGTownInstance*>(cur);
|
||||
t->town = VLC->townh->factions[t->subID]->town;
|
||||
if(t->hasCapitol())
|
||||
t->defInfo = VLC->dobjinfo->capitols[t->subID];
|
||||
else if(t->hasFort())
|
||||
t->defInfo = VLC->dobjinfo->gobjs[Obj::TOWN][t->subID];
|
||||
else
|
||||
t->defInfo = VLC->dobjinfo->villages[t->subID];
|
||||
t->appearance = VLC->dobjinfo->pickCandidates(Obj::TOWN, t->subID, tile.terType).front();
|
||||
t->updateAppearance();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -666,33 +650,33 @@ void CGameState::randomizeObject(CGObjectInstance *cur)
|
||||
}
|
||||
else if(ran.first==Obj::TOWN)//special code for town
|
||||
{
|
||||
const TerrainTile &tile = map->getTile(cur->pos);
|
||||
CGTownInstance *t = dynamic_cast<CGTownInstance*>(cur);
|
||||
if(!t) {logGlobal->warnStream()<<"Wrong random town at "<<cur->pos; return;}
|
||||
cur->ID = ran.first;
|
||||
cur->subID = ran.second;
|
||||
//FIXME: copy-pasted from above
|
||||
t->town = VLC->townh->factions[t->subID]->town;
|
||||
if(t->hasCapitol())
|
||||
t->defInfo = VLC->dobjinfo->capitols[t->subID];
|
||||
else if(t->hasFort())
|
||||
t->defInfo = VLC->dobjinfo->gobjs[Obj::TOWN][t->subID];
|
||||
else
|
||||
t->defInfo = VLC->dobjinfo->villages[t->subID];
|
||||
t->appearance = VLC->dobjinfo->pickCandidates(Obj::TOWN,t->subID, tile.terType).front();
|
||||
t->updateAppearance();
|
||||
|
||||
t->randomizeArmy(t->subID);
|
||||
map->towns.push_back(t);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ran.first != cur->appearance.id ||
|
||||
ran.second != cur->appearance.subid)
|
||||
{
|
||||
const TerrainTile &tile = map->getTile(cur->pos);
|
||||
cur->appearance = VLC->dobjinfo->pickCandidates(Obj(ran.first),ran.second, tile.terType).front();
|
||||
}
|
||||
}
|
||||
//we have to replace normal random object
|
||||
cur->ID = ran.first;
|
||||
cur->subID = ran.second;
|
||||
map->removeBlockVisTiles(cur); //recalculate blockvis tiles - picked object might have different than random placeholder
|
||||
map->customDefs.push_back(cur->defInfo = ran.first.toDefObjInfo()[ran.second]);
|
||||
if(!cur->defInfo)
|
||||
{
|
||||
logGlobal->errorStream()<<"*BIG* WARNING: Missing def declaration for "<<cur->ID<<" "<<cur->subID;
|
||||
return;
|
||||
}
|
||||
|
||||
map->removeBlockVisTiles(cur, true); //recalculate blockvis tiles - picked object might have different than random placeholder
|
||||
map->addBlockVisTiles(cur);
|
||||
}
|
||||
|
||||
@ -1189,7 +1173,6 @@ void CGameState::placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeI
|
||||
townPos.x += 1;
|
||||
|
||||
CGHeroInstance * hero = static_cast<CGHeroInstance*>(createObject(Obj::HERO, heroTypeId.getNum(), townPos, playerColor));
|
||||
hero->initHeroDefInfo();
|
||||
map->getEditManager()->insertObject(hero, townPos);
|
||||
}
|
||||
|
||||
@ -2205,14 +2188,15 @@ bool CGameState::isVisible( const CGObjectInstance *obj, boost::optional<PlayerC
|
||||
if(*player == PlayerColor::NEUTRAL) //-> TODO ??? needed?
|
||||
return false;
|
||||
//object is visible when at least one blocked tile is visible
|
||||
for(int fx=0; fx<8; ++fx)
|
||||
for(int fy=0; fy < obj->getHeight(); ++fy)
|
||||
{
|
||||
for(int fy=0; fy<6; ++fy)
|
||||
for(int fx=0; fx < obj->getWidth(); ++fx)
|
||||
{
|
||||
int3 pos = obj->pos + int3(fx-7,fy-5,0);
|
||||
if(map->isInTheMap(pos)
|
||||
&& !((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1)
|
||||
&& isVisible(pos, *player) )
|
||||
int3 pos = obj->pos + int3(-fx, -fy, 0);
|
||||
|
||||
if ( map->isInTheMap(pos) &&
|
||||
obj->coveringAt(pos.x, pos.y) &&
|
||||
isVisible(pos, *player))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2232,40 +2216,11 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom,
|
||||
if(!vstd::contains(pom->blockingObjects, pom->visitableObjects[b])) //this visitable object is not blocking, ignore
|
||||
continue;
|
||||
|
||||
CGDefInfo * di = pom->visitableObjects[b]->defInfo;
|
||||
if( (dst.x == src.x-1 && dst.y == src.y-1) && !(di->visitDir & (1<<4)) )
|
||||
{
|
||||
const CGObjectInstance * obj = pom->visitableObjects[b];
|
||||
|
||||
if (!obj->appearance.isVisitableFrom(src.x - dst.x, src.y - dst.y))
|
||||
return false;
|
||||
}
|
||||
if( (dst.x == src.x && dst.y == src.y-1) && !(di->visitDir & (1<<5)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( (dst.x == src.x+1 && dst.y == src.y-1) && !(di->visitDir & (1<<6)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( (dst.x == src.x+1 && dst.y == src.y) && !(di->visitDir & (1<<7)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( (dst.x == src.x+1 && dst.y == src.y+1) && !(di->visitDir & (1<<0)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( (dst.x == src.x && dst.y == src.y+1) && !(di->visitDir & (1<<1)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( (dst.x == src.x-1 && dst.y == src.y+1) && !(di->visitDir & (1<<2)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( (dst.x == src.x-1 && dst.y == src.y) && !(di->visitDir & (1<<3)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -223,6 +223,18 @@ void CHeroClassHandler::afterLoadFinalization()
|
||||
heroClass->selectionProbability[faction->index] = static_cast<int>(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it
|
||||
}
|
||||
}
|
||||
|
||||
ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::HERO, 0).front();
|
||||
for (CHeroClass * hc : heroClasses)
|
||||
{
|
||||
base.animationFile = hc->imageMapMale;
|
||||
base.subid = hc->id;
|
||||
|
||||
// replace existing (if any) and add new template.
|
||||
// Necessary for objects added via mods that don't have any templates in H3
|
||||
VLC->dobjinfo->eraseAll(Obj::HERO, hc->id);
|
||||
VLC->dobjinfo->registerTemplate(base);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bool> CHeroClassHandler::getDefaultAllowed() const
|
||||
|
@ -789,91 +789,4 @@ void CModHandler::afterLoad()
|
||||
|
||||
std::ofstream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::trunc);
|
||||
file << modSettings;
|
||||
reload();
|
||||
}
|
||||
|
||||
void CModHandler::reload()
|
||||
{
|
||||
{
|
||||
//recreate adventure map defs
|
||||
assert(!VLC->dobjinfo->gobjs[Obj::MONSTER].empty()); //make sure that at least some def info was found
|
||||
|
||||
const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::MONSTER].begin()->second;
|
||||
|
||||
for(auto & crea : VLC->creh->creatures)
|
||||
{
|
||||
if (!vstd::contains(VLC->dobjinfo->gobjs[Obj::MONSTER], crea->idNumber)) // no obj info for this type
|
||||
{
|
||||
auto info = new CGDefInfo(*baseInfo);
|
||||
info->subid = crea->idNumber;
|
||||
info->name = crea->advMapDef;
|
||||
|
||||
VLC->dobjinfo->gobjs[Obj::MONSTER][crea->idNumber] = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
assert(!VLC->dobjinfo->gobjs[Obj::ARTIFACT].empty());
|
||||
|
||||
const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::ARTIFACT].begin()->second;
|
||||
|
||||
for(auto & art : VLC->arth->artifacts)
|
||||
{
|
||||
if (!vstd::contains(VLC->dobjinfo->gobjs[Obj::ARTIFACT], art->id)) // no obj info for this type
|
||||
{
|
||||
auto info = new CGDefInfo(*baseInfo);
|
||||
info->subid = art->id;
|
||||
info->name = art->advMapDef;
|
||||
|
||||
VLC->dobjinfo->gobjs[Obj::ARTIFACT][art->id] = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
assert(!VLC->dobjinfo->gobjs[Obj::TOWN].empty()); //make sure that at least some def info was found
|
||||
|
||||
const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::TOWN].begin()->second;
|
||||
auto & townInfos = VLC->dobjinfo->gobjs[Obj::TOWN];
|
||||
|
||||
for(auto & faction : VLC->townh->factions)
|
||||
{
|
||||
TFaction index = faction->index;
|
||||
CTown * town = faction->town;
|
||||
if (town)
|
||||
{
|
||||
auto & cientInfo = town->clientInfo;
|
||||
|
||||
if (!vstd::contains(VLC->dobjinfo->gobjs[Obj::TOWN], index)) // no obj info for this type
|
||||
{
|
||||
auto info = new CGDefInfo(*baseInfo);
|
||||
info->subid = index;
|
||||
|
||||
townInfos[index] = info;
|
||||
}
|
||||
townInfos[index]->name = cientInfo.advMapCastle;
|
||||
|
||||
VLC->dobjinfo->villages[index] = new CGDefInfo(*townInfos[index]);
|
||||
VLC->dobjinfo->villages[index]->name = cientInfo.advMapVillage;
|
||||
|
||||
VLC->dobjinfo->capitols[index] = new CGDefInfo(*townInfos[index]);
|
||||
VLC->dobjinfo->capitols[index]->name = cientInfo.advMapCapitol;
|
||||
|
||||
for (int i = 0; i < town->dwellings.size(); ++i)
|
||||
{
|
||||
const CGDefInfo * baseInfo = VLC->dobjinfo->gobjs[Obj::CREATURE_GENERATOR1][i]; //get same blockmap as first dwelling of tier i
|
||||
|
||||
for (auto cre : town->creatures[i]) //both unupgraded and upgraded get same dwelling
|
||||
{
|
||||
auto info = new CGDefInfo(*baseInfo);
|
||||
info->subid = cre;
|
||||
info->name = town->dwellings[i];
|
||||
VLC->dobjinfo->gobjs[Obj::CREATURE_GENERATOR1][cre] = info;
|
||||
|
||||
VLC->objh->cregens[cre] = cre; //map of dwelling -> creature id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,10 +213,6 @@ public:
|
||||
void load();
|
||||
void afterLoad();
|
||||
|
||||
/// actions that should be triggered on map restart
|
||||
/// TODO: merge into appropriate handlers?
|
||||
void reload();
|
||||
|
||||
struct DLL_LINKAGE hardcodedFeatures
|
||||
{
|
||||
JsonNode data;
|
||||
|
@ -33,11 +33,6 @@
|
||||
|
||||
using namespace boost::assign;
|
||||
|
||||
// It looks that we can't rely on shadowCoverage correctness (Mantis #866). This may result
|
||||
// in notable performance decrease (SDL blit with custom alpha blit) not notable on my system (Ivan)
|
||||
#define USE_COVERAGE_MAP 0
|
||||
|
||||
|
||||
std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > CGTeleport::objs;
|
||||
std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > CGTeleport::gates;
|
||||
IGameCallback * IObjectInterface::cb = nullptr;
|
||||
@ -298,7 +293,6 @@ CGObjectInstance::CGObjectInstance():
|
||||
pos(-1,-1,-1),
|
||||
ID(Obj::NO_OBJ),
|
||||
subID(-1),
|
||||
defInfo(nullptr),
|
||||
tempOwner(PlayerColor::UNFLAGGABLE),
|
||||
blockVisit(false)
|
||||
{
|
||||
@ -323,62 +317,24 @@ void CGObjectInstance::setOwner(PlayerColor ow)
|
||||
}
|
||||
int CGObjectInstance::getWidth() const//returns width of object graphic in tiles
|
||||
{
|
||||
return defInfo->width;
|
||||
return appearance.getWidth();
|
||||
}
|
||||
int CGObjectInstance::getHeight() const //returns height of object graphic in tiles
|
||||
{
|
||||
return defInfo->height;
|
||||
return appearance.getHeight();
|
||||
}
|
||||
bool CGObjectInstance::visitableAt(int x, int y) const //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles)
|
||||
{
|
||||
if(defInfo==nullptr)
|
||||
{
|
||||
logGlobal->warnStream() << "Warning: VisitableAt for obj "<< id.getNum() <<": nullptr defInfo!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if((defInfo->visitMap[y] >> (7-x) ) & 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return appearance.isVisitableAt(pos.x - x, pos.y - y);
|
||||
}
|
||||
bool CGObjectInstance::blockingAt(int x, int y) const
|
||||
{
|
||||
if(x<0 || y<0 || x>=getWidth() || y>=getHeight() || defInfo==nullptr)
|
||||
return false;
|
||||
if((defInfo->blockMap[y+6-getHeight()] >> (7-(8-getWidth()+x) )) & 1)
|
||||
return false;
|
||||
return true;
|
||||
return appearance.isBlockedAt(pos.x - x, pos.y - y);
|
||||
}
|
||||
|
||||
bool CGObjectInstance::coveringAt(int x, int y) const
|
||||
{
|
||||
//input coordinates are always negative
|
||||
x = -x;
|
||||
y = -y;
|
||||
#if USE_COVERAGE_MAP
|
||||
//NOTE: this code may be broken
|
||||
if((defInfo->coverageMap[y] >> (7-(x) )) & 1
|
||||
|| (defInfo->shadowCoverage[y] >> (7-(x) )) & 1)
|
||||
return true;
|
||||
return false;
|
||||
#else
|
||||
return x >= 0 && y >= 0 && x < getWidth() && y < getHeight();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CGObjectInstance::hasShadowAt( int x, int y ) const
|
||||
{
|
||||
#if USE_COVERAGE_MAP
|
||||
//NOTE: this code may be broken
|
||||
if( (defInfo->shadowCoverage[y] >> (7-(x) )) & 1 )
|
||||
return true;
|
||||
return false;
|
||||
#else
|
||||
return coveringAt(x,y);// ignore unreliable shadowCoverage map
|
||||
#endif
|
||||
return appearance.isVisibleAt(pos.x - x, pos.y - y);
|
||||
}
|
||||
|
||||
std::set<int3> CGObjectInstance::getBlockedPos() const
|
||||
@ -388,8 +344,8 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
|
||||
{
|
||||
for(int h=0; h<getHeight(); ++h)
|
||||
{
|
||||
if(blockingAt(w, h))
|
||||
ret.insert(int3(pos.x - getWidth() + w + 1, pos.y - getHeight() + h + 1, pos.z));
|
||||
if (appearance.isBlockedAt(-w, -h))
|
||||
ret.insert(int3(pos.x - w, pos.y - h, pos.z));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -397,21 +353,20 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
|
||||
|
||||
bool CGObjectInstance::operator<(const CGObjectInstance & cmp) const //screen printing priority comparing
|
||||
{
|
||||
if(defInfo->printPriority==1 && cmp.defInfo->printPriority==0)
|
||||
return true;
|
||||
if(cmp.defInfo->printPriority==1 && defInfo->printPriority==0)
|
||||
return false;
|
||||
if(this->pos.y<cmp.pos.y)
|
||||
return true;
|
||||
if(this->pos.y>cmp.pos.y)
|
||||
return false;
|
||||
if (appearance.printPriority != cmp.appearance.printPriority)
|
||||
return appearance.printPriority > cmp.appearance.printPriority;
|
||||
|
||||
if(pos.y != cmp.pos.y)
|
||||
return pos.y < cmp.pos.y;
|
||||
|
||||
if(cmp.ID==Obj::HERO && ID!=Obj::HERO)
|
||||
return true;
|
||||
if(cmp.ID!=Obj::HERO && ID==Obj::HERO)
|
||||
return false;
|
||||
if(!defInfo->isVisitable() && cmp.defInfo->isVisitable())
|
||||
|
||||
if(!isVisitable() && cmp.isVisitable())
|
||||
return true;
|
||||
if(!cmp.defInfo->isVisitable() && defInfo->isVisitable())
|
||||
if(!cmp.isVisitable() && isVisitable())
|
||||
return false;
|
||||
if(this->pos.x<cmp.pos.x)
|
||||
return true;
|
||||
@ -490,13 +445,13 @@ void CGObjectInstance::hideTiles(PlayerColor ourplayer, int radius) const
|
||||
}
|
||||
int3 CGObjectInstance::getVisitableOffset() const
|
||||
{
|
||||
for(int y = 0; y < 6; y++)
|
||||
for (int x = 0; x < 8; x++)
|
||||
if((defInfo->visitMap[5-y] >> x) & 1)
|
||||
for(int y = 0; y < appearance.getHeight(); y++)
|
||||
for (int x = 0; x < appearance.getWidth(); x++)
|
||||
if (appearance.isVisitableAt(x, y))
|
||||
return int3(x,y,0);
|
||||
|
||||
logGlobal->warnStream() << "Warning: getVisitableOffset called on non-visitable obj!";
|
||||
return int3(-1,-1,-1);
|
||||
return int3(0,0,0);
|
||||
}
|
||||
|
||||
void CGObjectInstance::getNameVis( std::string &hname ) const
|
||||
@ -556,14 +511,7 @@ int3 CGObjectInstance::visitablePos() const
|
||||
|
||||
bool CGObjectInstance::isVisitable() const
|
||||
{
|
||||
for(int g=0; g<ARRAY_COUNT(defInfo->visitMap); ++g)
|
||||
{
|
||||
if(defInfo->visitMap[g] != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return appearance.isVisitable();
|
||||
}
|
||||
|
||||
bool CGObjectInstance::passableFor(PlayerColor color) const
|
||||
@ -766,11 +714,12 @@ void CGHeroInstance::initHero(HeroTypeID SUBID)
|
||||
void CGHeroInstance::initHero()
|
||||
{
|
||||
assert(validTypes(true));
|
||||
if(ID == Obj::HERO)
|
||||
initHeroDefInfo();
|
||||
if(!type)
|
||||
type = VLC->heroh->heroes[subID];
|
||||
|
||||
if (ID == Obj::HERO)
|
||||
appearance = VLC->dobjinfo->pickCandidates(Obj::HERO, type->heroClass->id).front();
|
||||
|
||||
if(!vstd::contains(spells, SpellID::PRESET)) //hero starts with a spell
|
||||
{
|
||||
for(auto spellID : type->spells)
|
||||
@ -888,27 +837,7 @@ void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= nullptr*/)
|
||||
dst->setCreature(SlotID(stackNo-warMachinesGiven), stack.creature, count);
|
||||
}
|
||||
}
|
||||
void CGHeroInstance::initHeroDefInfo()
|
||||
{
|
||||
if(!defInfo || defInfo->id != Obj::HERO)
|
||||
{
|
||||
defInfo = new CGDefInfo();
|
||||
defInfo->id = Obj::HERO;
|
||||
defInfo->subid = subID;
|
||||
defInfo->printPriority = 0;
|
||||
defInfo->visitDir = 0xff;
|
||||
}
|
||||
for(int i=0;i<6;i++)
|
||||
{
|
||||
defInfo->blockMap[i] = 255;
|
||||
defInfo->visitMap[i] = 0;
|
||||
defInfo->coverageMap[i] = 0;
|
||||
defInfo->shadowCoverage[i] = 0;
|
||||
}
|
||||
defInfo->blockMap[5] = 253;
|
||||
defInfo->visitMap[5] = 2;
|
||||
defInfo->coverageMap[4] = defInfo->coverageMap[5] = 224;
|
||||
}
|
||||
|
||||
CGHeroInstance::~CGHeroInstance()
|
||||
{
|
||||
commander.dellNull();
|
||||
@ -1780,8 +1709,8 @@ void CGDwelling::initObj()
|
||||
if (subID >= VLC->generaltexth->creGens.size()) //very messy workaround
|
||||
{
|
||||
auto & dwellingNames = VLC->townh->factions[crs->faction]->town->dwellingNames;
|
||||
assert (!dwellingNames.empty());
|
||||
hoverName = dwellingNames[VLC->creh->creatures[subID]->level - 1];
|
||||
assert (dwellingNames.size() > crs->level - 1);
|
||||
hoverName = dwellingNames[crs->level - 1];
|
||||
}
|
||||
else
|
||||
hoverName = VLC->generaltexth->creGens[subID];
|
||||
@ -2537,6 +2466,16 @@ std::vector<int> CGTownInstance::availableItemsIds(EMarketMode::EMarketMode mode
|
||||
return IMarket::availableItemsIds(mode);
|
||||
}
|
||||
|
||||
void CGTownInstance::updateAppearance()
|
||||
{
|
||||
if (!hasFort())
|
||||
appearance.animationFile = town->clientInfo.advMapVillage;
|
||||
else if(hasCapitol())
|
||||
appearance.animationFile = town->clientInfo.advMapCapitol;
|
||||
else
|
||||
appearance.animationFile = town->clientInfo.advMapCastle;
|
||||
}
|
||||
|
||||
std::string CGTownInstance::nodeName() const
|
||||
{
|
||||
return "Town (" + (town ? town->faction->name : "unknown") + ") of " + name;
|
||||
@ -5378,7 +5317,7 @@ std::vector<int3> CGMagicSpring::getVisitableOffsets() const
|
||||
|
||||
for(int y = 0; y < 6; y++)
|
||||
for (int x = 0; x < 8; x++) //starting from left
|
||||
if((defInfo->visitMap[5-y] >> x) & 1)
|
||||
if (appearance.isVisitableAt(x, y))
|
||||
visitableTiles.push_back (int3(x, y , 0));
|
||||
|
||||
return visitableTiles;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "../lib/CCreatureSet.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CDefObjInfoHandler.h"
|
||||
#include "CArtHandler.h"
|
||||
#include "../lib/ConstTransitivePtr.h"
|
||||
#include "int3.h"
|
||||
@ -36,7 +37,6 @@ class CSpell;
|
||||
class CGTownInstance;
|
||||
class CGTownBuilding;
|
||||
class CArtifact;
|
||||
class CGDefInfo;
|
||||
class CSpecObjInfo;
|
||||
class CCastleEvent;
|
||||
struct TerrainTile;
|
||||
@ -180,7 +180,7 @@ public:
|
||||
Obj ID;
|
||||
si32 subID; //normal subID (this one from OH3 maps ;])
|
||||
ObjectInstanceID id;//number of object in map's vector
|
||||
CGDefInfo * defInfo;
|
||||
ObjectTemplate appearance;
|
||||
|
||||
PlayerColor tempOwner;
|
||||
bool blockVisit; //if non-zero then blocks the tile but is visitable from neighbouring tile
|
||||
@ -194,12 +194,11 @@ public:
|
||||
void setOwner(PlayerColor ow);
|
||||
int getWidth() const; //returns width of object graphic in tiles
|
||||
int getHeight() const; //returns height of object graphic in tiles
|
||||
virtual bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles)
|
||||
virtual bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) (h3m pos)
|
||||
virtual int3 getVisitableOffset() const; //returns (x,y,0) offset to first visitable tile from bottom right obj tile (0,0,0) (h3m pos)
|
||||
int3 visitablePos() const;
|
||||
bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) form left top tile of image (x, y in tiles)
|
||||
bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) form left top tile of maximal possible image (8 x 6 tiles) (x, y in tiles)
|
||||
bool hasShadowAt(int x, int y) const;//returns true if object covers with shadow location (x, y) form left top tile of maximal possible image (8 x 6 tiles) (x, y in tiles)
|
||||
bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) (h3m pos)
|
||||
bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) (h3m pos)
|
||||
std::set<int3> getBlockedPos() const; //returns set of positions blocked by this object
|
||||
bool isVisitable() const; //returns true if object is visitable
|
||||
bool operator<(const CGObjectInstance & cmp) const; //screen printing priority comparing
|
||||
@ -220,7 +219,7 @@ public:
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & hoverName & pos & ID & subID & id & tempOwner & blockVisit & defInfo;
|
||||
h & hoverName & pos & ID & subID & id & tempOwner & blockVisit & appearance;
|
||||
//definfo is handled by map serializer
|
||||
}
|
||||
protected:
|
||||
@ -446,7 +445,6 @@ public:
|
||||
void initExp();
|
||||
void initArmy(IArmyDescriptor *dst = nullptr);
|
||||
//void giveArtifact (ui32 aid);
|
||||
void initHeroDefInfo();
|
||||
void pushPrimSkill(PrimarySkill::PrimarySkill which, int val);
|
||||
ui8 maxlevelsToMagicSchool() const;
|
||||
ui8 maxlevelsToWisdom() const;
|
||||
@ -688,6 +686,7 @@ public:
|
||||
bool allowsTrade(EMarketMode::EMarketMode mode) const;
|
||||
std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const;
|
||||
|
||||
void updateAppearance();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "CArtHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "CDefObjInfoHandler.h"
|
||||
#include "CObjectHandler.h"
|
||||
|
||||
/*
|
||||
* CTownHandler.cpp, part of VCMI engine
|
||||
@ -709,6 +711,38 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
|
||||
void CTownHandler::afterLoadFinalization()
|
||||
{
|
||||
initializeRequirements();
|
||||
ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::TOWN, 0).front();
|
||||
for (CFaction * fact : factions)
|
||||
{
|
||||
if (fact->town)
|
||||
{
|
||||
base.animationFile = fact->town->clientInfo.advMapCastle;
|
||||
base.subid = fact->index;
|
||||
|
||||
// replace existing (if any) and add new template.
|
||||
// Necessary for objects added via mods that don't have any templates in H3
|
||||
VLC->dobjinfo->eraseAll(Obj::TOWN, fact->index);
|
||||
VLC->dobjinfo->registerTemplate(base);
|
||||
|
||||
assert(fact->town->dwellings.size() == fact->town->dwellingNames.size());
|
||||
for (size_t i=0; i<fact->town->dwellings.size(); i++)
|
||||
{
|
||||
ObjectTemplate base = VLC->dobjinfo->pickCandidates(Obj::CREATURE_GENERATOR1, 0).front();
|
||||
|
||||
//both unupgraded and upgraded get same dwelling
|
||||
for (auto cre : fact->town->creatures[i])
|
||||
{
|
||||
base.subid = 80 + cre;
|
||||
base.animationFile = fact->town->dwellings[i];
|
||||
if (VLC->objh->cregens.count(cre) == 0)
|
||||
{
|
||||
VLC->dobjinfo->registerTemplate(base);
|
||||
VLC->objh->cregens[80 + cre] = cre; //map of dwelling -> creature id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CTownHandler::initializeRequirements()
|
||||
|
@ -75,12 +75,6 @@ ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID)
|
||||
|
||||
ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType)
|
||||
|
||||
|
||||
std::map<int, ConstTransitivePtr<CGDefInfo> > & Obj::toDefObjInfo() const
|
||||
{
|
||||
return VLC->dobjinfo->gobjs[*this];
|
||||
}
|
||||
|
||||
CArtifact * ArtifactID::toArtifact() const
|
||||
{
|
||||
return VLC->arth->artifacts[*this];
|
||||
|
@ -55,7 +55,6 @@ namespace GameConstants
|
||||
|
||||
class CArtifact;
|
||||
class CArtifactInstance;
|
||||
class CGDefInfo;
|
||||
class CCreature;
|
||||
class CSpell;
|
||||
class CGameInfoCallback;
|
||||
@ -598,8 +597,6 @@ public:
|
||||
|
||||
ID_LIKE_CLASS_COMMON(Obj, EObj)
|
||||
|
||||
std::map<int, ConstTransitivePtr<CGDefInfo> > & toDefObjInfo() const;
|
||||
|
||||
EObj num;
|
||||
};
|
||||
|
||||
|
@ -311,10 +311,7 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
|
||||
CGObjectInstance *obj = gs->getObjInstance(id);
|
||||
logGlobal->debugStream() << "removing object id=" << id << "; address=" << (intptr_t)obj << "; name=" << obj->getHoverText();
|
||||
//unblock tiles
|
||||
if(obj->defInfo)
|
||||
{
|
||||
gs->map->removeBlockVisTiles(obj);
|
||||
}
|
||||
|
||||
if(obj->ID==Obj::HERO)
|
||||
{
|
||||
@ -534,7 +531,6 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
|
||||
else
|
||||
gs->map->objects[h->id.getNum()] = h;
|
||||
|
||||
h->initHeroDefInfo();
|
||||
gs->map->heroesOnMap.push_back(h);
|
||||
p->heroes.push_back(h);
|
||||
h->attachTo(p);
|
||||
@ -558,7 +554,6 @@ DLL_LINKAGE void GiveHero::applyGs( CGameState *gs )
|
||||
gs->map->removeBlockVisTiles(h,true);
|
||||
h->setOwner(player);
|
||||
h->movement = h->maxMovePoints(true);
|
||||
h->initHeroDefInfo();
|
||||
gs->map->heroesOnMap.push_back(h);
|
||||
gs->getPlayer(h->getOwner())->heroes.push_back(h);
|
||||
gs->map->addBlockVisTiles(h);
|
||||
@ -594,27 +589,14 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs )
|
||||
o->ID = ID;
|
||||
o->subID = subID;
|
||||
o->pos = pos;
|
||||
o->defInfo = VLC->dobjinfo->gobjs[ID][subID];
|
||||
const TerrainTile &t = gs->map->getTile(pos);
|
||||
o->appearance = VLC->dobjinfo->pickCandidates(o->ID, o->subID, t.terType).front();
|
||||
id = o->id = ObjectInstanceID(gs->map->objects.size());
|
||||
o->hoverName = VLC->generaltexth->names[ID];
|
||||
|
||||
switch(ID)
|
||||
{
|
||||
case Obj::MONSTER:
|
||||
o->defInfo = VLC->dobjinfo->gobjs[ID][subID];
|
||||
assert(o->defInfo);
|
||||
break;
|
||||
case Obj::HOLE:
|
||||
const TerrainTile &t = gs->map->getTile(pos);
|
||||
o->defInfo = VLC->dobjinfo->gobjs[ID][t.terType];
|
||||
assert(o->defInfo);
|
||||
break;
|
||||
}
|
||||
|
||||
gs->map->objects.push_back(o);
|
||||
gs->map->addBlockVisTiles(o);
|
||||
o->initObj();
|
||||
assert(o->defInfo);
|
||||
|
||||
logGlobal->debugStream() << "added object id=" << id << "; address=" << (intptr_t)o << "; name=" << o->getHoverText();
|
||||
}
|
||||
|
@ -215,22 +215,22 @@ CMap::~CMap()
|
||||
|
||||
void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
|
||||
{
|
||||
for(int fx=0; fx<8; ++fx)
|
||||
for(int fx=0; fx<obj->getWidth(); ++fx)
|
||||
{
|
||||
for(int fy=0; fy<6; ++fy)
|
||||
for(int fy=0; fy<obj->getHeight(); ++fy)
|
||||
{
|
||||
int xVal = obj->pos.x + fx - 7;
|
||||
int yVal = obj->pos.y + fy - 5;
|
||||
int xVal = obj->pos.x - fx;
|
||||
int yVal = obj->pos.y - fy;
|
||||
int zVal = obj->pos.z;
|
||||
if(xVal>=0 && xVal<width && yVal>=0 && yVal<height)
|
||||
{
|
||||
TerrainTile & curt = terrain[xVal][yVal][zVal];
|
||||
if(total || ((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1))
|
||||
if(total || obj->visitableAt(xVal, yVal))
|
||||
{
|
||||
curt.visitableObjects -= obj;
|
||||
curt.visitable = curt.visitableObjects.size();
|
||||
}
|
||||
if(total || !((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1))
|
||||
if(total || obj->blockingAt(xVal, yVal))
|
||||
{
|
||||
curt.blockingObjects -= obj;
|
||||
curt.blocked = curt.blockingObjects.size();
|
||||
@ -242,22 +242,22 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
|
||||
|
||||
void CMap::addBlockVisTiles(CGObjectInstance * obj)
|
||||
{
|
||||
for(int fx=0; fx<8; ++fx)
|
||||
for(int fx=0; fx<obj->getWidth(); ++fx)
|
||||
{
|
||||
for(int fy=0; fy<6; ++fy)
|
||||
for(int fy=0; fy<obj->getHeight(); ++fy)
|
||||
{
|
||||
int xVal = obj->pos.x + fx - 7;
|
||||
int yVal = obj->pos.y + fy - 5;
|
||||
int xVal = obj->pos.x - fx;
|
||||
int yVal = obj->pos.y - fy;
|
||||
int zVal = obj->pos.z;
|
||||
if(xVal>=0 && xVal<width && yVal>=0 && yVal<height)
|
||||
{
|
||||
TerrainTile & curt = terrain[xVal][yVal][zVal];
|
||||
if(((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1))
|
||||
if( obj->visitableAt(xVal, yVal))
|
||||
{
|
||||
curt.visitableObjects.push_back(obj);
|
||||
curt.visitable = true;
|
||||
}
|
||||
if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1))
|
||||
if( obj->blockingAt(xVal, yVal))
|
||||
{
|
||||
curt.blockingObjects.push_back(obj);
|
||||
curt.blocked = true;
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "../LogicalExpression.h"
|
||||
|
||||
class CArtifactInstance;
|
||||
class CGDefInfo;
|
||||
class CGObjectInstance;
|
||||
class CGHeroInstance;
|
||||
class CCommanderInstance;
|
||||
@ -408,7 +407,6 @@ public:
|
||||
std::vector<Rumor> rumors;
|
||||
std::vector<DisposedHero> disposedHeroes;
|
||||
std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes;
|
||||
std::vector<ConstTransitivePtr<CGDefInfo> > customDefs;
|
||||
std::vector<bool> allowedSpell;
|
||||
std::vector<bool> allowedArtifact;
|
||||
std::vector<bool> allowedAbilities;
|
||||
@ -484,7 +482,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
h & customDefs & objects;
|
||||
h & objects;
|
||||
h & heroesOnMap & towns & artInstances;
|
||||
|
||||
// static members
|
||||
|
@ -124,7 +124,6 @@ void CMapLoaderH3M::init()
|
||||
// Calculate blocked / visitable positions
|
||||
for(auto & elem : map->objects)
|
||||
{
|
||||
if(!elem->defInfo) continue;
|
||||
map->addBlockVisTiles(elem);
|
||||
}
|
||||
times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff()));
|
||||
@ -945,78 +944,14 @@ void CMapLoaderH3M::readDefInfo()
|
||||
{
|
||||
int defAmount = reader.readUInt32();
|
||||
|
||||
map->customDefs.reserve(defAmount + 8);
|
||||
templates.reserve(defAmount);
|
||||
|
||||
// Read custom defs
|
||||
for(int idd = 0; idd < defAmount; ++idd)
|
||||
{
|
||||
auto defInfo = new CGDefInfo();
|
||||
|
||||
defInfo->name = reader.readString();
|
||||
std::transform(defInfo->name.begin(),defInfo->name.end(),defInfo->name.begin(),(int(*)(int))toupper);
|
||||
|
||||
ui8 bytes[12];
|
||||
for(auto & byte : bytes)
|
||||
{
|
||||
byte = reader.readUInt8();
|
||||
}
|
||||
|
||||
defInfo->terrainAllowed = reader.readUInt16();
|
||||
defInfo->terrainMenu = reader.readUInt16();
|
||||
defInfo->id = Obj(reader.readUInt32());
|
||||
defInfo->subid = reader.readUInt32();
|
||||
defInfo->type = reader.readUInt8();
|
||||
defInfo->printPriority = reader.readUInt8();
|
||||
|
||||
for(int zi = 0; zi < 6; ++zi)
|
||||
{
|
||||
defInfo->blockMap[zi] = reverse(bytes[zi]);
|
||||
}
|
||||
for(int zi = 0; zi < 6; ++zi)
|
||||
{
|
||||
defInfo->visitMap[zi] = reverse(bytes[6 + zi]);
|
||||
}
|
||||
|
||||
reader.skip(16);
|
||||
if(defInfo->id != Obj::HERO && defInfo->id != Obj::RANDOM_HERO)
|
||||
{
|
||||
CGDefInfo * h = VLC->dobjinfo->gobjs[defInfo->id][defInfo->subid];
|
||||
if(!h)
|
||||
{
|
||||
//remove fake entry
|
||||
VLC->dobjinfo->gobjs[defInfo->id].erase(defInfo->subid);
|
||||
if(VLC->dobjinfo->gobjs[defInfo->id].size())
|
||||
{
|
||||
VLC->dobjinfo->gobjs.erase(defInfo->id);
|
||||
}
|
||||
logGlobal->warnStream() << "\t\tWarning: no defobjinfo entry for object ID="
|
||||
<< defInfo->id << " subID=" << defInfo->subid;
|
||||
}
|
||||
else
|
||||
{
|
||||
defInfo->visitDir = VLC->dobjinfo->gobjs[defInfo->id][defInfo->subid]->visitDir;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
defInfo->visitDir = 0xff;
|
||||
}
|
||||
|
||||
if(defInfo->id == Obj::EVENT)
|
||||
{
|
||||
std::memset(defInfo->blockMap, 255, 6);
|
||||
}
|
||||
|
||||
//calculating coverageMap
|
||||
defInfo->fetchInfoFromMSK();
|
||||
|
||||
map->customDefs.push_back(defInfo);
|
||||
}
|
||||
|
||||
//add holes - they always can appear
|
||||
for(int i = 0; i < 8 ; ++i)
|
||||
{
|
||||
map->customDefs.push_back(VLC->dobjinfo->gobjs[Obj::HOLE][i]);
|
||||
ObjectTemplate tmpl;
|
||||
tmpl.readMap(reader);
|
||||
templates.push_back(tmpl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1033,10 +968,10 @@ void CMapLoaderH3M::readObjects()
|
||||
int defnum = reader.readUInt32();
|
||||
ObjectInstanceID idToBeGiven = ObjectInstanceID(map->objects.size());
|
||||
|
||||
CGDefInfo * defInfo = map->customDefs.at(defnum);
|
||||
ObjectTemplate & objTempl = templates.at(defnum);
|
||||
reader.skip(5);
|
||||
|
||||
switch(defInfo->id)
|
||||
switch(objTempl.id)
|
||||
{
|
||||
case Obj::EVENT:
|
||||
{
|
||||
@ -1142,7 +1077,7 @@ void CMapLoaderH3M::readObjects()
|
||||
break;
|
||||
}
|
||||
case Obj::TREASURE_CHEST:
|
||||
if(defInfo->subid == 0)
|
||||
if(objTempl.subid == 0)
|
||||
{
|
||||
nobj = new CGPickable();
|
||||
}
|
||||
@ -1315,15 +1250,15 @@ void CMapLoaderH3M::readObjects()
|
||||
|
||||
readMessageAndGuards(art->message, art);
|
||||
|
||||
if(defInfo->id == Obj::SPELL_SCROLL)
|
||||
if(objTempl.id == Obj::SPELL_SCROLL)
|
||||
{
|
||||
spellID = reader.readUInt32();
|
||||
artID = 1;
|
||||
}
|
||||
else if(defInfo->id == Obj::ARTIFACT)
|
||||
else if(objTempl.id == Obj::ARTIFACT)
|
||||
{
|
||||
//specific artifact
|
||||
artID = defInfo->subid;
|
||||
artID = objTempl.subid;
|
||||
}
|
||||
|
||||
art->storedArtifact = createArtifact(artID, spellID);
|
||||
@ -1338,7 +1273,7 @@ void CMapLoaderH3M::readObjects()
|
||||
readMessageAndGuards(res->message, res);
|
||||
|
||||
res->amount = reader.readUInt32();
|
||||
if(defInfo->subid == Res::GOLD)
|
||||
if(objTempl.subid == Res::GOLD)
|
||||
{
|
||||
// Gold is multiplied by 100.
|
||||
res->amount *= 100;
|
||||
@ -1349,7 +1284,7 @@ void CMapLoaderH3M::readObjects()
|
||||
case Obj::RANDOM_TOWN:
|
||||
case Obj::TOWN:
|
||||
{
|
||||
nobj = readTown(defInfo->subid);
|
||||
nobj = readTown(objTempl.subid);
|
||||
break;
|
||||
}
|
||||
case Obj::MINE:
|
||||
@ -1455,7 +1390,7 @@ void CMapLoaderH3M::readObjects()
|
||||
{
|
||||
nobj = new CGDwelling();
|
||||
CSpecObjInfo * spec = nullptr;
|
||||
switch(defInfo->id)
|
||||
switch(objTempl.id)
|
||||
{
|
||||
break; case Obj::RANDOM_DWELLING: spec = new CCreGenLeveledCastleInfo();
|
||||
break; case Obj::RANDOM_DWELLING_LVL: spec = new CCreGenAsCastleInfo();
|
||||
@ -1607,7 +1542,7 @@ void CMapLoaderH3M::readObjects()
|
||||
}
|
||||
case Obj::PYRAMID: //Pyramid of WoG object
|
||||
{
|
||||
if(defInfo->subid == 0)
|
||||
if(objTempl.subid == 0)
|
||||
{
|
||||
nobj = new CGPyramid();
|
||||
}
|
||||
@ -1671,13 +1606,13 @@ void CMapLoaderH3M::readObjects()
|
||||
}
|
||||
|
||||
nobj->pos = objPos;
|
||||
nobj->ID = defInfo->id;
|
||||
nobj->ID = objTempl.id;
|
||||
nobj->id = idToBeGiven;
|
||||
if(nobj->ID != Obj::HERO && nobj->ID != Obj::HERO_PLACEHOLDER && nobj->ID != Obj::PRISON)
|
||||
{
|
||||
nobj->subID = defInfo->subid;
|
||||
nobj->subID = objTempl.subid;
|
||||
}
|
||||
nobj->defInfo = defInfo;
|
||||
nobj->appearance = objTempl;
|
||||
assert(idToBeGiven == ObjectInstanceID(map->objects.size()));
|
||||
map->objects.push_back(nobj);
|
||||
if(nobj->ID == Obj::TOWN)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "CMapService.h"
|
||||
#include "../GameConstants.h"
|
||||
#include "../ResourceSet.h"
|
||||
#include "../CDefObjInfoHandler.h"
|
||||
|
||||
#include "../int3.h"
|
||||
|
||||
@ -250,6 +251,10 @@ private:
|
||||
return p;
|
||||
}
|
||||
|
||||
/** List of templates loaded from the map, used on later stage to create
|
||||
* objects but not needed for fully functional CMap */
|
||||
std::vector<ObjectTemplate> templates;
|
||||
|
||||
/** ptr to the map object which gets filled by data from the buffer */
|
||||
CMap * map;
|
||||
|
||||
|
@ -161,7 +161,7 @@ void CMapGenerator::genTowns()
|
||||
if(townId == CMapGenOptions::CPlayerSettings::RANDOM_TOWN) townId = gen.getInteger(0, 8); // Default towns
|
||||
town->subID = townId;
|
||||
town->tempOwner = owner;
|
||||
town->defInfo = VLC->dobjinfo->gobjs[town->ID][town->subID];
|
||||
town->appearance = VLC->dobjinfo->pickCandidates(town->ID, town->subID, map->getTile(townPos[side]).terType).front();
|
||||
town->builtBuildings.insert(BuildingID::FORT);
|
||||
town->builtBuildings.insert(BuildingID::DEFAULT);
|
||||
editManager->insertObject(town, int3(townPos[side].x, townPos[side].y + (i / 2) * 5, 0));
|
||||
|
@ -5221,7 +5221,7 @@ bool CGameHandler::dig( const CGHeroInstance *h )
|
||||
NewObject no;
|
||||
no.ID = Obj::HOLE;
|
||||
no.pos = h->getPosition();
|
||||
no.subID = getTile(no.pos)->terType;
|
||||
no.subID = 0;
|
||||
sendAndApply(&no);
|
||||
|
||||
//take MPs
|
||||
|
Loading…
Reference in New Issue
Block a user