as for Goblin Fortress problem… i cannot see how vanilla manages to NOT segfault from time to time on it. `RoomDefault` for level says: `Size = 3:7,3:7;`. Living Quarters is `RandomLevel 0:3;`, where that room size is in effect. it has `Square, Random HAS_NO_OTERRAIN; { OTerrain = decoration(CHEAP_BED); Times = 2:6; }`. this simply doomed to generate more items than there is free space.
now, in `level::MakeRoom()` the last part of the code applies room script. it starts with `for(const squarescript& Script : RoomScript->GetSquare())` line. there is the code to get random position in a room, it calls `level::GetRandomSquare()`, and that call returns `ERROR_V2` when there is no room `ERROR_V2` is defined as `(-0x8000, -0x8000);`
in k8ivan the code is:
const std::list<squarescript> Square = RoomScript->GetSquare();
for (std::list<squarescript>::const_iterator i = Square.begin(); i != Square.end(); ++i) {
game::BusyAnimation();
const squarescript *Script = &*i;
const interval *ScriptTimes = Script->GetTimes();
const int Times = (ScriptTimes ? ScriptTimes->Randomize() : 1);
for (int t = 0; t < Times; ++t) {
v2 SquarePos;
if (Script->GetPosition()->GetRandom()) {
const rect *ScriptBorders = Script->GetPosition()->GetBorders();
rect Borders = ScriptBorders ? *ScriptBorders+Pos : rect(Pos, Pos+Size-v2(1, 1));
SquarePos = GetRandomSquare(0, Script->GetPosition()->GetFlags(), &Borders);
} else {
SquarePos = Pos+Script->GetPosition()->GetVector();
}
if (SquarePos == ERROR_V2) {
ABORT("KABOOM!");
} else {
Map[SquarePos.X][SquarePos.Y]->ApplyScript(Script, RoomClass);
}
}
}
and in vanilla the code is:
for(const squarescript& Script : RoomScript->GetSquare())
{
game::BusyAnimation();
const interval* ScriptTimes = Script.GetTimes();
int Times = ScriptTimes ? ScriptTimes->Randomize() : 1;
for(int t = 0; t < Times; ++t)
{
v2 SquarePos;
while(true)
{
if(Script.GetPosition()->GetRandom())
{
const rect* ScriptBorders = Script.GetPosition()->GetBorders();
rect Borders = ScriptBorders ? *ScriptBorders + Pos : rect(Pos, Pos + Size - v2(1, 1));
SquarePos = GetRandomSquare(0, Script.GetPosition()->GetFlags(), &Borders);
}
else
SquarePos = Pos + Script.GetPosition()->GetVector();
Map[SquarePos.X][SquarePos.Y]->ApplyScript(&Script, RoomClass);
if(CheckExpansiveArea(GetLSquare(SquarePos)->GetOLTerrain(), SquarePos.X, SquarePos.Y, !Script.GetPosition()->GetRandom()))
break;
}
}
}
oops. no checks. that's why k8ivan crashed — it properly detects erorr. and vanilla seems to ignore it, either going haywire shooting random memory, or segfaulting. so i wonder WHY doesn't vanilla segfaulting quite often on GF generation?
i mean, `Map` indexing is not bounds-checked in any way, it is just `lsquare*** Map` — not a fancy class, just a pointer. so the code is doomed to access random out-of-bounds memory two times (first index, second index), and then pass that gibberish to `ApplyScript()` as `this` pointer.