diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index fe761dc9b..131ae82d5 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -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; diff --git a/client/CMT.cpp b/client/CMT.cpp index 2476a7ca3..f37a07c4a 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -954,11 +954,6 @@ void endGame() { client->endGame(); vstd::clear_pointer(client); - - delete CGI->dobjinfo.get(); - const_cast(CGI)->dobjinfo = new CDefObjInfoHandler; - - const_cast(CGI)->modh->reload(); //add info about new creatures to dobjinfo } void handleQuit() diff --git a/client/Graphics.cpp b/client/Graphics.cpp index 35aad6b4d..ec9a981ad 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -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() diff --git a/client/Graphics.h b/client/Graphics.h index 3e4e1cc8e..29daf52d5 100644 --- a/client/Graphics.h +++ b/client/Graphics.h @@ -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 advmapobjGraphics; CDefEssential * getDef(const CGObjectInstance * obj); - CDefEssential * getDef(const CGDefInfo * info); + CDefEssential * getDef(const ObjectTemplate & info); //towns std::map ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type //for battles diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 3607705c6..f79e9ab0a 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -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(CGI->dobjinfo->capitols.at(town->subID).get()); - } - if(id == BuildingID::FORT) - { - town->defInfo = const_cast(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(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); } diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index 06dbe9a33..987672987 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -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(obj)->inTownGarrison) //garrisoned hero - || (obj->ID==Obj::BOAT && static_cast(obj)->hero) //boat with hero (hero graphics is used) - || !obj->defInfo ) + || (obj->ID==Obj::BOAT && static_cast(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; fxw>>5; ++fx) //bitmap->w/32 + for(int fx=0; fx < obj->getWidth(); ++fx) { - for(int fy=0; fyh>>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 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; fyh/32; ++fy) - } //for(int fx=0; fxw/32; ++fx) - } // for(int f=0; fobjects.size(); ++f) + } + } + } for(int ix=0; ixadvmapobjGraphics[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: "<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); + CSDL_Ext::blit8bppAlphaTo24bpp(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 - if(map->getTile(int3(pos.x, pos.y, top_tile.z)).blocked) //temporary hiding blocked positions + SDL_Rect tileRect = genRect(sr.h, sr.w, 0, 0); + + if (settings["session"]["showBlock"].Bool()) { - SDL_Rect sr; + 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"); - sr.x=srx; - sr.y=sry; - sr.h=sr.w=32; + SDL_Rect sr; - memset(rSurf->pixels, 128, rSurf->pitch * rSurf->h); - CSDL_Ext::blitSurface(rSurf,&genRect(sr.h, sr.w, 0, 0),extSurf,&sr); + sr.x=srx; + sr.y=sry; + sr.h=sr.w=32; + + CSDL_Ext::blitSurface(block, &tileRect, extSurf, &sr); + } } -#endif -#ifdef MARK_VISITABLE_POSITIONS - if(map->getTile(int3(pos.x, pos.y, top_tile.z)).visitable) //temporary hiding visitable positions + if (settings["session"]["showVisit"].Bool()) { - SDL_Rect sr; + 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"); - sr.x=srx; - sr.y=sry; - sr.h=sr.w=32; + SDL_Rect sr; - memset(rSurf->pixels, 128, rSurf->pitch * rSurf->h); - CSDL_Ext::blitSurface(rSurf,&genRect(sr.h, sr.w, 0, 0),extSurf,&sr); + sr.x=srx; + sr.y=sry; + sr.h=sr.w=32; + CSDL_Ext::blitSurface(visit, &tileRect, extSurf, &sr); + } } -#endif } } } @@ -849,7 +852,7 @@ std::pair 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; diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 141666b99..9b0bbd15c 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -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() @@ -695,13 +711,13 @@ void CArtifactInstance::init() id = static_cast(ArtifactID::NONE); //to be randomized setNodeType(ARTIFACT_INSTANCE); } - -ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const -{ - for(auto slot : artType->possibleSlots.at(h->bearerType())) - { - if(canBePutAt(h, slot)) //if(artType->fitsAt(h->artifWorn, slot)) - { + +ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const +{ + for(auto slot : artType->possibleSlots.at(h->bearerType())) + { + if(canBePutAt(h, slot)) //if(artType->fitsAt(h->artifWorn, slot)) + { //we've found a free suitable slot. return slot; } @@ -1101,13 +1117,13 @@ bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/) const return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST; } -const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const -{ - if(vstd::contains(artifactsWorn, pos)) - return &artifactsWorn.at(pos); - if(pos >= ArtifactPosition::AFTER_LAST ) - { - int backpackPos = (int)pos - GameConstants::BACKPACK_START; +const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const +{ + if(vstd::contains(artifactsWorn, pos)) + return &artifactsWorn.at(pos); + if(pos >= ArtifactPosition::AFTER_LAST ) + { + int backpackPos = (int)pos - GameConstants::BACKPACK_START; if(backpackPos < 0 || backpackPos >= artifactsInBackpack.size()) return nullptr; else diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 5a0e7b34d..b5a1de42e 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -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(); diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index 2991063d9..afa95486c 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -198,6 +198,8 @@ public: /// generates tier-specific bonus tree entries void buildBonusTreeForTiers(); + void afterLoadFinalization(); + std::vector loadLegacyData(size_t dataSize) override; void loadObject(std::string scope, std::string name, const JsonNode & data) override; diff --git a/lib/CDefObjInfoHandler.cpp b/lib/CDefObjInfoHandler.cpp index b1d9d3108..cc54ae368 100644 --- a/lib/CDefObjInfoHandler.cpp +++ b/lib/CDefObjInfoHandler.cpp @@ -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,164 +21,387 @@ * */ -bool CGDefInfo::isVisitable() const +static bool isVisitableFromTop(int identifier, int type) { - for (auto & elem : visitMap) - { - if (elem) - return true; - } + if(type == 2 || type == 3 || type == 4 || type == 5) //creature, hero, artifact, resource + return true; + + static const Obj visitableFromTop[] = + {Obj::FLOTSAM, + Obj::SEA_CHEST, + Obj::SHIPWRECK_SURVIVOR, + Obj::BUOY, + Obj::OCEAN_BOTTLE, + Obj::BOAT, + Obj::WHIRLPOOL, + Obj::GARRISON, + Obj::SCHOLAR, + Obj::CAMPFIRE, + Obj::BORDERGUARD, + Obj::BORDER_GATE, + Obj::QUEST_GUARD, + Obj::CORPSE + }; + if (vstd::find_pos(visitableFromTop, identifier) != -1) + 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; +ObjectTemplate::ObjectTemplate(): + visitDir(8|16|32|64|128), // all but top + id(Obj::NO_OBJ), + subid(0), + printPriority(0) +{ } -void CGDefInfo::fetchInfoFromMSK() +void ObjectTemplate::readTxt(CLegacyConfigParser & parser) { - ResourceID resID("SPRITES/" + name, EResType::MASK); + std::string data = parser.readString(); + std::vector 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(strings[5])); + subid = boost::lexical_cast(strings[6]); + int type = boost::lexical_cast(strings[7]); + printPriority = boost::lexical_cast(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); + } +} - width = msk.first.get()[0]; - height = msk.first.get()[1]; - for(int i=0; i<6; ++i) +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 { - coverageMap[i] = msk.first.get()[i+2]; - shadowCoverage[i] = msk.first.get()[i+8]; + 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; } } - else + + reader.readUInt16(); + ui16 terrMask = reader.readUInt16(); + for (size_t i=0; i<9; i++) { - //maximum possible size of H3 object - //TODO: remove hardcode and move this data into modding system - width = 8; - height = 6; + 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= 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; idobjinfo = this; + readTextFile("Data/Objects.txt"); + readTextFile("Data/Heroes.txt"); +/* + const JsonNode node = JsonUtils::assembleFromFiles("config/objectTemplates.json"); + std::vector newTemplates; + newTemplates.reserve(node.Struct().size()); - 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>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; vblockMap[v/8] &= 255 - (128 >> (v%8)); - } - } - inp>>mapStr; - std::reverse(mapStr.begin(), mapStr.end()); - for(int v=0; vvisitMap[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, - Obj::SHIPWRECK_SURVIVOR, - Obj::BUOY, - Obj::OCEAN_BOTTLE, - Obj::BOAT, - Obj::WHIRLPOOL, - Obj::GARRISON, - Obj::SCHOLAR, - Obj::CAMPFIRE, - 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; - + ObjectTemplate templ; + templ.readJson(entry.second); + newTemplates.push_back(templ); } - for (int i = 0; i < 8 ; i++) - { + // erase old ones to avoid conflicts + for (auto & entry : newTemplates) + eraseAll(entry.id, entry.subid); - 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]; - } + // merge new templates into storage + objects.insert(objects.end(), newTemplates.begin(), newTemplates.end()); +*/ } -CDefObjInfoHandler::~CDefObjInfoHandler() +void CDefObjInfoHandler::eraseAll(Obj type, si32 subtype) { - for(auto & elem : gobjs) - for(auto j=elem.second.begin(); j!=elem.second.end(); j++) - j->second.dellNull(); + 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 CDefObjInfoHandler::pickCandidates(Obj type, si32 subtype) const +{ + std::vector 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 CDefObjInfoHandler::pickCandidates(Obj type, si32 subtype, ETerrainType terrain) const +{ + std::vector ret = pickCandidates(type, subtype); + std::vector 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; } diff --git a/lib/CDefObjInfoHandler.h b/lib/CDefObjInfoHandler.h index 10d3e6156..6701486dd 100644 --- a/lib/CDefObjInfoHandler.h +++ b/lib/CDefObjInfoHandler.h @@ -13,52 +13,96 @@ * */ -class CDefEssential; -class DLL_LINKAGE CGDefInfo +class CBinaryReader; +class CLegacyConfigParser; +class JsonNode; + +class DLL_LINKAGE ObjectTemplate { + enum EBlockMapBits + { + VISIBLE = 1, + VISITABLE = 2, + BLOCKED = 4 + }; + + /// tiles that are covered by this object, uses EBlockMapBits enum as flags + std::vector> 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 allowedTerrains; + public: - std::string name; - - 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) + /// H3 ID/subID of this object 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 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; - bool operator<(const CGDefInfo& por) const - { - if(id!=por.id) - return id void serialize(Handler &h, const int version) - { - h & name & visitMap & blockMap & visitDir & id & subid &terrainAllowed - & terrainMenu & width & height & type & printPriority & coverageMap & shadowCoverage; - } - CGDefInfo(); -void fetchInfoFromMSK(); -}; -class DLL_LINKAGE CDefObjInfoHandler -{ -public: - std::map > > gobjs; - - std::map > capitols; - std::map > villages; - - CDefObjInfoHandler(); - ~CDefObjInfoHandler(); + + // 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 void serialize(Handler &h, const int version) { - h & gobjs & capitols & villages; + h & usedTiles & allowedTerrains & animationFile; + h & id & subid & printPriority & visitDir; + } +}; + +class DLL_LINKAGE CDefObjInfoHandler +{ + /// list of all object templates loaded from text files + /// actual object have ObjectTemplate as member "appearance" + std::vector objects; + + /// reads one of H3 text files that contain object templates description + void readTextFile(std::string path); +public: + + 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 + std::vector pickCandidates(Obj type, si32 subtype) const; + /// picks all candidates for and of possible - also filters them by terrain + std::vector 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 pickCandidates(Obj type, si32 subtype, ETerrainType terrain, std::function accept) const; + + template void serialize(Handler &h, const int version) + { + h & objects; } }; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index d03edaa96..e9071764b 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -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 " <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(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(cur); if(!t) {logGlobal->warnStream()<<"Wrong random town at "<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 "<ID<<" "<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(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 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,39 +2216,10 @@ 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; } diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index a424ff324..1f62a4721 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -223,6 +223,18 @@ void CHeroClassHandler::afterLoadFinalization() heroClass->selectionProbability[faction->index] = static_cast(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 CHeroClassHandler::getDefaultAllowed() const diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 704905c68..11500c33b 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -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 - } - } - } - } - } } diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 9c36f0385..cbe6397a5 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -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; diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 265af6a26..6a62dc65e 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -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 > > CGTeleport::objs; std::vector > 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 CGObjectInstance::getBlockedPos() const @@ -388,8 +344,8 @@ std::set CGObjectInstance::getBlockedPos() const { for(int h=0; h 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.ypos.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.xvisitMap[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; gvisitMap); ++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 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 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; diff --git a/lib/CObjectHandler.h b/lib/CObjectHandler.h index eb2c24244..60675c552 100644 --- a/lib/CObjectHandler.h +++ b/lib/CObjectHandler.h @@ -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 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 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 availableItemsIds(EMarketMode::EMarketMode mode) const; + void updateAppearance(); ////////////////////////////////////////////////////////////////////////// diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index c0bd329f3..0f1d18017 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -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; itown->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() diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index cf28026b4..6ec8dcdcb 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -73,13 +73,7 @@ ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID) ID_LIKE_OPERATORS(BuildingID, BuildingID::EBuildingID) -ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType) - - -std::map > & Obj::toDefObjInfo() const -{ - return VLC->dobjinfo->gobjs[*this]; -} +ID_LIKE_OPERATORS(BFieldType, BFieldType::EBFieldType) CArtifact * ArtifactID::toArtifact() const { diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 9ca1f30c1..74acec693 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -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 > & toDefObjInfo() const; - EObj num; }; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 2b0278069..83aaded7e 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -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); - } + 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(); } diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index d31ba57ea..1ec8789c1 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -215,22 +215,22 @@ CMap::~CMap() void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total) { - for(int fx=0; fx<8; ++fx) + for(int fx=0; fxgetWidth(); ++fx) { - for(int fy=0; fy<6; ++fy) + for(int fy=0; fygetHeight(); ++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=0 && yValdefInfo->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; fxgetWidth(); ++fx) { - for(int fy=0; fy<6; ++fy) + for(int fy=0; fygetHeight(); ++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=0 && yValdefInfo->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; diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index e65316769..179b67dfe 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -19,7 +19,6 @@ #include "../LogicalExpression.h" class CArtifactInstance; -class CGDefInfo; class CGObjectInstance; class CGHeroInstance; class CCommanderInstance; @@ -408,7 +407,6 @@ public: std::vector rumors; std::vector disposedHeroes; std::vector > predefinedHeroes; - std::vector > customDefs; std::vector allowedSpell; std::vector allowedArtifact; std::vector allowedAbilities; @@ -484,7 +482,7 @@ public: } } - h & customDefs & objects; + h & objects; h & heroesOnMap & towns & artInstances; // static members diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 7faf6d597..2252414cc 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -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) diff --git a/lib/mapping/MapFormatH3M.h b/lib/mapping/MapFormatH3M.h index 346bbce6d..6a44449e0 100644 --- a/lib/mapping/MapFormatH3M.h +++ b/lib/mapping/MapFormatH3M.h @@ -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 templates; + /** ptr to the map object which gets filled by data from the buffer */ CMap * map; @@ -262,4 +267,4 @@ private: CBinaryReader reader; CInputStream * inputStream; -}; \ No newline at end of file +}; diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index 1d203956c..d6d780cd0 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -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)); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9bc9c8486..3033f49ea 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -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