diff --git a/Source/drlg_l1.cpp b/Source/drlg_l1.cpp index 5046ca6d0..91c3ffae6 100644 --- a/Source/drlg_l1.cpp +++ b/Source/drlg_l1.cpp @@ -2195,7 +2195,7 @@ void LoadL1Dungeon(const char *path, int vx, int vy) InitDungeonPieces(); SetMapMonsters(dunData.get(), Point(0, 0).megaToWorld()); - SetMapObjects(dunData.get(), 0, 0); + SetMapObjects(dunData.get(), Point(0, 0).megaToWorld()); } void LoadPreL1Dungeon(const char *path) diff --git a/Source/drlg_l2.cpp b/Source/drlg_l2.cpp index 59e58c084..66545f2fe 100644 --- a/Source/drlg_l2.cpp +++ b/Source/drlg_l2.cpp @@ -2926,7 +2926,7 @@ void LoadL2Dungeon(const char *path, int vx, int vy) ViewPosition = { vx, vy }; SetMapMonsters(dunData.get(), Point(0, 0).megaToWorld()); - SetMapObjects(dunData.get(), 0, 0); + SetMapObjects(dunData.get(), Point(0, 0).megaToWorld()); } void LoadPreL2Dungeon(const char *path) diff --git a/Source/drlg_l3.cpp b/Source/drlg_l3.cpp index c0f24fa76..2fbe25d46 100644 --- a/Source/drlg_l3.cpp +++ b/Source/drlg_l3.cpp @@ -2268,7 +2268,7 @@ void LoadL3Dungeon(const char *path, int vx, int vy) ViewPosition = { vx, vy }; SetMapMonsters(dunData.get(), Point(0, 0).megaToWorld()); - SetMapObjects(dunData.get(), 0, 0); + SetMapObjects(dunData.get(), Point(0, 0).megaToWorld()); for (int j = 0; j < MAXDUNY; j++) { for (int i = 0; i < MAXDUNX; i++) { diff --git a/Source/drlg_l4.cpp b/Source/drlg_l4.cpp index a8b1a0edb..7bb35959a 100644 --- a/Source/drlg_l4.cpp +++ b/Source/drlg_l4.cpp @@ -1334,7 +1334,7 @@ void LoadL4Dungeon(const char *path, int vx, int vy) DRLG_Init_Globals(); SetMapMonsters(dunData.get(), Point(0, 0).megaToWorld()); - SetMapObjects(dunData.get(), 0, 0); + SetMapObjects(dunData.get(), Point(0, 0).megaToWorld()); } void LoadPreL4Dungeon(const char *path) diff --git a/Source/items.cpp b/Source/items.cpp index bb1929219..a0fae3879 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -412,12 +412,13 @@ bool ItemPlace(Point position) Point GetRandomAvailableItemPosition() { - Point position = {}; - do { - position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; - } while (!ItemPlace(position)); + while (true) { + Point position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; + if (ItemPlace(position)) + return position; + } - return position; + return {}; } void AddInitItems() diff --git a/Source/monster.cpp b/Source/monster.cpp index 24d45dfcd..1a844b1bc 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -186,15 +186,15 @@ void InitMonsterTRN(CMonster &monst) } } -void InitMonster(Monster &monster, Direction rd, int mtype, Point position) +void InitMonster(Monster &monster, Direction rd, int monsterTypeIndex, Point position) { monster._mdir = rd; monster.position.tile = position; monster.position.future = position; monster.position.old = position; - monster._mMTidx = mtype; + monster._mMTidx = monsterTypeIndex; monster._mmode = MonsterMode::Stand; - monster.MType = &LevelMonsterTypes[mtype]; + monster.MType = &LevelMonsterTypes[monsterTypeIndex]; monster.MData = monster.MType->MData; monster.mName = pgettext("monster", monster.MData->mName).c_str(); monster.AnimInfo = {}; @@ -295,11 +295,11 @@ bool CanPlaceMonster(Point position) && !IsTileOccupied(position); } -void PlaceMonster(int i, int mtype, Point position) +void PlaceMonster(int i, int monsterTypeIndex, Point position) { - if (LevelMonsterTypes[mtype].mtype == MT_NAKRUL) { + if (LevelMonsterTypes[monsterTypeIndex].mtype == MT_NAKRUL) { for (int j = 0; j < ActiveMonsterCount; j++) { - if (Monsters[j]._mMTidx == mtype) { + if (Monsters[j]._mMTidx == monsterTypeIndex) { return; } if (Monsters[j].MType->mtype == MT_NAKRUL) { @@ -310,118 +310,127 @@ void PlaceMonster(int i, int mtype, Point position) dMonster[position.x][position.y] = i + 1; auto rd = static_cast(GenerateRnd(8)); - InitMonster(Monsters[i], rd, mtype, position); + InitMonster(Monsters[i], rd, monsterTypeIndex, position); } -void PlaceGroup(int mtype, int num, UniqueMonsterPack uniqueMonsterPack, int leaderId) +/** + * @brief Walk in a random direction untill the given position is a valid for spawning a monster + */ +std::optional GetNextSpawnPosition(Point position, int roomId, Rectangle area) { - int placed = 0; + for (int try2 = 0; try2 < 100; try2++, position += static_cast(GenerateRnd(8))) { + if (!CanPlaceMonster(position)) + continue; + if (dTransVal[position.x][position.y] != roomId) + continue; + if (!area.Contains(position)) + continue; + return position; + } - auto &leader = Monsters[leaderId]; + return {}; +} - for (int try1 = 0; try1 < 10; try1++) { - while (placed != 0) { - ActiveMonsterCount--; - placed--; - const auto &position = Monsters[ActiveMonsterCount].position.tile; - dMonster[position.x][position.y] = 0; - } +bool PlaceGroup(std::optional startPosition, int monsterTypeIndex, int num, Rectangle area) +{ + num = std::min(num, totalmonsters - ActiveMonsterCount); - int xp; - int yp; - if (uniqueMonsterPack != UniqueMonsterPack::None) { - int offset = GenerateRnd(8); - auto position = leader.position.tile + static_cast(offset); - xp = position.x; - yp = position.y; + for (int attempts = 0; attempts < 10; attempts++) { + Point position; + if (startPosition) { + position = *startPosition; } else { do { - xp = GenerateRnd(80) + 16; - yp = GenerateRnd(80) + 16; - } while (!CanPlaceMonster({ xp, yp })); + position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; + } while (!CanPlaceMonster(position)); } - int x1 = xp; - int y1 = yp; - if (num + ActiveMonsterCount > totalmonsters) { - num = totalmonsters - ActiveMonsterCount; - } - - int j = 0; - for (int try2 = 0; j < num && try2 < 100; xp += Displacement(static_cast(GenerateRnd(8))).deltaX, yp += Displacement(static_cast(GenerateRnd(8))).deltaX) { /// BUGFIX: `yp += Point.y` - if (!CanPlaceMonster({ xp, yp }) - || (dTransVal[xp][yp] != dTransVal[x1][y1]) - || (uniqueMonsterPack == UniqueMonsterPack::Leashed && (abs(xp - x1) >= 4 || abs(yp - y1) >= 4))) { - try2++; - continue; - } + int roomId = dTransVal[position.x][position.y]; - PlaceMonster(ActiveMonsterCount, mtype, { xp, yp }); - if (uniqueMonsterPack != UniqueMonsterPack::None) { - auto &minion = Monsters[ActiveMonsterCount]; - minion._mmaxhp *= 2; - minion._mhitpoints = minion._mmaxhp; - minion._mint = leader._mint; - - if (uniqueMonsterPack == UniqueMonsterPack::Leashed) { - minion.leader = leaderId; - minion.leaderRelation = LeaderRelation::Leashed; - minion._mAi = leader._mAi; - } + int placed = 0; + while (true) { + std::optional nextPosition = GetNextSpawnPosition(position, roomId, area); + if (!nextPosition) + break; - if (minion._mAi != AI_GARG) { - minion.ChangeAnimationData(MonsterGraphic::Stand); - minion.AnimInfo.CurrentFrame = GenerateRnd(minion.AnimInfo.NumberOfFrames - 1); - minion._mFlags &= ~MFLAG_ALLOW_SPECIAL; - minion._mmode = MonsterMode::Stand; - } - } + position = *nextPosition; + PlaceMonster(ActiveMonsterCount, monsterTypeIndex, position); ActiveMonsterCount++; placed++; - j++; + if (placed == num) + return true; + + position += static_cast(GenerateRnd(8)); } - if (placed >= num) { - break; + for (int i = 0; i < placed; i++) { + ActiveMonsterCount--; + Point position = Monsters[ActiveMonsterCount].position.tile; + dMonster[position.x][position.y] = 0; + } + } + + return false; +} + +void PlaceMinions(int monsterTypeIndex, int packSize, UniqueMonsterPack uniqueMonsterPack, int leaderId) +{ + Monster &leader = Monsters[leaderId]; + + if (!PlaceGroup(leader.position.tile, monsterTypeIndex, packSize, Rectangle { leader.position.tile, 3 })) + return; + + for (int i = ActiveMonsterCount - packSize - 1; i < ActiveMonsterCount; i++) { + Monster &minion = Monsters[i]; + minion._mmaxhp *= 2; + minion._mhitpoints = minion._mmaxhp; + minion._mint = leader._mint; + + if (uniqueMonsterPack == UniqueMonsterPack::Leashed) { + minion.leader = leaderId; + minion.leaderRelation = LeaderRelation::Leashed; + minion._mAi = leader._mAi; + } + + if (minion._mAi != AI_GARG) { + minion.ChangeAnimationData(MonsterGraphic::Stand); + minion.AnimInfo.CurrentFrame = GenerateRnd(minion.AnimInfo.NumberOfFrames - 1); + minion._mFlags &= ~MFLAG_ALLOW_SPECIAL; + minion._mmode = MonsterMode::Stand; } } if (uniqueMonsterPack == UniqueMonsterPack::Leashed) { - leader.packsize = placed; + leader.packsize = packSize; } } -void PlaceUniqueMonst(int uniqindex, int miniontype, int bosspacksize) +void PlaceUniqueMonst(int uniqindex, int minionIndex, int bosspacksize) { auto &monster = Monsters[ActiveMonsterCount]; const auto &uniqueMonsterData = UniqueMonstersData[uniqindex]; - int uniqtype; - for (uniqtype = 0; uniqtype < LevelMonsterTypeCount; uniqtype++) { - if (LevelMonsterTypes[uniqtype].mtype == uniqueMonsterData.mtype) { + int monsterTypeIndex; + for (monsterTypeIndex = 0; monsterTypeIndex < LevelMonsterTypeCount; monsterTypeIndex++) { + if (LevelMonsterTypes[monsterTypeIndex].mtype == uniqueMonsterData.mtype) { break; } } - int count = 0; Point position; - while (true) { - position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; - int count2 = 0; - for (int x = position.x - 3; x < position.x + 3; x++) { - for (int y = position.y - 3; y < position.y + 3; y++) { - if (InDungeonBounds({ x, y }) && CanPlaceMonster({ x, y })) { - count2++; - } - } - } + for (int tries = 0;; tries++) { + if (tries > 997) + return; - if (count2 < 9) { - count++; - if (count < 1000) { - continue; + position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; + int freeSpots = 0; + for (Point testPosition : PointsInRectangleRange(Rectangle { position, 3 })) { + if (CanPlaceMonster(testPosition)) { + freeSpots++; } } + if (freeSpots < 9) + continue; if (CanPlaceMonster(position)) { break; @@ -478,8 +487,8 @@ void PlaceUniqueMonst(int uniqindex, int miniontype, int bosspacksize) position = { UberRow - 2, UberCol }; UberDiabloMonsterIndex = ActiveMonsterCount; } - PlaceMonster(ActiveMonsterCount, uniqtype, position); - PrepareUniqueMonst(monster, uniqindex, miniontype, bosspacksize, uniqueMonsterData); + PlaceMonster(ActiveMonsterCount, monsterTypeIndex, position); + PrepareUniqueMonst(monster, uniqindex, minionIndex, bosspacksize, uniqueMonsterData); } int AddMonsterType(_monster_id type, placeflag placeflag) @@ -539,28 +548,25 @@ void ClrAllMonsters() void PlaceUniqueMonsters() { - for (int u = 0; UniqueMonstersData[u].mtype != -1; u++) { + for (int u = 0; UniqueMonstersData[u].mtype != MT_INVALID; u++) { if (UniqueMonstersData[u].mlevel != currlevel) continue; - bool done = false; - int mt; - for (mt = 0; mt < LevelMonsterTypeCount; mt++) { - done = (LevelMonsterTypes[mt].mtype == UniqueMonstersData[u].mtype); - if (done) - break; - } if (u == UMT_GARBUD && Quests[Q_GARBUD]._qactive == QUEST_NOTAVAIL) - done = false; + continue; if (u == UMT_ZHAR && Quests[Q_ZHAR]._qactive == QUEST_NOTAVAIL) - done = false; + continue; if (u == UMT_SNOTSPIL && Quests[Q_LTBANNER]._qactive == QUEST_NOTAVAIL) - done = false; + continue; if (u == UMT_LACHDAN && Quests[Q_VEIL]._qactive == QUEST_NOTAVAIL) - done = false; + continue; if (u == UMT_WARLORD && Quests[Q_WARLORD]._qactive == QUEST_NOTAVAIL) - done = false; - if (done) - PlaceUniqueMonst(u, mt, 8); + continue; + for (int minionIndex = 0; minionIndex < LevelMonsterTypeCount; minionIndex++) { + if (LevelMonsterTypes[minionIndex].mtype == UniqueMonstersData[u].mtype) { + PlaceUniqueMonst(u, minionIndex, 8); + break; + } + } } } @@ -568,7 +574,7 @@ void PlaceQuestMonsters() { if (!setlevel) { if (Quests[Q_BUTCHER].IsAvailable()) { - PlaceUniqueMonst(UMT_BUTCHER, 0, 0); + PlaceUniqueMonst(UMT_BUTCHER, -1, 0); } if (currlevel == Quests[Q_SKELKING]._qlevel && gbIsMultiplayer) { @@ -611,9 +617,9 @@ void PlaceQuestMonsters() if (currlevel == Quests[Q_BETRAYER]._qlevel && gbIsMultiplayer) { AddMonsterType(UniqueMonstersData[UMT_LAZARUS].mtype, PLACE_UNIQUE); AddMonsterType(UniqueMonstersData[UMT_RED_VEX].mtype, PLACE_UNIQUE); - PlaceUniqueMonst(UMT_LAZARUS, 0, 0); - PlaceUniqueMonst(UMT_RED_VEX, 0, 0); - PlaceUniqueMonst(UMT_BLACKJADE, 0, 0); + PlaceUniqueMonst(UMT_LAZARUS, -1, 0); + PlaceUniqueMonst(UMT_RED_VEX, -1, 0); + PlaceUniqueMonst(UMT_BLACKJADE, -1, 0); auto dunData = LoadFileInMem("Levels\\L4Data\\Vile1.DUN"); SetMapMonsters(dunData.get(), SetPiece.position.megaToWorld()); } @@ -636,10 +642,10 @@ void PlaceQuestMonsters() } } if (UberDiabloMonsterIndex == -1) - PlaceUniqueMonst(UMT_NAKRUL, 0, 0); + PlaceUniqueMonst(UMT_NAKRUL, -1, 0); } } else if (setlvlnum == SL_SKELKING) { - PlaceUniqueMonst(UMT_SKELKING, 0, 0); + PlaceUniqueMonst(UMT_SKELKING, -1, 0); } } @@ -3453,7 +3459,7 @@ void InitTRNForUniqueMonster(Monster &monster) monster.uniqueTRN = LoadFileInMem(filestr); } -void PrepareUniqueMonst(Monster &monster, int uniqindex, int miniontype, int bosspacksize, const UniqueMonsterData &uniqueMonsterData) +void PrepareUniqueMonst(Monster &monster, int uniqindex, int minionIndex, int bosspacksize, const UniqueMonsterData &uniqueMonsterData) { monster._uniqtype = uniqindex + 1; @@ -3551,8 +3557,8 @@ void PrepareUniqueMonst(Monster &monster, int uniqindex, int miniontype, int bos ActiveMonsterCount++; - if (uniqueMonsterData.monsterPack != UniqueMonsterPack::None) { - PlaceGroup(miniontype, bosspacksize, uniqueMonsterData.monsterPack, ActiveMonsterCount - 1); + if (minionIndex != -1 && bosspacksize != 0 && uniqueMonsterData.monsterPack != UniqueMonsterPack::None) { + PlaceMinions(minionIndex, bosspacksize, uniqueMonsterData.monsterPack, ActiveMonsterCount - 1); } if (monster._mAi != AI_GARG) { @@ -3801,14 +3807,14 @@ void InitMonsters() if (!setlevel) { if (!gbIsSpawn) PlaceUniqueMonsters(); - int na = 0; + int monsterCount = 0; for (int s = 16; s < 96; s++) { for (int t = 16; t < 96; t++) { if (!IsTileSolid({ s, t })) - na++; + monsterCount++; } } - int numplacemonsters = na / 30; + int numplacemonsters = monsterCount / 30; if (gbIsMultiplayer) numplacemonsters += numplacemonsters / 2; if (ActiveMonsterCount + numplacemonsters > MAXMONSTERS - 10) @@ -3823,14 +3829,15 @@ void InitMonsters() } } while (ActiveMonsterCount < totalmonsters) { - int mtype = scattertypes[GenerateRnd(numscattypes)]; + int monsterTypeIndex = scattertypes[GenerateRnd(numscattypes)]; + int groupSize = 1; if (currlevel == 1 || GenerateRnd(2) == 0) - na = 1; + groupSize = 1; else if (currlevel == 2 || leveltype == DTYPE_CRYPT) - na = GenerateRnd(2) + 2; + groupSize = GenerateRnd(2) + 2; else - na = GenerateRnd(3) + 3; - PlaceGroup(mtype, na, UniqueMonsterPack::None, 0); + groupSize = GenerateRnd(3) + 3; + PlaceGroup({}, monsterTypeIndex, groupSize, { { 16, 16 }, { 80, 80 } }); } } for (int i = 0; i < nt; i++) { @@ -3852,9 +3859,9 @@ void SetMapMonsters(const uint16_t *dunData, Point startPosition) AddMonsterType(UniqueMonstersData[UMT_LAZARUS].mtype, PLACE_UNIQUE); AddMonsterType(UniqueMonstersData[UMT_RED_VEX].mtype, PLACE_UNIQUE); AddMonsterType(UniqueMonstersData[UMT_BLACKJADE].mtype, PLACE_UNIQUE); - PlaceUniqueMonst(UMT_LAZARUS, 0, 0); - PlaceUniqueMonst(UMT_RED_VEX, 0, 0); - PlaceUniqueMonst(UMT_BLACKJADE, 0, 0); + PlaceUniqueMonst(UMT_LAZARUS, -1, 0); + PlaceUniqueMonst(UMT_RED_VEX, -1, 0); + PlaceUniqueMonst(UMT_BLACKJADE, -1, 0); } int width = SDL_SwapLE16(dunData[0]); @@ -3872,20 +3879,20 @@ void SetMapMonsters(const uint16_t *dunData, Point startPosition) for (int i = 0; i < width; i++) { auto monsterId = static_cast(SDL_SwapLE16(monsterLayer[j * width + i])); if (monsterId != 0) { - int mtype = AddMonsterType(MonstConvTbl[monsterId - 1], PLACE_SPECIAL); - PlaceMonster(ActiveMonsterCount++, mtype, startPosition + Displacement { i, j }); + int monsterTypeIndex = AddMonsterType(MonstConvTbl[monsterId - 1], PLACE_SPECIAL); + PlaceMonster(ActiveMonsterCount++, monsterTypeIndex, startPosition + Displacement { i, j }); } } } } -int AddMonster(Point position, Direction dir, int mtype, bool inMap) +int AddMonster(Point position, Direction dir, int monsterTypeIndex, bool inMap) { if (ActiveMonsterCount < MAXMONSTERS) { int i = ActiveMonsters[ActiveMonsterCount++]; if (inMap) dMonster[position.x][position.y] = i + 1; - InitMonster(Monsters[i], dir, mtype, position); + InitMonster(Monsters[i], dir, monsterTypeIndex, position); return i; } diff --git a/Source/objects.cpp b/Source/objects.cpp index f8b082bf8..ee349d4aa 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -96,10 +96,6 @@ int numobjfiles; /** Tracks progress through the tome sequence that spawns Na-Krul (see OperateNakrulBook()) */ int NaKrulTomeSequence; -/** Specifies the X-coordinate delta between barrels. */ -int bxadd[8] = { -1, 0, 1, -1, 1, -1, 0, 1 }; -/** Specifies the Y-coordinate delta between barrels. */ -int byadd[8] = { -1, -1, -1, 0, 0, 1, 1, 1 }; /** Maps from shrine_id to shrine name. */ const char *const ShrineNames[] = { // TRANSLATORS: Shrine Name Block @@ -291,19 +287,36 @@ _speech_id StoryText[3][3] = { { TEXT_BOOK31, TEXT_BOOK32, TEXT_BOOK33 } }; -bool RndLocOk(int xp, int yp) +bool IsAvailableObjectPosition(Point position) { - if (dMonster[xp][yp] != 0) - return false; - if (dPlayer[xp][yp] != 0) - return false; - if (IsObjectAtPosition({ xp, yp })) - return false; - if (TileContainsSetPiece({ xp, yp })) - return false; - if (nSolidTable[dPiece[xp][yp]]) - return false; - return IsNoneOf(leveltype, DTYPE_CATHEDRAL, DTYPE_CRYPT) || dPiece[xp][yp] <= 126 || dPiece[xp][yp] >= 144; + return dMonster[position.x][position.y] == 0 + && dPlayer[position.x][position.y] == 0 + && !IsObjectAtPosition(position) + && !TileContainsSetPiece(position) + && !nSolidTable[dPiece[position.x][position.y]] + && (IsNoneOf(leveltype, DTYPE_CATHEDRAL, DTYPE_CRYPT) || dPiece[position.x][position.y] <= 126 || dPiece[position.x][position.y] >= 144); +} + +bool IsAvailableObjectPosition(Rectangle area) +{ + for (Point testPosition : PointsInRectangleRange(area)) { + if (!IsAvailableObjectPosition(testPosition)) { + return false; + } + } + + return true; +} + +Point GetRandomAvailableObjectPosition() +{ + while (true) { + Point position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; + if (IsAvailableObjectPosition(position)) + return position; + } + + return {}; } bool CanPlaceWallTrap(int xp, int yp) @@ -316,24 +329,15 @@ bool CanPlaceWallTrap(int xp, int yp) return nTrapTable[dPiece[xp][yp]]; } -void InitRndLocObj(int min, int max, _object_id objtype) +void InitRndLocObj(int min, int max, _object_id objtype, int range = 1) { int numobjs = GenerateRnd(max - min) + min; for (int i = 0; i < numobjs; i++) { while (true) { - int xp = GenerateRnd(80) + 16; - int yp = GenerateRnd(80) + 16; - if (RndLocOk(xp - 1, yp - 1) - && RndLocOk(xp, yp - 1) - && RndLocOk(xp + 1, yp - 1) - && RndLocOk(xp - 1, yp) - && RndLocOk(xp, yp) - && RndLocOk(xp + 1, yp) - && RndLocOk(xp - 1, yp + 1) - && RndLocOk(xp, yp + 1) - && RndLocOk(xp + 1, yp + 1)) { - AddObject(objtype, { xp, yp }); + Point position = GetRandomAvailableObjectPosition(); + if (IsAvailableObjectPosition(Rectangle { position, range })) { + AddObject(objtype, position); break; } } @@ -343,57 +347,19 @@ void InitRndLocObj(int min, int max, _object_id objtype) void InitRndLocBigObj(int min, int max, _object_id objtype) { int numobjs = GenerateRnd(max - min) + min; + for (int i = 0; i < numobjs; i++) { while (true) { - int xp = GenerateRnd(80) + 16; - int yp = GenerateRnd(80) + 16; - if (RndLocOk(xp - 1, yp - 2) - && RndLocOk(xp, yp - 2) - && RndLocOk(xp + 1, yp - 2) - && RndLocOk(xp - 1, yp - 1) - && RndLocOk(xp, yp - 1) - && RndLocOk(xp + 1, yp - 1) - && RndLocOk(xp - 1, yp) - && RndLocOk(xp, yp) - && RndLocOk(xp + 1, yp) - && RndLocOk(xp - 1, yp + 1) - && RndLocOk(xp, yp + 1) - && RndLocOk(xp + 1, yp + 1)) { - AddObject(objtype, { xp, yp }); + Point position = GetRandomAvailableObjectPosition(); + if (IsAvailableObjectPosition(Rectangle { position + Displacement { 0, -2 }, { 2, 1 } }) + && IsAvailableObjectPosition(Rectangle { position, 1 })) { + AddObject(objtype, position); break; } } } } -void InitRndLocObj5x5(int min, int max, _object_id objtype) -{ - int numobjs = min + GenerateRnd(max - min); - for (int i = 0; i < numobjs; i++) { - int xp; - int yp; - int cnt = 0; - bool exit = false; - while (!exit) { - exit = true; - xp = GenerateRnd(80) + 16; - yp = GenerateRnd(80) + 16; - for (int n = -2; n <= 2; n++) { - for (int m = -2; m <= 2; m++) { - if (!RndLocOk(xp + m, yp + n)) - exit = false; - } - } - if (!exit) { - cnt++; - if (cnt > 20000) - return; - } - } - AddObject(objtype, { xp, yp }); - } -} - void ClrAllObjects() { memset(Objects, 0, sizeof(Objects)); @@ -450,87 +416,47 @@ void AddCandles() */ void AddBookLever(Rectangle affectedArea, _speech_id msg) { - int cnt = 0; - Point position; - bool exit = false; - while (!exit) { - exit = true; - position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; - for (int n = -2; n <= 2; n++) { - for (int m = -2; m <= 2; m++) { - if (!RndLocOk(position.x + m, position.y + n)) - exit = false; + for (int tries = 0; tries < 19999; tries++) { + Point position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; + if (IsAvailableObjectPosition(Rectangle { position, 2 })) { + if (Quests[Q_BLIND].IsAvailable()) + AddObject(OBJ_BLINDBOOK, position); + else if (Quests[Q_WARLORD].IsAvailable()) + AddObject(OBJ_STEELTOME, position); + else if (Quests[Q_BLOOD].IsAvailable()) { + position = SetPiece.position.megaToWorld() + Displacement { 9, 24 }; + AddObject(OBJ_BLOODBOOK, position); } - } - if (!exit) { - cnt++; - if (cnt > 20000) - return; + ObjectAtPosition(position)->InitializeQuestBook(affectedArea, leverid, msg); + leverid++; + break; } } - - if (Quests[Q_BLIND].IsAvailable()) - AddObject(OBJ_BLINDBOOK, position); - if (Quests[Q_WARLORD].IsAvailable()) - AddObject(OBJ_STEELTOME, position); - if (Quests[Q_BLOOD].IsAvailable()) { - position = SetPiece.position.megaToWorld() + Displacement { 9, 24 }; - AddObject(OBJ_BLOODBOOK, position); - } - ObjectAtPosition(position)->InitializeQuestBook(affectedArea, leverid, msg); - leverid++; } -void InitRndBarrels() +void InitRndBarrels(_object_id barrelId = OBJ_BARREL, _object_id explosiveBarrelId = OBJ_BARRELEX) { - _object_id barrelId = OBJ_BARREL; - _object_id explosiveBarrelId = OBJ_BARRELEX; - if (leveltype == DTYPE_NEST) { - barrelId = OBJ_POD; - explosiveBarrelId = OBJ_PODEX; - } else if (leveltype == DTYPE_CRYPT) { - barrelId = OBJ_URN; - explosiveBarrelId = OBJ_URNEX; - } - /** number of groups of barrels to generate */ - int numobjs = GenerateRnd(5) + 3; - for (int i = 0; i < numobjs; i++) { - int xp; - int yp; + int groups = GenerateRnd(5) + 3; + + for (int i = 0; i < groups; i++) { + Point position = GetRandomAvailableObjectPosition(); + + AddObject(GenerateRnd(4) != 0 ? barrelId : explosiveBarrelId, position); + + int barrelsInGroup = 1; do { - xp = GenerateRnd(80) + 16; - yp = GenerateRnd(80) + 16; - } while (!RndLocOk(xp, yp)); - _object_id o = (GenerateRnd(4) != 0) ? barrelId : explosiveBarrelId; - AddObject(o, { xp, yp }); - bool found = true; - /** regulates chance to stop placing barrels in current group */ - int p = 0; - /** number of barrels in current group */ - int c = 1; - while (GenerateRnd(p) == 0 && found) { - /** number of tries of placing next barrel in current group */ - int t = 0; - found = false; - while (true) { - if (t >= 3) - break; - int dir = GenerateRnd(8); - xp += bxadd[dir]; - yp += byadd[dir]; - found = RndLocOk(xp, yp); - t++; - if (found) + for (int tries = 0;; tries++) { + if (tries >= 3) + return; + position += static_cast(GenerateRnd(8)); + if (IsAvailableObjectPosition(position)) { + AddObject(GenerateRnd(5) != 0 ? barrelId : explosiveBarrelId, position); + barrelsInGroup++; break; + } } - if (found) { - o = (GenerateRnd(5) != 0) ? barrelId : explosiveBarrelId; - AddObject(o, { xp, yp }); - c++; - } - p = c / 2; - } + } while (GenerateRnd(barrelsInGroup / 2) == 0); } } @@ -804,7 +730,7 @@ void SetupObject(Object &object, Point position, _object_id ot) object._oDoorFlag = false; } -void AddCryptBook(_object_id ot, int v2, int ox, int oy) +void AddCryptBook(_object_id ot, int v2, Point position) { if (ActiveObjectCount >= MAXOBJECTS) return; @@ -812,47 +738,41 @@ void AddCryptBook(_object_id ot, int v2, int ox, int oy) int oi = AvailableObjects[0]; AvailableObjects[0] = AvailableObjects[MAXOBJECTS - 1 - ActiveObjectCount]; ActiveObjects[ActiveObjectCount] = oi; - dObject[ox][oy] = oi + 1; + dObject[position.x][position.y] = oi + 1; Object &object = Objects[oi]; - SetupObject(object, { ox, oy }, ot); + SetupObject(object, position, ot); AddCryptObject(object, v2); ActiveObjectCount++; } -void AddCryptStoryBook(int s) +Point FindPositionForStoryBook() { - int cnt = 0; - int xp; - int yp; - bool exit = false; - while (!exit) { - exit = true; - xp = GenerateRnd(80) + 16; - yp = GenerateRnd(80) + 16; - for (int n = -2; n <= 2; n++) { - for (int m = -3; m <= 3; m++) { - if (!RndLocOk(xp + m, yp + n)) - exit = false; - } - } - if (!exit) { - cnt++; - if (cnt > 20000) - return; - } + Point position; + for (int tries = 0; tries < 19999; tries++) { + position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; + if (IsAvailableObjectPosition({ position + Displacement { -3, -2 }, { 7, 5 } })) + break; } - AddCryptBook(OBJ_L5BOOKS, s, xp, yp); - AddObject(OBJ_L5CANDLE, { xp - 2, yp + 1 }); - AddObject(OBJ_L5CANDLE, { xp - 2, yp }); - AddObject(OBJ_L5CANDLE, { xp - 1, yp - 1 }); - AddObject(OBJ_L5CANDLE, { xp + 1, yp - 1 }); - AddObject(OBJ_L5CANDLE, { xp + 2, yp }); - AddObject(OBJ_L5CANDLE, { xp + 2, yp + 1 }); + + return position; +} + +void AddCryptStoryBook(int s) +{ + Point position = FindPositionForStoryBook(); + + AddCryptBook(OBJ_L5BOOKS, s, position); + AddObject(OBJ_L5CANDLE, position + Displacement { -2, 1 }); + AddObject(OBJ_L5CANDLE, position + Displacement { -2, 0 }); + AddObject(OBJ_L5CANDLE, position + Displacement { -1, -1 }); + AddObject(OBJ_L5CANDLE, position + Displacement { 1, -1 }); + AddObject(OBJ_L5CANDLE, position + Displacement { 2, 0 }); + AddObject(OBJ_L5CANDLE, position + Displacement { 2, 1 }); } void AddNakrulBook(int a1, int a2, int a3) { - AddCryptBook(OBJ_L5BOOKS, a1, a2, a3); + AddCryptBook(OBJ_L5BOOKS, a1, { a2, a3 }); } void AddNakrulGate() @@ -894,33 +814,15 @@ void AddNakrulGate() void AddStoryBooks() { - int cnt = 0; - int xp; - int yp; - bool done = false; - while (!done) { - done = true; - xp = GenerateRnd(80) + 16; - yp = GenerateRnd(80) + 16; - for (int yy = -2; yy <= 2; yy++) { - for (int xx = -3; xx <= 3; xx++) { - if (!RndLocOk(xx + xp, yy + yp)) - done = false; - } - } - if (!done) { - cnt++; - if (cnt > 20000) - return; - } - } - AddObject(OBJ_STORYBOOK, { xp, yp }); - AddObject(OBJ_STORYCANDLE, { xp - 2, yp + 1 }); - AddObject(OBJ_STORYCANDLE, { xp - 2, yp }); - AddObject(OBJ_STORYCANDLE, { xp - 1, yp - 1 }); - AddObject(OBJ_STORYCANDLE, { xp + 1, yp - 1 }); - AddObject(OBJ_STORYCANDLE, { xp + 2, yp }); - AddObject(OBJ_STORYCANDLE, { xp + 2, yp + 1 }); + Point position = FindPositionForStoryBook(); + + AddObject(OBJ_STORYBOOK, position); + AddObject(OBJ_STORYCANDLE, position + Displacement { -2, 1 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { -2, 0 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { -1, -1 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { 1, -1 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { 2, 0 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { 2, 1 }); } void AddHookedBodies(int freq) @@ -979,38 +881,27 @@ void AddL4Goodies() void AddLazStand() { - int cnt = 0; - int xp; - int yp; - bool found = false; - while (!found) { - found = true; - xp = GenerateRnd(80) + 16; - yp = GenerateRnd(80) + 16; - for (int yy = -3; yy <= 3; yy++) { - for (int xx = -2; xx <= 3; xx++) { - if (!RndLocOk(xp + xx, yp + yy)) - found = false; - } - } - if (!found) { - cnt++; - if (cnt > 10000) { - InitRndLocObj(1, 1, OBJ_LAZSTAND); - return; - } + Point position; + for (int tries = 0;; tries++) { + if (tries >= 9999) { + AddObject(OBJ_LAZSTAND, { 1, 1 }); + return; } + position = Point { GenerateRnd(80), GenerateRnd(80) } + Displacement { 16, 16 }; + if (IsAvailableObjectPosition({ position + Displacement { -3, -2 }, { 7, 6 } })) + break; } - AddObject(OBJ_LAZSTAND, { xp, yp }); - AddObject(OBJ_TNUDEM2, { xp, yp + 2 }); - AddObject(OBJ_STORYCANDLE, { xp + 1, yp + 2 }); - AddObject(OBJ_TNUDEM3, { xp + 2, yp + 2 }); - AddObject(OBJ_TNUDEW1, { xp, yp - 2 }); - AddObject(OBJ_STORYCANDLE, { xp + 1, yp - 2 }); - AddObject(OBJ_TNUDEW2, { xp + 2, yp - 2 }); - AddObject(OBJ_STORYCANDLE, { xp - 1, yp - 1 }); - AddObject(OBJ_TNUDEW3, { xp - 1, yp }); - AddObject(OBJ_STORYCANDLE, { xp - 1, yp + 1 }); + + AddObject(OBJ_LAZSTAND, position); + AddObject(OBJ_TNUDEM2, position + Displacement { 0, 2 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { 1, 2 }); + AddObject(OBJ_TNUDEM3, position + Displacement { 2, 2 }); + AddObject(OBJ_TNUDEW1, position + Displacement { 0, -2 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { 1, -2 }); + AddObject(OBJ_TNUDEW2, position + Displacement { 2, -2 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { -1, -1 }); + AddObject(OBJ_TNUDEW3, position + Displacement { -1, 0 }); + AddObject(OBJ_STORYCANDLE, position + Displacement { -1, 1 }); } void DeleteObject(int oi, int i) @@ -1341,41 +1232,33 @@ void AddTorturedBody(int i) Objects[i]._oPreFlag = true; } -void GetRndObjLoc(int randarea, int *xx, int *yy) +/** + * @brief Make 999 attempts at randmly finding a area of the requested size, try a single time more for each size smaller then requested + * @return Found location + */ +Point GetRndObjLoc(int randarea) { - if (randarea == 0) - return; - - int tries = 0; - while (true) { - tries++; - if (tries > 1000 && randarea > 1) + Point position; + for (int tries = 0;; tries++) { + if (tries > 999 && randarea > 1) randarea--; - *xx = GenerateRnd(MAXDUNX); - *yy = GenerateRnd(MAXDUNY); - bool failed = false; - for (int i = 0; i < randarea && !failed; i++) { - for (int j = 0; j < randarea && !failed; j++) { - failed = !RndLocOk(i + *xx, j + *yy); - } - } - if (!failed) + position = Point { GenerateRnd(MAXDUNX), GenerateRnd(MAXDUNY) }; + if (IsAvailableObjectPosition({ position, { randarea, randarea } })) break; } + + return position; } void AddMushPatch() { - int y; - int x; - if (ActiveObjectCount < MAXOBJECTS) { int i = AvailableObjects[0]; - GetRndObjLoc(5, &x, &y); - dObject[x + 1][y + 1] = -(i + 1); - dObject[x + 2][y + 1] = -(i + 1); - dObject[x + 1][y + 2] = -(i + 1); - AddObject(OBJ_MUSHPATCH, { x + 2, y + 2 }); + Point position = GetRndObjLoc(5); + dObject[position.x + 1][position.y + 1] = -(i + 1); + dObject[position.x + 2][position.y + 1] = -(i + 1); + dObject[position.x + 1][position.y + 2] = -(i + 1); + AddObject(OBJ_MUSHPATCH, position + Displacement { 2, 2 }); } } @@ -4492,11 +4375,7 @@ void AddCryptObjects(int x1, int y1, int x2, int y2) void AddSlainHero() { - int x; - int y; - - GetRndObjLoc(5, &x, &y); - AddObject(OBJ_SLAINHERO, { x + 2, y + 2 }); + AddObject(OBJ_SLAINHERO, GetRndObjLoc(5) + Displacement { 2, 2 }); } void InitObjects() @@ -4540,9 +4419,9 @@ void InitObjects() } if (leveltype == DTYPE_CATACOMBS) { if (Quests[Q_ROCK].IsAvailable()) - InitRndLocObj5x5(1, 1, OBJ_STAND); + InitRndLocObj(1, 1, OBJ_STAND, 2); if (Quests[Q_SCHAMB].IsAvailable()) - InitRndLocObj5x5(1, 1, OBJ_BOOK2R); + InitRndLocObj(1, 1, OBJ_BOOK2R, 2); AddL2Objs(0, 0, MAXDUNX, MAXDUNY); AddL2Torches(); if (Quests[Q_BLIND].IsAvailable()) { @@ -4636,12 +4515,12 @@ void InitObjects() AddL4Goodies(); } if (leveltype == DTYPE_NEST) { - InitRndBarrels(); + InitRndBarrels(OBJ_POD, OBJ_PODEX); } if (leveltype == DTYPE_CRYPT) { InitRndLocBigObj(10, 15, OBJ_L5SARC); AddCryptObjects(0, 0, MAXDUNX, MAXDUNY); - InitRndBarrels(); + InitRndBarrels(OBJ_URN, OBJ_URNEX); } InitRndLocObj(5, 10, OBJ_CHEST1); InitRndLocObj(3, 6, OBJ_CHEST2); @@ -4654,7 +4533,7 @@ void InitObjects() } } -void SetMapObjects(const uint16_t *dunData, int startx, int starty) +void SetMapObjects(const uint16_t *dunData, Point start) { bool filesLoaded[65] = {}; @@ -4672,9 +4551,9 @@ void SetMapObjects(const uint16_t *dunData, int startx, int starty) const uint16_t *objectLayer = &dunData[layer2Offset + width * height * 2]; - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - auto objectId = static_cast(SDL_SwapLE16(objectLayer[j * width + i])); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + auto objectId = static_cast(SDL_SwapLE16(objectLayer[x + y * width])); if (objectId != 0) { filesLoaded[AllObjects[ObjTypeConv[objectId]].ofindex] = true; } @@ -4683,11 +4562,12 @@ void SetMapObjects(const uint16_t *dunData, int startx, int starty) LoadLevelObjects(filesLoaded); - for (int j = 0; j < height; j++) { - for (int i = 0; i < width; i++) { - auto objectId = static_cast(SDL_SwapLE16(objectLayer[j * width + i])); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + auto objectId = static_cast(SDL_SwapLE16(objectLayer[x + y * width])); if (objectId != 0) { - AddObject(ObjTypeConv[objectId], { startx + 16 + i, starty + 16 + j }); + AddObject(ObjTypeConv[objectId], start + Displacement { x, y }); + SDL_Log("Object at %dx%d", start.x + x, start.y + y); } } } diff --git a/Source/objects.h b/Source/objects.h index 19624bcec..d3ad3c57d 100644 --- a/Source/objects.h +++ b/Source/objects.h @@ -291,7 +291,7 @@ void AddL2Objs(int x1, int y1, int x2, int y2); void AddL3Objs(int x1, int y1, int x2, int y2); void AddCryptObjects(int x1, int y1, int x2, int y2); void InitObjects(); -void SetMapObjects(const uint16_t *dunData, int startx, int starty); +void SetMapObjects(const uint16_t *dunData, Point start); /** * @brief Spawns an object of the given type at the map coordinates provided * @param objType Type specifier