This file is indexed.

/usr/src/castle-game-engine-5.2.0/3d/castlesectors.pas is in castle-game-engine-src 5.2.0-3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
{
  Copyright 2006-2014 Michalis Kamburelis.

  This file is part of "Castle Game Engine".

  "Castle Game Engine" is free software; see the file COPYING.txt,
  included in this distribution, for details about the copyright.

  "Castle Game Engine" is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  ----------------------------------------------------------------------------
}

{ Sectors and waypoints, to improve creature AI in 3D levels.
  For user-oriented description what are sectors and waypoints,
  see "The Castle" developer docs,
  [http://castle-engine.sourceforge.net/castle-development.php]. }
unit CastleSectors;

interface

uses SysUtils, CastleUtils, CastleClassUtils, Classes, CastleVectors, CastleBoxes,
  FGL;

type
  TSectorList = class;

  TWaypoint = class
  private
    FSectors: TSectorList;
  public
    constructor Create;
    destructor Destroy; override;
  public
    Position: TVector3Single;

    { Box of the waypoint is only used by TSectorList.LinkToWaypoints,
      to detect which sectors contain ths waypoint (presumably,
      on their border).
      This box is @italic(not) used by actual AI using waypoints/sectors
      (only @link(Position) matters then). }
    Box: TBox3D;

    { Sectors that contain this waypoint. }
    property Sectors: TSectorList read FSectors;
  end;

  TWaypointList = class(specialize TFPGObjectList<TWaypoint>)
  end;

  TSector = class
  private
    FBoxes: TBox3DList;
    FVisibleSectors: TBooleanList;
    FWaypoints: TWaypointList;
  public
    constructor Create;
    destructor Destroy; override;

    property Boxes: TBox3DList read FBoxes;

    { Is Point inside the sector. }
    function PointInside(const Point: TVector3Single): boolean;

    { Does the box collide (at least partially) with sector. }
    function Collision(const Box: TBox3D): boolean;

    { What sectors are visible from this sector.

      When reading this, you should generally be prepared that length
      of it may be smaller than all sectors of your scene.
      That's because sometimes we don't initialize VisibleSectors fully.

      In this case you should assume that not initialized sector
      indexes are visible. And always you can assume that
      Always Sectors[I].VisibleSectors[I] = @true, i.e. the sector
      is visible from itself (assuming that VisibleSectors has
      enough length to contain I). }
    property VisibleSectors: TBooleanList read FVisibleSectors;

    { Waypoints that are included in this sector. }
    property Waypoints: TWaypointList read FWaypoints;
  end;

  ESectorNotInitialized = class(Exception);
  EWaypointNotInitialized = class(Exception);

  TSectorList = class(specialize TFPGObjectList<TSector>)
  public
    { Connect sectors and waypoints into a graph.
      Adds appropriate waypoints to sectors and sectors to waypoints,
      knowing which waypoint belongs (presumably, lies at the border of)
      to which sector.
      A waypoint belongs to the sector simply when TWaypoint.Box
      collides with one of the sector boxes.

      @raises ESectorNotInitialized When some sector is nil.
      @raises EWaypointNotInitialized When some waypoint is nil. }
    procedure LinkToWaypoints(Waypoints: TWaypointList);

    { Returns sector with given point (using IsPointInside of each sector).
      Returns nil if no such sector. }
    function SectorWithPoint(const Point: TVector3Single): TSector;

    { This sets Waypoints contents to the list of waypoints
      that must be passed to travel from sector SectorBegin to SectorEnd.

      Special cases: when either SectorBegin or SectorEnd are nil
      (this can easily happen if you pass here results
      of SectorWithPoint method), or when SectorBegin = SectorEnd,
      or when there is no possible way, it returns @false and
      just clears Waypoints (i.e. sets Waypoints.Count to 0).

      Otherwise (if a way is found) it returns @true and sets
      Waypoints items as appropriate. The order of Waypoints
      is significant: starting from SectorBegin, you should
      first travel to Waypoints[0], then to Waypoints[1] etc.
      In this case for sure we have at least one Waypoint.

      (So the result of this function is actually just a comfortable
      thing, you can get the same result just checking
      Waypoints.Count <> 0)

      TODO: This should use breadth-first search.
      Right now it uses depth-first search. For small sectors+waypoints
      graphs it doesn't matter. }
    class function FindWay(SectorBegin, SectorEnd: TSector;
      Waypoints: TWaypointList): boolean;
  end;

var
  LogSectors: boolean = false;

implementation

uses CastleStringUtils, CastleLog;

{ TWaypoint ------------------------------------------------------------- }

constructor TWaypoint.Create;
begin
  inherited Create;
  FSectors := TSectorList.Create(false);
end;

destructor TWaypoint.Destroy;
begin
  FreeAndNil(FSectors);
  inherited;
end;

{ TSector --------------------------------------------------------------- }

constructor TSector.Create;
begin
  inherited Create;
  FBoxes := TBox3DList.Create;
  FVisibleSectors := TBooleanList.Create;
  FWaypoints := TWaypointList.Create(false);
end;

destructor TSector.Destroy;
begin
  FreeAndNil(FBoxes);
  FreeAndNil(FVisibleSectors);
  FreeAndNil(FWaypoints);
  inherited;
end;

function TSector.PointInside(const Point: TVector3Single): boolean;
var
  I: Integer;
begin
  for I := 0 to Boxes.Count - 1 do
    if Boxes.L[I].PointInside(Point) then
      Exit(true);
  Result := false;
end;

function TSector.Collision(const Box: TBox3D): boolean;
var
  I: Integer;
begin
  for I := 0 to Boxes.Count - 1 do
    if Boxes.L[I].Collision(Box) then
      Exit(true);
  Result := false;
end;

{ TSectorList -------------------------------------------------------- }

procedure TSectorList.LinkToWaypoints(Waypoints: TWaypointList);
var
  S: TSector;
  W: TWaypoint;
  SectorIndex, WaypointIndex: Integer;
begin
  for SectorIndex := 0 to Count - 1 do
  begin
    S := Items[SectorIndex];
    if S = nil then
      raise ESectorNotInitialized.CreateFmt('Sector %d not initialized',
        [SectorIndex]);

    for WaypointIndex := 0 to Waypoints.Count - 1 do
    begin
      W := Waypoints[WaypointIndex];
      if W = nil then
        raise EWaypointNotInitialized.CreateFmt('Waypoint %d not initialized',
          [WaypointIndex]);

      if S.Collision(W.Box) then
      begin
        S.Waypoints.Add(W);
        W.Sectors.Add(S);
        if Log and LogSectors then
          WritelnLog('Sectors', Format('Waypoint %d links to sector %d',
            [WaypointIndex, SectorIndex]));
      end;
    end;
  end;

  if Log and LogSectors then
  begin
    for WaypointIndex := 0 to Waypoints.Count - 1 do
      if Waypoints[WaypointIndex].Sectors.Count <= 1 then
        WritelnLog('Sectors', Format('Warning: Waypoint %d only links to %d sectors. Waypoints that link to 1 or 0 sectors are useless.',
          [WaypointIndex, Waypoints[WaypointIndex].Sectors.Count]));
    for SectorIndex := 0 to Count - 1 do
      if Items[SectorIndex].Waypoints.Count = 0 then
        WritelnLog('Sectors', Format('Warning: Sector %d is not connected to any waypoint. Such sectors are useless.',
          [SectorIndex]));
  end;
end;

function TSectorList.SectorWithPoint(const Point: TVector3Single):
  TSector;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
  begin
    Result := Items[I];
    if Result.PointInside(Point) then
      Exit;
  end;
  Result := nil;
end;

class function TSectorList.FindWay(SectorBegin, SectorEnd: TSector;
  Waypoints: TWaypointList): boolean;
var
  { This is used to avoid falling into loops. }
  SectorsVisited: TSectorList;

  function FindWayToSectorEnd(SectorNow: TSector;
    SectorDistance: Integer): boolean;
  var
    WaypointIndex, SectorIndex: Integer;
    W: TWaypoint;
  begin
    if SectorsVisited.IndexOf(SectorNow) <> -1 then
      Exit(false);
    SectorsVisited.Add(SectorNow);

    if SectorNow = SectorEnd then
    begin
      Waypoints.Count := SectorDistance;
      Exit(true);
    end;

    for WaypointIndex := 0 to SectorNow.Waypoints.Count - 1 do
    begin
      W := SectorNow.Waypoints[WaypointIndex];
      for SectorIndex := 0 to W.Sectors.Count - 1 do
        if FindWayToSectorEnd(W.Sectors[SectorIndex], SectorDistance + 1) then
        begin
          Waypoints[SectorDistance] := W;
          Exit(true);
        end;
    end;

    Result := false;
  end;

begin
  Waypoints.Count := 0;
  if (SectorBegin = nil) or
     (SectorEnd = nil) or
     (SectorBegin = SectorEnd) then
    Exit(false);

  { Note that we know here that SectorBegin <> SectorEnd,
    so the first call to FindWayToSectorEnd will not immediately
    return with true, so Waypoints[0] will for sure be filled...
    so Waypoints.Count will have to be > 0 in this case.
    Just like I promised in the interface. }

  SectorsVisited := TSectorList.Create(false);
  try
    Result := FindWayToSectorEnd(SectorBegin, 0);
  finally SectorsVisited.Free end;
end;

end.