another one IVAN fork

Yesterday, 10:16 pm
Joined: Sep 5, 2010
Interests: make more ivans!
Posts: 167
sadly, there is no wizard command to show dangerous squares. basically, the algo stops when:
1. there is some danger around.
2. it is about to step on a dangerous square.
3. there is something interesting around (new items, new closed doors).
4. there is new direction to go (actually, when the number of available directions changed).

rule #4 allows to walk both in 1-tile tunnels and in wide hallways. and there are some heuristics to detect narrow turns and follow them.

the algo has little flaws here and there, but most of the time it should work as expected. tbh, i am not happy with that mess of a code, but… it works.
Yesterday, 10:28 pm
Joined: Sep 5, 2010
Interests: make more ivans!
Posts: 167
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.
Jump to