DEADSOFTWARE

typo in mapcvt: microseconds -> milliseconds
[d2df-sdl.git] / src / game / g_map.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {$DEFINE MAP_DEBUG_ENABLED_FLAG}
18 unit g_map;
20 interface
22 uses
23 e_graphics, g_basic, MAPDEF, g_textures, Classes,
24 g_phys, wadreader, BinEditor, g_panel, g_grid, md5, binheap, xprofiler, xparser, xdynrec;
26 type
27 TMapInfo = record
28 Map: String;
29 Name: String;
30 Description: String;
31 Author: String;
32 MusicName: String;
33 SkyName: String;
34 Height: Word;
35 Width: Word;
36 end;
38 PRespawnPoint = ^TRespawnPoint;
39 TRespawnPoint = record
40 X, Y: Integer;
41 Direction: TDirection;
42 PointType: Byte;
43 end;
45 PFlagPoint = ^TFlagPoint;
46 TFlagPoint = TRespawnPoint;
48 PFlag = ^TFlag;
49 TFlag = record
50 Obj: TObj;
51 RespawnType: Byte;
52 State: Byte;
53 Count: Integer;
54 CaptureTime: LongWord;
55 Animation: TAnimation;
56 Direction: TDirection;
57 end;
59 function g_Map_Load(Res: String): Boolean;
60 function g_Map_GetMapInfo(Res: String): TMapInfo;
61 function g_Map_GetMapsList(WADName: String): SArray;
62 function g_Map_Exist(Res: String): Boolean;
63 procedure g_Map_Free();
64 procedure g_Map_Update();
66 procedure g_Map_DrawPanels (PanelType: Word); // unaccelerated
67 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
69 procedure g_Map_DrawBack(dx, dy: Integer);
70 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
71 PanelType: Word; b1x3: Boolean=false): Boolean;
72 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
73 procedure g_Map_EnableWall(ID: DWORD);
74 procedure g_Map_DisableWall(ID: DWORD);
75 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
76 procedure g_Map_SetLift(ID: DWORD; t: Integer);
77 procedure g_Map_ReAdd_DieTriggers();
78 function g_Map_IsSpecialTexture(Texture: String): Boolean;
80 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
81 function g_Map_GetPointCount(PointType: Byte): Word;
83 function g_Map_HaveFlagPoints(): Boolean;
85 procedure g_Map_ResetFlag(Flag: Byte);
86 procedure g_Map_DrawFlags();
88 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
90 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
91 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
93 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
95 // returns panel or nil
96 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
97 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
99 // returns panel or nil
100 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
101 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
103 type
104 TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop
106 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
107 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
109 // trace liquid, stepping by `dx` and `dy`
110 // return last seen liquid coords, and `false` if we're started outside of the liquid
111 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
114 procedure g_Map_ProfilersBegin ();
115 procedure g_Map_ProfilersEnd ();
118 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
120 const
121 NNF_NO_NAME = 0;
122 NNF_NAME_BEFORE = 1;
123 NNF_NAME_EQUALS = 2;
124 NNF_NAME_AFTER = 3;
126 function g_Texture_NumNameFindStart(name: String): Boolean;
127 function g_Texture_NumNameFindNext(var newName: String): Byte;
129 const
130 RESPAWNPOINT_PLAYER1 = 1;
131 RESPAWNPOINT_PLAYER2 = 2;
132 RESPAWNPOINT_DM = 3;
133 RESPAWNPOINT_RED = 4;
134 RESPAWNPOINT_BLUE = 5;
136 FLAG_NONE = 0;
137 FLAG_RED = 1;
138 FLAG_BLUE = 2;
139 FLAG_DOM = 3;
141 FLAG_STATE_NONE = 0;
142 FLAG_STATE_NORMAL = 1;
143 FLAG_STATE_DROPPED = 2;
144 FLAG_STATE_CAPTURED = 3;
145 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
146 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
148 FLAG_TIME = 720; // 20 seconds
150 SKY_STRETCH: Single = 1.5;
152 const
153 GridTagInvalid = 0;
155 (* draw order:
156 PANEL_BACK
157 PANEL_STEP
158 PANEL_WALL
159 PANEL_CLOSEDOOR
160 PANEL_ACID1
161 PANEL_ACID2
162 PANEL_WATER
163 PANEL_FORE
164 *)
165 // sorted by draw priority
166 GridTagBack = 1 shl 0;
167 GridTagStep = 1 shl 1;
168 GridTagWall = 1 shl 2;
169 GridTagDoor = 1 shl 3;
170 GridTagAcid1 = 1 shl 4;
171 GridTagAcid2 = 1 shl 5;
172 GridTagWater = 1 shl 6;
173 GridTagFore = 1 shl 7;
174 // the following are invisible
175 GridTagLift = 1 shl 8;
176 GridTagBlockMon = 1 shl 9;
178 GridDrawableMask = (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore);
181 var
182 gWalls: TPanelArray;
183 gRenderBackgrounds: TPanelArray;
184 gRenderForegrounds: TPanelArray;
185 gWater, gAcid1, gAcid2: TPanelArray;
186 gSteps: TPanelArray;
187 gLifts: TPanelArray;
188 gBlockMon: TPanelArray;
189 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
190 //gDOMFlags: array of TFlag;
191 gMapInfo: TMapInfo;
192 gBackSize: TDFPoint;
193 gDoorMap: array of array of DWORD;
194 gLiftMap: array of array of DWORD;
195 gWADHash: TMD5Digest;
196 BackID: DWORD = DWORD(-1);
197 gExternalResources: TStringList;
199 gdbg_map_use_accel_render: Boolean = true;
200 gdbg_map_use_accel_coldet: Boolean = true;
201 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
202 gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
205 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
208 type
209 TPanelGrid = specialize TBodyGridBase<TPanel>;
211 var
212 mapGrid: TPanelGrid = nil; // DO NOT USE! public for debugging only!
215 implementation
217 uses
218 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
219 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
220 g_options, g_triggers, g_player,
221 Math, g_monsters, g_saveload, g_language, g_netmsg,
222 utils, sfs, xstreams,
223 ImagingTypes, Imaging, ImagingUtility,
224 ImagingGif, ImagingNetworkGraphics;
226 const
227 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
228 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
229 FLAG_SIGNATURE = $47414C46; // 'FLAG'
232 var
233 dfmapdef: TDynMapDef = nil;
236 procedure loadMapDefinition ();
237 var
238 pr: TTextParser = nil;
239 st: TStream = nil;
240 WAD: TWADFile = nil;
241 begin
242 if (dfmapdef <> nil) then exit;
243 try
244 e_LogWritefln('parsing "mapdef.txt"...', []);
245 st := openDiskFileRO(DataDir+'mapdef.txt');
246 except
247 st := nil;
248 e_LogWritefln('local "%smapdef.txt" not found', [DataDir]);
249 end;
250 if (st = nil) then
251 begin
252 WAD := TWADFile.Create();
253 if not WAD.ReadFile(GameWAD) then
254 begin
255 //raise Exception.Create('cannot load "game.wad"');
256 st := nil;
257 end
258 else
259 begin
260 st := WAD.openFileStream('mapdef.txt');
261 end;
262 end;
264 if (st = nil) then
265 begin
266 //raise Exception.Create('cannot open "mapdef.txt"');
267 e_LogWritefln('using default "mapdef.txt"...', [], MSG_WARNING);
268 pr := TStrTextParser.Create(defaultMapDef);
269 end
270 else
271 begin
272 pr := TFileTextParser.Create(st);
273 end;
275 try
276 dfmapdef := TDynMapDef.Create(pr);
277 except on e: Exception do
278 raise Exception.Create(Format('ERROR in "mapdef.txt" at (%s,%s): %s', [pr.line, pr.col, e.message]));
279 end;
281 st.Free();
282 WAD.Free();
283 end;
286 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
287 var
288 wst: TSFSMemoryChunkStream = nil;
289 pr: TTextParser = nil;
290 begin
291 result := nil;
292 if (dataLen < 4) then exit;
293 loadMapDefinition();
294 if (dfmapdef = nil) then raise Exception.Create('internal map loader error');
296 wst := TSFSMemoryChunkStream.Create(data, dataLen);
298 if (PAnsiChar(data)[0] = 'M') and (PAnsiChar(data)[1] = 'A') and (PAnsiChar(data)[2] = 'P') and (PByte(data)[3] = 1) then
299 begin
300 // binary map
301 try
302 result := dfmapdef.parseBinMap(wst);
303 except on e: Exception do
304 begin
305 e_LogWritefln('ERROR: %s', [e.message]);
306 wst.Free();
307 result := nil;
308 exit;
309 end;
310 end;
311 wst.Free();
312 end
313 else
314 begin
315 // text map
316 pr := TFileTextParser.Create(wst);
317 try
318 result := dfmapdef.parseMap(pr);
319 except on e: Exception do
320 begin
321 if (pr <> nil) then e_LogWritefln('ERROR at (%s,%s): %s', [pr.line, pr.col, e.message])
322 else e_LogWritefln('ERROR: %s', [e.message]);
323 pr.Free(); // will free `wst`
324 result := nil;
325 exit;
326 end;
327 end;
328 pr.Free(); // will free `wst`
329 end;
330 end;
333 var
334 NNF_PureName: String; // Èìÿ òåêñòóðû áåç öèôð â êîíöå
335 NNF_FirstNum: Integer; // ×èñëî ó íà÷àëüíîé òåêñòóðû
336 NNF_CurrentNum: Integer; // Ñëåäóþùåå ÷èñëî ó òåêñòóðû
339 function g_Texture_NumNameFindStart(name: String): Boolean;
340 var
341 i: Integer;
343 begin
344 Result := False;
345 NNF_PureName := '';
346 NNF_FirstNum := -1;
347 NNF_CurrentNum := -1;
349 for i := Length(name) downto 1 do
350 if (name[i] = '_') then // "_" - ñèìâîë íà÷àëà íîìåðíîãî ïîñòôèêñà
351 begin
352 if i = Length(name) then
353 begin // Íåò öèôð â êîíöå ñòðîêè
354 Exit;
355 end
356 else
357 begin
358 NNF_PureName := Copy(name, 1, i);
359 Delete(name, 1, i);
360 Break;
361 end;
362 end;
364 // Íå ïåðåâåñòè â ÷èñëî:
365 if not TryStrToInt(name, NNF_FirstNum) then
366 Exit;
368 NNF_CurrentNum := 0;
370 Result := True;
371 end;
374 function g_Texture_NumNameFindNext(var newName: String): Byte;
375 begin
376 if (NNF_PureName = '') or (NNF_CurrentNum < 0) then
377 begin
378 newName := '';
379 Result := NNF_NO_NAME;
380 Exit;
381 end;
383 newName := NNF_PureName + IntToStr(NNF_CurrentNum);
385 if NNF_CurrentNum < NNF_FirstNum then
386 Result := NNF_NAME_BEFORE
387 else
388 if NNF_CurrentNum > NNF_FirstNum then
389 Result := NNF_NAME_AFTER
390 else
391 Result := NNF_NAME_EQUALS;
393 Inc(NNF_CurrentNum);
394 end;
397 function panelTypeToTag (panelType: Word): Integer;
398 begin
399 case panelType of
400 PANEL_WALL: result := GridTagWall; // gWalls
401 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
402 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
403 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
404 PANEL_WATER: result := GridTagWater; // gWater
405 PANEL_ACID1: result := GridTagAcid1; // gAcid1
406 PANEL_ACID2: result := GridTagAcid2; // gAcid2
407 PANEL_STEP: result := GridTagStep; // gSteps
408 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
409 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
410 else result := GridTagInvalid;
411 end;
412 end;
415 function dplLess (a, b: TObject): Boolean;
416 var
417 pa, pb: TPanel;
418 begin
419 pa := TPanel(a);
420 pb := TPanel(b);
421 if (pa.tag < pb.tag) then begin result := true; exit; end;
422 if (pa.tag > pb.tag) then begin result := false; exit; end;
423 result := (pa.arrIdx < pb.arrIdx);
424 end;
426 procedure dplClear ();
427 begin
428 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
429 end;
432 type
433 TPanelID = record
434 PWhere: ^TPanelArray;
435 PArrID: Integer;
436 end;
438 var
439 PanelById: array of TPanelID;
440 Textures: TLevelTextureArray;
441 RespawnPoints: Array of TRespawnPoint;
442 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
443 //DOMFlagPoints: Array of TFlagPoint;
446 procedure g_Map_ProfilersBegin ();
447 begin
448 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
449 profMapCollision.mainBegin(g_profile_collision);
450 // create sections
451 if g_profile_collision then
452 begin
453 profMapCollision.sectionBegin('*solids');
454 profMapCollision.sectionEnd();
455 profMapCollision.sectionBegin('liquids');
456 profMapCollision.sectionEnd();
457 end;
458 end;
460 procedure g_Map_ProfilersEnd ();
461 begin
462 if (profMapCollision <> nil) then profMapCollision.mainEnd();
463 end;
466 // wall index in `gWalls` or -1
467 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
468 var
469 ex, ey: Integer;
470 begin
471 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, (GridTagWall or GridTagDoor));
472 if (result <> nil) then
473 begin
474 if (hitx <> nil) then hitx^ := ex;
475 if (hity <> nil) then hity^ := ey;
476 end
477 else
478 begin
479 if (hitx <> nil) then hitx^ := x1;
480 if (hity <> nil) then hity^ := y1;
481 end;
482 end;
484 // returns panel or nil
485 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
486 var
487 ex, ey: Integer;
488 begin
489 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, tag);
490 if (result <> nil) then
491 begin
492 if (hitx <> nil) then hitx^ := ex;
493 if (hity <> nil) then hity^ := ey;
494 end
495 else
496 begin
497 if (hitx <> nil) then hitx^ := x1;
498 if (hity <> nil) then hity^ := y1;
499 end;
500 end;
503 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
505 function checker (pan: TPanel; tag: Integer): Boolean;
506 begin
508 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
509 begin
510 result := pan.Enabled; // stop if wall is enabled
511 exit;
512 end;
515 if ((tag and GridTagLift) <> 0) then
516 begin
517 // stop if the lift of the right type
518 result :=
519 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
520 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
521 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
522 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
523 exit;
524 end;
526 result := true; // otherwise, stop anyway, 'cause `forEachAtPoint()` is guaranteed to call this only for correct panels
527 end;
529 var
530 tagmask: Integer = 0;
531 begin
532 result := false;
534 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
535 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
536 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
537 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
538 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
539 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
540 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
542 if (tagmask = 0) then exit;// just in case
543 if ((tagmask and GridTagLift) <> 0) then
544 begin
545 // slow
546 result := (mapGrid.forEachAtPoint(x, y, checker, tagmask) <> nil);
547 end
548 else
549 begin
550 // fast
551 result := (mapGrid.forEachAtPoint(x, y, nil, tagmask) <> nil);
552 end;
553 end;
556 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
557 begin
558 result := nil;
559 if (tagmask = 0) then exit;
560 result := mapGrid.forEachAtPoint(x, y, nil, tagmask);
561 end;
564 function g_Map_IsSpecialTexture(Texture: String): Boolean;
565 begin
566 Result := (Texture = TEXTURE_NAME_WATER) or
567 (Texture = TEXTURE_NAME_ACID1) or
568 (Texture = TEXTURE_NAME_ACID2);
569 end;
571 procedure CreateDoorMap();
572 var
573 PanelArray: Array of record
574 X, Y: Integer;
575 Width, Height: Word;
576 Active: Boolean;
577 PanelID: DWORD;
578 end;
579 a, b, c, m, i, len: Integer;
580 ok: Boolean;
581 begin
582 if gWalls = nil then
583 Exit;
585 i := 0;
586 len := 128;
587 SetLength(PanelArray, len);
589 for a := 0 to High(gWalls) do
590 if gWalls[a].Door then
591 begin
592 PanelArray[i].X := gWalls[a].X;
593 PanelArray[i].Y := gWalls[a].Y;
594 PanelArray[i].Width := gWalls[a].Width;
595 PanelArray[i].Height := gWalls[a].Height;
596 PanelArray[i].Active := True;
597 PanelArray[i].PanelID := a;
599 i := i + 1;
600 if i = len then
601 begin
602 len := len + 128;
603 SetLength(PanelArray, len);
604 end;
605 end;
607 // Íåò äâåðåé:
608 if i = 0 then
609 begin
610 PanelArray := nil;
611 Exit;
612 end;
614 SetLength(gDoorMap, 0);
616 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
618 for a := 0 to i-1 do
619 if PanelArray[a].Active then
620 begin
621 PanelArray[a].Active := False;
622 m := Length(gDoorMap);
623 SetLength(gDoorMap, m+1);
624 SetLength(gDoorMap[m], 1);
625 gDoorMap[m, 0] := PanelArray[a].PanelID;
626 ok := True;
628 while ok do
629 begin
630 ok := False;
632 for b := 0 to i-1 do
633 if PanelArray[b].Active then
634 for c := 0 to High(gDoorMap[m]) do
635 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
636 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
637 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
638 PanelArray[b].Width, PanelArray[b].Height,
639 gWalls[gDoorMap[m, c]].X,
640 gWalls[gDoorMap[m, c]].Y,
641 gWalls[gDoorMap[m, c]].Width,
642 gWalls[gDoorMap[m, c]].Height) then
643 begin
644 PanelArray[b].Active := False;
645 SetLength(gDoorMap[m],
646 Length(gDoorMap[m])+1);
647 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
648 ok := True;
649 Break;
650 end;
651 end;
653 g_Game_StepLoading();
654 end;
656 PanelArray := nil;
657 end;
659 procedure CreateLiftMap();
660 var
661 PanelArray: Array of record
662 X, Y: Integer;
663 Width, Height: Word;
664 Active: Boolean;
665 end;
666 a, b, c, len, i, j: Integer;
667 ok: Boolean;
668 begin
669 if gLifts = nil then
670 Exit;
672 len := Length(gLifts);
673 SetLength(PanelArray, len);
675 for a := 0 to len-1 do
676 begin
677 PanelArray[a].X := gLifts[a].X;
678 PanelArray[a].Y := gLifts[a].Y;
679 PanelArray[a].Width := gLifts[a].Width;
680 PanelArray[a].Height := gLifts[a].Height;
681 PanelArray[a].Active := True;
682 end;
684 SetLength(gLiftMap, len);
685 i := 0;
687 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
689 for a := 0 to len-1 do
690 if PanelArray[a].Active then
691 begin
692 PanelArray[a].Active := False;
693 SetLength(gLiftMap[i], 32);
694 j := 0;
695 gLiftMap[i, j] := a;
696 ok := True;
698 while ok do
699 begin
700 ok := False;
701 for b := 0 to len-1 do
702 if PanelArray[b].Active then
703 for c := 0 to j do
704 if g_CollideAround(PanelArray[b].X,
705 PanelArray[b].Y,
706 PanelArray[b].Width,
707 PanelArray[b].Height,
708 PanelArray[gLiftMap[i, c]].X,
709 PanelArray[gLiftMap[i, c]].Y,
710 PanelArray[gLiftMap[i, c]].Width,
711 PanelArray[gLiftMap[i, c]].Height) then
712 begin
713 PanelArray[b].Active := False;
714 j := j+1;
715 if j > High(gLiftMap[i]) then
716 SetLength(gLiftMap[i],
717 Length(gLiftMap[i])+32);
719 gLiftMap[i, j] := b;
720 ok := True;
722 Break;
723 end;
724 end;
726 SetLength(gLiftMap[i], j+1);
727 i := i+1;
729 g_Game_StepLoading();
730 end;
732 SetLength(gLiftMap, i);
734 PanelArray := nil;
735 end;
737 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
738 CurTex: Integer; sav: Boolean): Integer;
739 var
740 len: Integer;
741 panels: ^TPanelArray;
742 begin
743 Result := -1;
745 case PanelRec.PanelType of
746 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
747 panels := @gWalls;
748 PANEL_BACK:
749 panels := @gRenderBackgrounds;
750 PANEL_FORE:
751 panels := @gRenderForegrounds;
752 PANEL_WATER:
753 panels := @gWater;
754 PANEL_ACID1:
755 panels := @gAcid1;
756 PANEL_ACID2:
757 panels := @gAcid2;
758 PANEL_STEP:
759 panels := @gSteps;
760 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
761 panels := @gLifts;
762 PANEL_BLOCKMON:
763 panels := @gBlockMon;
764 else
765 Exit;
766 end;
768 len := Length(panels^);
769 SetLength(panels^, len + 1);
771 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
772 panels^[len].arrIdx := len;
773 panels^[len].proxyId := -1;
774 panels^[len].tag := panelTypeToTag(PanelRec.PanelType);
775 if sav then
776 panels^[len].SaveIt := True;
778 Result := len;
780 len := Length(PanelByID);
781 SetLength(PanelByID, len + 1);
782 PanelByID[len].PWhere := panels;
783 PanelByID[len].PArrID := Result;
784 end;
786 function CreateNullTexture(RecName: String): Integer;
787 begin
788 SetLength(Textures, Length(Textures)+1);
789 result := High(Textures);
791 with Textures[High(Textures)] do
792 begin
793 TextureName := RecName;
794 Width := 1;
795 Height := 1;
796 Anim := False;
797 TextureID := LongWord(TEXTURE_NONE);
798 end;
799 end;
801 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
802 var
803 WAD: TWADFile;
804 TextureData: Pointer;
805 WADName, txname: String;
806 a, ResLength: Integer;
807 begin
808 Result := -1;
810 if Textures <> nil then
811 for a := 0 to High(Textures) do
812 if Textures[a].TextureName = RecName then
813 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
814 Result := a;
815 Exit;
816 end;
818 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
819 if (RecName = TEXTURE_NAME_WATER) or
820 (RecName = TEXTURE_NAME_ACID1) or
821 (RecName = TEXTURE_NAME_ACID2) then
822 begin
823 SetLength(Textures, Length(Textures)+1);
825 with Textures[High(Textures)] do
826 begin
827 TextureName := RecName;
829 if TextureName = TEXTURE_NAME_WATER then
830 TextureID := LongWord(TEXTURE_SPECIAL_WATER)
831 else
832 if TextureName = TEXTURE_NAME_ACID1 then
833 TextureID := LongWord(TEXTURE_SPECIAL_ACID1)
834 else
835 if TextureName = TEXTURE_NAME_ACID2 then
836 TextureID := LongWord(TEXTURE_SPECIAL_ACID2);
838 Anim := False;
839 end;
841 result := High(Textures);
842 Exit;
843 end;
845 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
846 WADName := g_ExtractWadName(RecName);
848 WAD := TWADFile.Create();
850 if WADName <> '' then
851 WADName := GameDir+'/wads/'+WADName
852 else
853 WADName := Map;
855 WAD.ReadFile(WADName);
857 txname := RecName;
859 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
860 begin
861 FreeMem(TextureData);
862 RecName := 'COMMON\ALIEN';
863 end;
866 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
867 begin
868 SetLength(Textures, Length(Textures)+1);
869 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
870 Exit;
871 e_GetTextureSize(Textures[High(Textures)].TextureID,
872 @Textures[High(Textures)].Width,
873 @Textures[High(Textures)].Height);
874 FreeMem(TextureData);
875 Textures[High(Textures)].TextureName := {RecName}txname;
876 Textures[High(Textures)].Anim := False;
878 result := High(Textures);
879 end
880 else // Íåò òàêîãî ðåóñðñà â WAD'å
881 begin
882 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
883 if log then
884 begin
885 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
886 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
887 end;
888 end;
890 WAD.Free();
891 end;
893 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
894 var
895 WAD: TWADFile;
896 TextureWAD: PChar = nil;
897 TextData: Pointer = nil;
898 TextureData: Pointer = nil;
899 cfg: TConfig = nil;
900 WADName: String;
901 ResLength: Integer;
902 TextureResource: String;
903 _width, _height, _framecount, _speed: Integer;
904 _backanimation: Boolean;
905 //imgfmt: string;
906 ia: TDynImageDataArray = nil;
907 f, c, frdelay, frloop: Integer;
908 begin
909 result := -1;
911 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
913 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
914 WADName := g_ExtractWadName(RecName);
916 WAD := TWADFile.Create();
917 try
918 if WADName <> '' then
919 WADName := GameDir+'/wads/'+WADName
920 else
921 WADName := Map;
923 WAD.ReadFile(WADName);
925 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
926 begin
927 if log then
928 begin
929 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
930 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
931 end;
932 exit;
933 end;
935 {TEST
936 if WADName = Map then
937 begin
938 //FreeMem(TextureWAD);
939 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
940 end;
943 WAD.FreeWAD();
945 if ResLength < 6 then
946 begin
947 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
948 exit;
949 end;
951 // ýòî ïòèöà? ýòî ñàìîë¸ò?
952 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
953 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
954 begin
955 // íåò, ýòî ñóïåðìåí!
956 if not WAD.ReadMemory(TextureWAD, ResLength) then
957 begin
958 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
959 exit;
960 end;
962 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
963 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
964 begin
965 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
966 exit;
967 end;
969 cfg := TConfig.CreateMem(TextData, ResLength);
971 TextureResource := cfg.ReadStr('', 'resource', '');
972 if TextureResource = '' then
973 begin
974 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
975 exit;
976 end;
978 _width := cfg.ReadInt('', 'framewidth', 0);
979 _height := cfg.ReadInt('', 'frameheight', 0);
980 _framecount := cfg.ReadInt('', 'framecount', 0);
981 _speed := cfg.ReadInt('', 'waitcount', 0);
982 _backanimation := cfg.ReadBool('', 'backanimation', False);
984 cfg.Free();
985 cfg := nil;
987 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
988 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
989 begin
990 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
991 exit;
992 end;
994 WAD.Free();
995 WAD := nil;
997 SetLength(Textures, Length(Textures)+1);
998 with Textures[High(Textures)] do
999 begin
1000 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
1001 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
1002 begin
1003 TextureName := RecName;
1004 Width := _width;
1005 Height := _height;
1006 Anim := True;
1007 FramesCount := _framecount;
1008 Speed := _speed;
1009 result := High(Textures);
1010 end
1011 else
1012 begin
1013 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
1014 end;
1015 end;
1016 end
1017 else
1018 begin
1019 // try animated image
1021 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
1022 if length(imgfmt) = 0 then
1023 begin
1024 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
1025 exit;
1026 end;
1028 GlobalMetadata.ClearMetaItems();
1029 GlobalMetadata.ClearMetaItemsForSaving();
1030 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
1031 begin
1032 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
1033 exit;
1034 end;
1035 if length(ia) = 0 then
1036 begin
1037 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
1038 exit;
1039 end;
1041 WAD.Free();
1042 WAD := nil;
1044 _width := ia[0].width;
1045 _height := ia[0].height;
1046 _framecount := length(ia);
1047 _speed := 1;
1048 _backanimation := false;
1049 frdelay := -1;
1050 frloop := -666;
1051 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
1052 begin
1053 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
1054 try
1055 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
1056 frdelay := f;
1057 if f < 0 then f := 0;
1058 // rounding ;-)
1059 c := f mod 28;
1060 if c < 13 then c := 0 else c := 1;
1061 f := (f div 28)+c;
1062 if f < 1 then f := 1 else if f > 255 then f := 255;
1063 _speed := f;
1064 except
1065 end;
1066 end;
1067 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
1068 begin
1069 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
1070 try
1071 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
1072 frloop := f;
1073 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
1074 except
1075 end;
1076 end;
1077 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
1078 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
1079 f := ord(_backanimation);
1080 e_WriteLog(Format('Animated texture file "%s": %d frames (delay:%d; back:%d; frdelay:%d; frloop:%d), %dx%d', [RecName, length(ia), _speed, f, frdelay, frloop, _width, _height]), MSG_NOTIFY);
1082 SetLength(Textures, Length(Textures)+1);
1083 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
1084 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
1085 begin
1086 Textures[High(Textures)].TextureName := RecName;
1087 Textures[High(Textures)].Width := _width;
1088 Textures[High(Textures)].Height := _height;
1089 Textures[High(Textures)].Anim := True;
1090 Textures[High(Textures)].FramesCount := length(ia);
1091 Textures[High(Textures)].Speed := _speed;
1092 result := High(Textures);
1093 //writeln(' CREATED!');
1094 end
1095 else
1096 begin
1097 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
1098 end;
1099 end;
1100 finally
1101 for f := 0 to High(ia) do FreeImage(ia[f]);
1102 WAD.Free();
1103 cfg.Free();
1104 if TextureWAD <> nil then FreeMem(TextureWAD);
1105 if TextData <> nil then FreeMem(TextData);
1106 if TextureData <> nil then FreeMem(TextureData);
1107 end;
1108 end;
1110 procedure CreateItem(Item: TItemRec_1);
1111 begin
1112 if g_Game_IsClient then Exit;
1114 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
1115 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
1116 Exit;
1118 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
1119 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
1120 end;
1122 procedure CreateArea(Area: TAreaRec_1);
1123 var
1124 a: Integer;
1125 id: DWORD = 0;
1126 begin
1127 case Area.AreaType of
1128 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
1129 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
1130 begin
1131 SetLength(RespawnPoints, Length(RespawnPoints)+1);
1132 with RespawnPoints[High(RespawnPoints)] do
1133 begin
1134 X := Area.X;
1135 Y := Area.Y;
1136 Direction := TDirection(Area.Direction);
1138 case Area.AreaType of
1139 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
1140 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
1141 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
1142 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
1143 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
1144 end;
1145 end;
1146 end;
1148 AREA_REDFLAG, AREA_BLUEFLAG:
1149 begin
1150 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
1152 if FlagPoints[a] <> nil then Exit;
1154 New(FlagPoints[a]);
1156 with FlagPoints[a]^ do
1157 begin
1158 X := Area.X-FLAGRECT.X;
1159 Y := Area.Y-FLAGRECT.Y;
1160 Direction := TDirection(Area.Direction);
1161 end;
1163 with gFlags[a] do
1164 begin
1165 case a of
1166 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
1167 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
1168 end;
1170 Animation := TAnimation.Create(id, True, 8);
1171 Obj.Rect := FLAGRECT;
1173 g_Map_ResetFlag(a);
1174 end;
1175 end;
1177 AREA_DOMFLAG:
1178 begin
1179 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
1180 with DOMFlagPoints[High(DOMFlagPoints)] do
1181 begin
1182 X := Area.X;
1183 Y := Area.Y;
1184 Direction := TDirection(Area.Direction);
1185 end;
1187 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
1188 end;
1189 end;
1190 end;
1192 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
1193 var
1194 _trigger: TTrigger;
1195 begin
1196 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
1198 with _trigger do
1199 begin
1200 X := Trigger.X;
1201 Y := Trigger.Y;
1202 Width := Trigger.Width;
1203 Height := Trigger.Height;
1204 Enabled := ByteBool(Trigger.Enabled);
1205 TexturePanel := Trigger.TexturePanel;
1206 TexturePanelType := fTexturePanel1Type;
1207 ShotPanelType := fTexturePanel2Type;
1208 TriggerType := Trigger.TriggerType;
1209 ActivateType := Trigger.ActivateType;
1210 Keys := Trigger.Keys;
1211 Data.Default := Trigger.DATA;
1212 end;
1214 g_Triggers_Create(_trigger);
1215 end;
1217 procedure CreateMonster(monster: TMonsterRec_1);
1218 var
1219 a: Integer;
1220 mon: TMonster;
1221 begin
1222 if g_Game_IsClient then Exit;
1224 if (gGameSettings.GameType = GT_SINGLE)
1225 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1226 begin
1227 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1229 if gTriggers <> nil then
1230 begin
1231 for a := 0 to High(gTriggers) do
1232 begin
1233 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1234 begin
1235 if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
1236 end;
1237 end;
1238 end;
1240 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1241 end;
1242 end;
1244 procedure g_Map_ReAdd_DieTriggers();
1246 function monsDieTrig (mon: TMonster): Boolean;
1247 var
1248 a: Integer;
1249 begin
1250 result := false; // don't stop
1251 mon.ClearTriggers();
1252 for a := 0 to High(gTriggers) do
1253 begin
1254 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1255 begin
1256 if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
1257 end;
1258 end;
1259 end;
1261 begin
1262 if g_Game_IsClient then Exit;
1264 g_Mons_ForEach(monsDieTrig);
1265 end;
1267 function extractWadName(resourceName: string): string;
1268 var
1269 posN: Integer;
1270 begin
1271 posN := Pos(':', resourceName);
1272 if posN > 0 then
1273 Result:= Copy(resourceName, 0, posN-1)
1274 else
1275 Result := '';
1276 end;
1278 procedure addResToExternalResList(res: string);
1279 begin
1280 res := extractWadName(res);
1281 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1282 gExternalResources.Add(res);
1283 end;
1285 procedure generateExternalResourcesList({mapReader: TMapReader_1}map: TDynRecord);
1286 var
1287 textures: TTexturesRec1Array;
1288 mapHeader: TMapHeaderRec_1;
1289 i: integer;
1290 resFile: String = '';
1291 begin
1292 if gExternalResources = nil then
1293 gExternalResources := TStringList.Create;
1295 gExternalResources.Clear;
1296 textures := GetTextures(map);
1297 for i := 0 to High(textures) do
1298 begin
1299 addResToExternalResList(resFile);
1300 end;
1302 textures := nil;
1304 mapHeader := GetMapHeader(map);
1306 addResToExternalResList(mapHeader.MusicName);
1307 addResToExternalResList(mapHeader.SkyName);
1308 end;
1311 procedure mapCreateGrid ();
1312 var
1313 mapX0: Integer = $3fffffff;
1314 mapY0: Integer = $3fffffff;
1315 mapX1: Integer = -$3fffffff;
1316 mapY1: Integer = -$3fffffff;
1318 procedure calcBoundingBox (constref panels: TPanelArray);
1319 var
1320 idx: Integer;
1321 pan: TPanel;
1322 begin
1323 for idx := 0 to High(panels) do
1324 begin
1325 pan := panels[idx];
1326 if not pan.visvalid then continue;
1327 if (pan.Width < 1) or (pan.Height < 1) then continue;
1328 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1329 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1330 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1331 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1332 end;
1333 end;
1335 procedure addPanelsToGrid (constref panels: TPanelArray);
1336 var
1337 idx: Integer;
1338 pan: TPanel;
1339 newtag: Integer;
1340 begin
1341 //tag := panelTypeToTag(tag);
1342 for idx := 0 to High(panels) do
1343 begin
1344 pan := panels[idx];
1345 if not pan.visvalid then continue;
1346 if (pan.proxyId <> -1) then
1347 begin
1348 {$IF DEFINED(D2F_DEBUG)}
1349 e_WriteLog(Format('DUPLICATE wall #%d(%d) enabled (%d); type:%08x', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.PanelType]), MSG_NOTIFY);
1350 {$ENDIF}
1351 continue;
1352 end;
1353 case pan.PanelType of
1354 PANEL_WALL: newtag := GridTagWall;
1355 PANEL_OPENDOOR, PANEL_CLOSEDOOR: newtag := GridTagDoor;
1356 PANEL_BACK: newtag := GridTagBack;
1357 PANEL_FORE: newtag := GridTagFore;
1358 PANEL_WATER: newtag := GridTagWater;
1359 PANEL_ACID1: newtag := GridTagAcid1;
1360 PANEL_ACID2: newtag := GridTagAcid2;
1361 PANEL_STEP: newtag := GridTagStep;
1362 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: newtag := GridTagLift;
1363 PANEL_BLOCKMON: newtag := GridTagBlockMon;
1364 else continue; // oops
1365 end;
1366 pan.tag := newtag;
1368 pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, newtag);
1369 // "enabled" flag has meaning only for doors and walls (engine assumes it); but meh...
1370 mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
1371 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
1373 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
1374 begin
1375 e_WriteLog(Format('INSERTED wall #%d(%d) enabled (%d)', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY);
1376 end;
1378 {$ENDIF}
1379 end;
1380 end;
1382 begin
1383 mapGrid.Free();
1384 mapGrid := nil;
1386 calcBoundingBox(gWalls);
1387 calcBoundingBox(gRenderBackgrounds);
1388 calcBoundingBox(gRenderForegrounds);
1389 calcBoundingBox(gWater);
1390 calcBoundingBox(gAcid1);
1391 calcBoundingBox(gAcid2);
1392 calcBoundingBox(gSteps);
1393 calcBoundingBox(gLifts);
1394 calcBoundingBox(gBlockMon);
1396 e_LogWritefln('map dimensions: (%d,%d)-(%d,%d); editor size:(0,0)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1, gMapInfo.Width, gMapInfo.Height]);
1398 if (mapX0 > 0) then mapX0 := 0;
1399 if (mapY0 > 0) then mapY0 := 0;
1401 if (mapX1 < gMapInfo.Width-1) then mapX1 := gMapInfo.Width-1;
1402 if (mapY1 < gMapInfo.Height-1) then mapY1 := gMapInfo.Height-1;
1404 mapGrid := TPanelGrid.Create(mapX0-128, mapY0-128, mapX1-mapX0+1+128*2, mapY1-mapY0+1+128*2);
1405 //mapGrid := TPanelGrid.Create(0, 0, gMapInfo.Width, gMapInfo.Height);
1407 addPanelsToGrid(gWalls);
1408 addPanelsToGrid(gRenderBackgrounds);
1409 addPanelsToGrid(gRenderForegrounds);
1410 addPanelsToGrid(gWater);
1411 addPanelsToGrid(gAcid1);
1412 addPanelsToGrid(gAcid2);
1413 addPanelsToGrid(gSteps);
1414 addPanelsToGrid(gLifts); // it doesn't matter which LIFT type is used here
1415 addPanelsToGrid(gBlockMon);
1417 mapGrid.dumpStats();
1419 g_Mons_InitTree(mapGrid.gridX0, mapGrid.gridY0, mapGrid.gridWidth, mapGrid.gridHeight);
1420 end;
1423 function g_Map_Load(Res: String): Boolean;
1424 const
1425 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1426 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1427 var
1428 WAD: TWADFile;
1429 //MapReader: TMapReader_1;
1430 mapReader: TDynRecord = nil;
1431 Header: TMapHeaderRec_1;
1432 _textures: TTexturesRec1Array;
1433 _texnummap: array of Integer; // `_textures` -> `Textures`
1434 panels: TPanelsRec1Array;
1435 items: TItemsRec1Array;
1436 monsters: TMonsterRec1Array;
1437 areas: TAreasRec1Array;
1438 triggers: TTriggersRec1Array;
1439 a, b, c, k: Integer;
1440 PanelID: DWORD;
1441 AddTextures: TAddTextureArray;
1442 texture: TTextureRec_1;
1443 TriggersTable: Array of record
1444 TexturePanel: Integer;
1445 LiftPanel: Integer;
1446 DoorPanel: Integer;
1447 ShotPanel: Integer;
1448 end;
1449 FileName, mapResName, s, TexName: String;
1450 Data: Pointer;
1451 Len: Integer;
1452 ok, isAnim, trigRef: Boolean;
1453 CurTex, ntn: Integer;
1454 begin
1455 mapGrid.Free();
1456 mapGrid := nil;
1458 Result := False;
1459 gMapInfo.Map := Res;
1460 TriggersTable := nil;
1461 FillChar(texture, SizeOf(texture), 0);
1463 sfsGCDisable(); // temporary disable removing of temporary volumes
1464 try
1465 // Çàãðóçêà WAD:
1466 FileName := g_ExtractWadName(Res);
1467 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1468 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1470 WAD := TWADFile.Create();
1471 if not WAD.ReadFile(FileName) then
1472 begin
1473 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1474 WAD.Free();
1475 Exit;
1476 end;
1478 //k8: why loader ignores path here?
1479 mapResName := g_ExtractFileName(Res);
1480 if not WAD.GetMapResource(mapResName, Data, Len) then
1481 begin
1482 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1483 WAD.Free();
1484 Exit;
1485 end;
1487 WAD.Free();
1489 if (Len < 4) then
1490 begin
1491 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1492 FreeMem(Data);
1493 exit;
1494 end;
1496 // Çàãðóçêà êàðòû:
1497 e_LogWritefln('Loading map: %s', [mapResName], MSG_NOTIFY);
1498 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1501 if (PChar(Data)[0] = 'M') and (PChar(Data)[1] = 'A') and (PChar(Data)[2] = 'P') and (PByte(Data)[3] = 1) then
1502 begin
1503 // nothing
1504 end
1505 else
1506 begin
1507 e_LogWritefln('Loading text map: %s', [mapResName]);
1508 loadMapDefinition();
1509 if (dfmapdef = nil) then raise Exception.Create('internal map loader error');
1510 //e_LogWritefln('***'#10'%s'#10'***', [dfmapdef.headerType.definition]);
1511 wst := TSFSMemoryChunkStream.Create(Data, Len);
1512 try
1513 pr := TFileTextParser.Create(wst);
1514 e_LogWritefln('parsing text map: %s', [mapResName]);
1515 rec := dfmapdef.parseMap(pr);
1516 except on e: Exception do
1517 begin
1518 if (pr <> nil) then e_LogWritefln('ERROR at (%s,%s): %s', [pr.line, pr.col, e.message])
1519 else e_LogWritefln('ERROR: %s', [e.message]);
1520 pr.Free();
1521 wst.Free();
1522 FreeMem(Data);
1523 exit;
1524 end;
1525 end;
1526 pr.Free();
1527 //wst.Free(); // pr will do it
1528 e_LogWritefln('writing text map to temporary bin storage...', []);
1529 st := TMemoryStream.Create();
1530 try
1531 rec.writeBinTo(st);
1532 Len := Integer(st.position);
1533 st.position := 0;
1534 FreeMem(Data);
1535 GetMem(Data, Len);
1536 st.ReadBuffer(Data^, Len);
1537 except on e: Exception do
1538 begin
1539 rec.Free();
1540 st.Free();
1541 e_LogWritefln('ERROR: %s', [e.message]);
1542 FreeMem(Data);
1543 exit;
1544 end;
1545 end;
1546 st.Free();
1547 end;
1549 try
1550 mapReader := g_Map_ParseMap(Data, Len);
1551 except
1552 mapReader.Free();
1553 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1554 FreeMem(Data);
1555 MapReader.Free();
1556 Exit;
1557 end;
1560 MapReader := TMapReader_1.Create();
1561 if not MapReader.LoadMap(Data) then
1562 begin
1563 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1564 FreeMem(Data);
1565 MapReader.Free();
1566 Exit;
1567 end;
1570 FreeMem(Data);
1571 generateExternalResourcesList(MapReader);
1572 // Çàãðóçêà òåêñòóð:
1573 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1574 _textures := GetTextures(mapReader);
1575 _texnummap := nil;
1577 // Çàãðóçêà îïèñàíèÿ êàðòû:
1578 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1579 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1580 Header := GetMapHeader(mapReader);
1582 with gMapInfo do
1583 begin
1584 Name := Header.MapName;
1585 Description := Header.MapDescription;
1586 Author := Header.MapAuthor;
1587 MusicName := Header.MusicName;
1588 SkyName := Header.SkyName;
1589 Height := Header.Height;
1590 Width := Header.Width;
1591 end;
1593 // Äîáàâëåíèå òåêñòóð â Textures[]:
1594 if _textures <> nil then
1595 begin
1596 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1597 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
1598 SetLength(_texnummap, length(_textures));
1600 for a := 0 to High(_textures) do
1601 begin
1602 SetLength(s, 64);
1603 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
1604 for b := 1 to Length(s) do
1605 begin
1606 if s[b] = #0 then
1607 begin
1608 SetLength(s, b-1);
1609 Break;
1610 end;
1611 end;
1612 {$IF DEFINED(D2F_DEBUG)}
1613 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1614 {$ENDIF}
1615 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1616 // Àíèìèðîâàííàÿ òåêñòóðà:
1617 if ByteBool(_textures[a].Anim) then
1618 begin
1619 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1620 if ntn < 0 then
1621 begin
1622 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1623 ntn := CreateNullTexture(_textures[a].Resource);
1624 end;
1625 end
1626 else // Îáû÷íàÿ òåêñòóðà:
1627 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1628 if ntn < 0 then
1629 begin
1630 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1631 ntn := CreateNullTexture(_textures[a].Resource);
1632 end;
1634 _texnummap[a] := ntn; // fix texture number
1635 g_Game_StepLoading();
1636 end;
1637 end;
1639 // Çàãðóçêà òðèããåðîâ:
1640 gTriggerClientID := 0;
1641 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1642 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1643 triggers := GetTriggers(mapReader);
1645 // Çàãðóçêà ïàíåëåé:
1646 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1647 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1648 panels := GetPanels(mapReader);
1650 // check texture numbers for panels
1651 for a := 0 to High(panels) do
1652 begin
1653 if panels[a].TextureNum > High(_textures) then
1654 begin
1655 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1656 result := false;
1657 exit;
1658 end;
1659 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1660 end;
1662 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1663 if triggers <> nil then
1664 begin
1665 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1666 SetLength(TriggersTable, Length(triggers));
1667 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1669 for a := 0 to High(TriggersTable) do
1670 begin
1671 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1672 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1673 // Ëèôòû:
1674 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1675 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1676 else
1677 TriggersTable[a].LiftPanel := -1;
1678 // Äâåðè:
1679 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1680 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1681 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1682 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1683 else
1684 TriggersTable[a].DoorPanel := -1;
1685 // Òóðåëü:
1686 if triggers[a].TriggerType = TRIGGER_SHOT then
1687 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1688 else
1689 TriggersTable[a].ShotPanel := -1;
1691 g_Game_StepLoading();
1692 end;
1693 end;
1695 // Ñîçäàåì ïàíåëè:
1696 if panels <> nil then
1697 begin
1698 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1699 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1701 for a := 0 to High(panels) do
1702 begin
1703 SetLength(AddTextures, 0);
1704 trigRef := False;
1705 CurTex := -1;
1706 if _textures <> nil then
1707 begin
1708 texture := _textures[panels[a].TextureNum];
1709 ok := True;
1710 end
1711 else
1712 ok := False;
1714 if ok then
1715 begin
1716 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1717 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1718 ok := False;
1719 if (TriggersTable <> nil) and (_textures <> nil) then
1720 for b := 0 to High(TriggersTable) do
1721 if (TriggersTable[b].TexturePanel = a)
1722 or (TriggersTable[b].ShotPanel = a) then
1723 begin
1724 trigRef := True;
1725 ok := True;
1726 Break;
1727 end;
1728 end;
1730 if ok then
1731 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1732 SetLength(s, 64);
1733 CopyMemory(@s[1], @texture.Resource[0], 64);
1734 // Èçìåðÿåì äëèíó:
1735 Len := Length(s);
1736 for c := Len downto 1 do
1737 if s[c] <> #0 then
1738 begin
1739 Len := c;
1740 Break;
1741 end;
1742 SetLength(s, Len);
1744 // Ñïåö-òåêñòóðû çàïðåùåíû:
1745 if g_Map_IsSpecialTexture(s) then
1746 ok := False
1747 else
1748 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1749 ok := g_Texture_NumNameFindStart(s);
1751 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1752 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1753 if ok then
1754 begin
1755 k := NNF_NAME_BEFORE;
1756 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1757 while ok or (k = NNF_NAME_BEFORE) or
1758 (k = NNF_NAME_EQUALS) do
1759 begin
1760 k := g_Texture_NumNameFindNext(TexName);
1762 if (k = NNF_NAME_BEFORE) or
1763 (k = NNF_NAME_AFTER) then
1764 begin
1765 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1766 if ByteBool(texture.Anim) then
1767 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1768 isAnim := True;
1769 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1770 if not ok then
1771 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1772 isAnim := False;
1773 ok := CreateTexture(TexName, FileName, False) >= 0;
1774 end;
1775 end
1776 else
1777 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1778 isAnim := False;
1779 ok := CreateTexture(TexName, FileName, False) >= 0;
1780 if not ok then
1781 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1782 isAnim := True;
1783 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1784 end;
1785 end;
1787 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1788 if ok then
1789 begin
1790 for c := 0 to High(Textures) do
1791 if Textures[c].TextureName = TexName then
1792 begin
1793 SetLength(AddTextures, Length(AddTextures)+1);
1794 AddTextures[High(AddTextures)].Texture := c;
1795 AddTextures[High(AddTextures)].Anim := isAnim;
1796 Break;
1797 end;
1798 end;
1799 end
1800 else
1801 if k = NNF_NAME_EQUALS then
1802 begin
1803 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1804 SetLength(AddTextures, Length(AddTextures)+1);
1805 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1806 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1807 CurTex := High(AddTextures);
1808 ok := True;
1809 end
1810 else // NNF_NO_NAME
1811 ok := False;
1812 end; // while ok...
1814 ok := True;
1815 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1816 end; // if ok - ññûëàþòñÿ òðèããåðû
1818 if not ok then
1819 begin
1820 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1821 SetLength(AddTextures, 1);
1822 AddTextures[0].Texture := panels[a].TextureNum;
1823 AddTextures[0].Anim := ByteBool(texture.Anim);
1824 CurTex := 0;
1825 end;
1827 //e_WriteLog(Format('panel #%d: TextureNum=%d; ht=%d; ht1=%d; atl=%d', [a, panels[a].TextureNum, High(_textures), High(Textures), High(AddTextures)]), MSG_NOTIFY);
1829 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1830 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1832 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1833 if TriggersTable <> nil then
1834 for b := 0 to High(TriggersTable) do
1835 begin
1836 // Òðèããåð äâåðè/ëèôòà:
1837 if (TriggersTable[b].LiftPanel = a) or
1838 (TriggersTable[b].DoorPanel = a) then
1839 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1840 // Òðèããåð ñìåíû òåêñòóðû:
1841 if TriggersTable[b].TexturePanel = a then
1842 triggers[b].TexturePanel := PanelID;
1843 // Òðèããåð "Òóðåëü":
1844 if TriggersTable[b].ShotPanel = a then
1845 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1846 end;
1848 g_Game_StepLoading();
1849 end;
1850 end;
1852 // create map grid, init other grids (for monsters, for example)
1853 e_WriteLog('Creating map grid', MSG_NOTIFY);
1854 mapCreateGrid();
1856 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1857 if (triggers <> nil) and not gLoadGameMode then
1858 begin
1859 e_LogWritefln(' Creating triggers (%d)...', [Length(triggers)]);
1860 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1861 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1862 for a := 0 to High(triggers) do
1863 begin
1864 if (triggers[a].TexturePanel <> -1) then
1865 begin
1866 if (TriggersTable[a].TexturePanel < 0) or (TriggersTable[a].TexturePanel > High(panels)) then
1867 begin
1868 e_WriteLog('error loading map: invalid panel index for trigger', MSG_FATALERROR);
1869 result := false;
1870 exit;
1871 end;
1872 b := panels[TriggersTable[a].TexturePanel].PanelType;
1873 end
1874 else
1875 begin
1876 b := 0;
1877 end;
1878 if (triggers[a].TriggerType = TRIGGER_SHOT) and (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1879 begin
1880 c := panels[TriggersTable[a].ShotPanel].PanelType
1881 end
1882 else
1883 begin
1884 c := 0;
1885 end;
1886 CreateTrigger(triggers[a], b, c);
1887 end;
1888 end;
1890 // Çàãðóçêà ïðåäìåòîâ:
1891 e_WriteLog(' Loading items...', MSG_NOTIFY);
1892 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1893 items := GetItems(mapReader);
1895 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1896 if (items <> nil) and not gLoadGameMode then
1897 begin
1898 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1899 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1900 for a := 0 to High(items) do
1901 CreateItem(Items[a]);
1902 end;
1904 // Çàãðóçêà îáëàñòåé:
1905 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1906 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1907 areas := GetAreas(mapReader);
1909 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1910 if areas <> nil then
1911 begin
1912 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1913 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1914 for a := 0 to High(areas) do
1915 CreateArea(areas[a]);
1916 end;
1918 // Çàãðóçêà ìîíñòðîâ:
1919 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1920 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1921 monsters := GetMonsters(mapReader);
1923 gTotalMonsters := 0;
1925 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1926 if (monsters <> nil) and not gLoadGameMode then
1927 begin
1928 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1929 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1930 for a := 0 to High(monsters) do
1931 CreateMonster(monsters[a]);
1932 end;
1934 MapReader.Free();
1936 // Çàãðóçêà íåáà:
1937 if gMapInfo.SkyName <> '' then
1938 begin
1939 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1940 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1941 FileName := g_ExtractWadName(gMapInfo.SkyName);
1943 if FileName <> '' then
1944 FileName := GameDir+'/wads/'+FileName
1945 else
1946 begin
1947 FileName := g_ExtractWadName(Res);
1948 end;
1950 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1951 if g_Texture_CreateWAD(BackID, s) then
1952 begin
1953 g_Game_SetupScreenSize();
1954 end
1955 else
1956 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1957 end;
1959 // Çàãðóçêà ìóçûêè:
1960 ok := False;
1961 if gMapInfo.MusicName <> '' then
1962 begin
1963 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1964 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1965 FileName := g_ExtractWadName(gMapInfo.MusicName);
1967 if FileName <> '' then
1968 FileName := GameDir+'/wads/'+FileName
1969 else
1970 begin
1971 FileName := g_ExtractWadName(Res);
1972 end;
1974 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1975 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1976 ok := True
1977 else
1978 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1979 end;
1981 // Îñòàëüíûå óñòàíâêè:
1982 CreateDoorMap();
1983 CreateLiftMap();
1985 g_Items_Init();
1986 g_Weapon_Init();
1987 g_Monsters_Init();
1989 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1990 if not gLoadGameMode then
1991 g_GFX_Init();
1993 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1994 _textures := nil;
1995 panels := nil;
1996 items := nil;
1997 areas := nil;
1998 triggers := nil;
1999 TriggersTable := nil;
2000 AddTextures := nil;
2002 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
2003 if ok and (not gLoadGameMode) then
2004 begin
2005 gMusic.SetByName(gMapInfo.MusicName);
2006 gMusic.Play();
2007 end
2008 else
2009 gMusic.SetByName('');
2010 finally
2011 sfsGCEnable(); // enable releasing unused volumes
2012 end;
2014 e_WriteLog('Done loading map.', MSG_NOTIFY);
2015 Result := True;
2016 end;
2018 function g_Map_GetMapInfo(Res: String): TMapInfo;
2019 var
2020 WAD: TWADFile;
2021 MapReader: TDynRecord;
2022 Header: TMapHeaderRec_1;
2023 FileName: String;
2024 Data: Pointer;
2025 Len: Integer;
2026 begin
2027 FillChar(Result, SizeOf(Result), 0);
2028 FileName := g_ExtractWadName(Res);
2030 WAD := TWADFile.Create();
2031 if not WAD.ReadFile(FileName) then
2032 begin
2033 WAD.Free();
2034 Exit;
2035 end;
2037 //k8: it ignores path again
2038 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
2039 begin
2040 WAD.Free();
2041 Exit;
2042 end;
2044 WAD.Free();
2047 MapReader := TMapReader_1.Create();
2048 if not MapReader.LoadMap(Data) then
2049 begin
2050 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2051 ZeroMemory(@Header, SizeOf(Header));
2052 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2053 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2054 end
2055 else
2056 begin
2057 Header := MapReader.GetMapHeader();
2058 Result.Name := Header.MapName;
2059 Result.Description := Header.MapDescription;
2060 end;
2062 try
2063 mapReader := g_Map_ParseMap(Data, Len);
2064 except
2065 mapReader := nil;
2066 end;
2068 FreeMem(Data);
2069 //MapReader.Free();
2071 if (mapReader <> nil) then Header := GetMapHeader(mapReader) else FillChar(Header, sizeof(Header), 0);
2072 MapReader.Free();
2074 if (Header.Width > 0) and (Header.Height > 0) then
2075 begin
2076 Result.Name := Header.MapName;
2077 Result.Description := Header.MapDescription;
2078 end
2079 else
2080 begin
2081 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2082 ZeroMemory(@Header, SizeOf(Header));
2083 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2084 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2085 end;
2087 Result.Map := Res;
2088 Result.Author := Header.MapAuthor;
2089 Result.Height := Header.Height;
2090 Result.Width := Header.Width;
2091 end;
2093 function g_Map_GetMapsList(WADName: string): SArray;
2094 var
2095 WAD: TWADFile;
2096 a: Integer;
2097 ResList: SArray;
2098 begin
2099 Result := nil;
2100 WAD := TWADFile.Create();
2101 if not WAD.ReadFile(WADName) then
2102 begin
2103 WAD.Free();
2104 Exit;
2105 end;
2106 ResList := WAD.GetMapResources();
2107 if ResList <> nil then
2108 begin
2109 for a := 0 to High(ResList) do
2110 begin
2111 SetLength(Result, Length(Result)+1);
2112 Result[High(Result)] := ResList[a];
2113 end;
2114 end;
2115 WAD.Free();
2116 end;
2118 function g_Map_Exist(Res: string): Boolean;
2119 var
2120 WAD: TWADFile;
2121 FileName, mnn: string;
2122 ResList: SArray;
2123 a: Integer;
2124 begin
2125 Result := False;
2127 FileName := addWadExtension(g_ExtractWadName(Res));
2129 WAD := TWADFile.Create;
2130 if not WAD.ReadFile(FileName) then
2131 begin
2132 WAD.Free();
2133 Exit;
2134 end;
2136 ResList := WAD.GetMapResources();
2137 WAD.Free();
2139 mnn := g_ExtractFileName(Res);
2140 if ResList <> nil then
2141 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
2142 begin
2143 Result := True;
2144 Exit;
2145 end;
2146 end;
2148 procedure g_Map_Free();
2149 var
2150 a: Integer;
2152 procedure FreePanelArray(var panels: TPanelArray);
2153 var
2154 i: Integer;
2156 begin
2157 if panels <> nil then
2158 begin
2159 for i := 0 to High(panels) do
2160 panels[i].Free();
2161 panels := nil;
2162 end;
2163 end;
2165 begin
2166 g_GFX_Free();
2167 g_Weapon_Free();
2168 g_Items_Free();
2169 g_Triggers_Free();
2170 g_Monsters_Free();
2172 RespawnPoints := nil;
2173 if FlagPoints[FLAG_RED] <> nil then
2174 begin
2175 Dispose(FlagPoints[FLAG_RED]);
2176 FlagPoints[FLAG_RED] := nil;
2177 end;
2178 if FlagPoints[FLAG_BLUE] <> nil then
2179 begin
2180 Dispose(FlagPoints[FLAG_BLUE]);
2181 FlagPoints[FLAG_BLUE] := nil;
2182 end;
2183 //DOMFlagPoints := nil;
2185 //gDOMFlags := nil;
2187 if Textures <> nil then
2188 begin
2189 for a := 0 to High(Textures) do
2190 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
2191 if Textures[a].Anim then
2192 g_Frames_DeleteByID(Textures[a].FramesID)
2193 else
2194 if Textures[a].TextureID <> LongWord(TEXTURE_NONE) then
2195 e_DeleteTexture(Textures[a].TextureID);
2197 Textures := nil;
2198 end;
2200 FreePanelArray(gWalls);
2201 FreePanelArray(gRenderBackgrounds);
2202 FreePanelArray(gRenderForegrounds);
2203 FreePanelArray(gWater);
2204 FreePanelArray(gAcid1);
2205 FreePanelArray(gAcid2);
2206 FreePanelArray(gSteps);
2207 FreePanelArray(gLifts);
2208 FreePanelArray(gBlockMon);
2210 if BackID <> DWORD(-1) then
2211 begin
2212 gBackSize.X := 0;
2213 gBackSize.Y := 0;
2214 e_DeleteTexture(BackID);
2215 BackID := DWORD(-1);
2216 end;
2218 g_Game_StopAllSounds(False);
2219 gMusic.FreeSound();
2220 g_Sound_Delete(gMapInfo.MusicName);
2222 gMapInfo.Name := '';
2223 gMapInfo.Description := '';
2224 gMapInfo.MusicName := '';
2225 gMapInfo.Height := 0;
2226 gMapInfo.Width := 0;
2228 gDoorMap := nil;
2229 gLiftMap := nil;
2231 PanelByID := nil;
2232 end;
2234 procedure g_Map_Update();
2235 var
2236 a, d, j: Integer;
2237 m: Word;
2238 s: String;
2240 procedure UpdatePanelArray(var panels: TPanelArray);
2241 var
2242 i: Integer;
2244 begin
2245 if panels <> nil then
2246 for i := 0 to High(panels) do
2247 panels[i].Update();
2248 end;
2250 begin
2251 UpdatePanelArray(gWalls);
2252 UpdatePanelArray(gRenderBackgrounds);
2253 UpdatePanelArray(gRenderForegrounds);
2254 UpdatePanelArray(gWater);
2255 UpdatePanelArray(gAcid1);
2256 UpdatePanelArray(gAcid2);
2257 UpdatePanelArray(gSteps);
2259 if gGameSettings.GameMode = GM_CTF then
2260 begin
2261 for a := FLAG_RED to FLAG_BLUE do
2262 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2263 with gFlags[a] do
2264 begin
2265 if gFlags[a].Animation <> nil then
2266 gFlags[a].Animation.Update();
2268 m := g_Obj_Move(@Obj, True, True);
2270 if gTime mod (GAME_TICK*2) <> 0 then
2271 Continue;
2273 // Ñîïðîòèâëåíèå âîçäóõà:
2274 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2276 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
2277 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2278 begin
2279 g_Map_ResetFlag(a);
2280 gFlags[a].CaptureTime := 0;
2281 if a = FLAG_RED then
2282 s := _lc[I_PLAYER_FLAG_RED]
2283 else
2284 s := _lc[I_PLAYER_FLAG_BLUE];
2285 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2287 if g_Game_IsNet then
2288 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2289 Continue;
2290 end;
2292 if Count > 0 then
2293 Count := Count - 1;
2295 // Èãðîê áåðåò ôëàã:
2296 if gPlayers <> nil then
2297 begin
2298 j := Random(Length(gPlayers)) - 1;
2300 for d := 0 to High(gPlayers) do
2301 begin
2302 Inc(j);
2303 if j > High(gPlayers) then
2304 j := 0;
2306 if gPlayers[j] <> nil then
2307 if gPlayers[j].Live and
2308 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2309 begin
2310 if gPlayers[j].GetFlag(a) then
2311 Break;
2312 end;
2313 end;
2314 end;
2315 end;
2316 end;
2317 end;
2320 // old algo
2321 procedure g_Map_DrawPanels (PanelType: Word);
2323 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2324 var
2325 idx: Integer;
2326 begin
2327 if (panels <> nil) then
2328 begin
2329 // alas, no visible set
2330 for idx := 0 to High(panels) do
2331 begin
2332 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
2333 end;
2334 end;
2335 end;
2337 begin
2338 case PanelType of
2339 PANEL_WALL: DrawPanels(gWalls);
2340 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2341 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2342 PANEL_FORE: DrawPanels(gRenderForegrounds);
2343 PANEL_WATER: DrawPanels(gWater);
2344 PANEL_ACID1: DrawPanels(gAcid1);
2345 PANEL_ACID2: DrawPanels(gAcid2);
2346 PANEL_STEP: DrawPanels(gSteps);
2347 end;
2348 end;
2351 // new algo
2352 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2354 function checker (pan: TPanel; tag: Integer): Boolean;
2355 begin
2356 result := false; // don't stop, ever
2357 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2358 gDrawPanelList.insert(pan);
2359 end;
2361 begin
2362 dplClear();
2363 //tagmask := panelTypeToTag(PanelType);
2364 mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask);
2365 // list will be rendered in `g_game.DrawPlayer()`
2366 end;
2369 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2371 function checker (pan: TPanel; tag: Integer): Boolean;
2372 begin
2373 result := false; // don't stop, ever
2374 pan.DrawShadowVolume(lightX, lightY, radius);
2375 end;
2377 begin
2378 mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2379 end;
2382 procedure g_Map_DrawBack(dx, dy: Integer);
2383 begin
2384 if gDrawBackGround and (BackID <> DWORD(-1)) then
2385 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2386 else
2387 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2388 end;
2390 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2391 PanelType: Word; b1x3: Boolean=false): Boolean;
2392 var
2393 a, h: Integer;
2394 begin
2395 Result := False;
2397 if WordBool(PanelType and PANEL_WALL) then
2398 if gWalls <> nil then
2399 begin
2400 h := High(gWalls);
2402 for a := 0 to h do
2403 if gWalls[a].Enabled and
2404 g_Collide(X, Y, Width, Height,
2405 gWalls[a].X, gWalls[a].Y,
2406 gWalls[a].Width, gWalls[a].Height) then
2407 begin
2408 Result := True;
2409 Exit;
2410 end;
2411 end;
2413 if WordBool(PanelType and PANEL_WATER) then
2414 if gWater <> nil then
2415 begin
2416 h := High(gWater);
2418 for a := 0 to h do
2419 if g_Collide(X, Y, Width, Height,
2420 gWater[a].X, gWater[a].Y,
2421 gWater[a].Width, gWater[a].Height) then
2422 begin
2423 Result := True;
2424 Exit;
2425 end;
2426 end;
2428 if WordBool(PanelType and PANEL_ACID1) then
2429 if gAcid1 <> nil then
2430 begin
2431 h := High(gAcid1);
2433 for a := 0 to h do
2434 if g_Collide(X, Y, Width, Height,
2435 gAcid1[a].X, gAcid1[a].Y,
2436 gAcid1[a].Width, gAcid1[a].Height) then
2437 begin
2438 Result := True;
2439 Exit;
2440 end;
2441 end;
2443 if WordBool(PanelType and PANEL_ACID2) then
2444 if gAcid2 <> nil then
2445 begin
2446 h := High(gAcid2);
2448 for a := 0 to h do
2449 if g_Collide(X, Y, Width, Height,
2450 gAcid2[a].X, gAcid2[a].Y,
2451 gAcid2[a].Width, gAcid2[a].Height) then
2452 begin
2453 Result := True;
2454 Exit;
2455 end;
2456 end;
2458 if WordBool(PanelType and PANEL_STEP) then
2459 if gSteps <> nil then
2460 begin
2461 h := High(gSteps);
2463 for a := 0 to h do
2464 if g_Collide(X, Y, Width, Height,
2465 gSteps[a].X, gSteps[a].Y,
2466 gSteps[a].Width, gSteps[a].Height) then
2467 begin
2468 Result := True;
2469 Exit;
2470 end;
2471 end;
2473 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2474 if gLifts <> nil then
2475 begin
2476 h := High(gLifts);
2478 for a := 0 to h do
2479 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2480 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2481 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2482 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2483 g_Collide(X, Y, Width, Height,
2484 gLifts[a].X, gLifts[a].Y,
2485 gLifts[a].Width, gLifts[a].Height) then
2486 begin
2487 Result := True;
2488 Exit;
2489 end;
2490 end;
2492 if WordBool(PanelType and PANEL_BLOCKMON) then
2493 if gBlockMon <> nil then
2494 begin
2495 h := High(gBlockMon);
2497 for a := 0 to h do
2498 if ( (not b1x3) or
2499 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2500 g_Collide(X, Y, Width, Height,
2501 gBlockMon[a].X, gBlockMon[a].Y,
2502 gBlockMon[a].Width, gBlockMon[a].Height) then
2503 begin
2504 Result := True;
2505 Exit;
2506 end;
2507 end;
2508 end;
2510 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2511 var
2512 texid: DWORD;
2514 function checkPanels (constref panels: TPanelArray): Boolean;
2515 var
2516 a: Integer;
2517 begin
2518 result := false;
2519 if panels = nil then exit;
2520 for a := 0 to High(panels) do
2521 begin
2522 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2523 begin
2524 result := true;
2525 texid := panels[a].GetTextureID();
2526 exit;
2527 end;
2528 end;
2529 end;
2531 begin
2532 texid := LongWord(TEXTURE_NONE);
2533 result := texid;
2534 if not checkPanels(gWater) then
2535 if not checkPanels(gAcid1) then
2536 if not checkPanels(gAcid2) then exit;
2537 result := texid;
2538 end;
2541 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2542 const
2543 SlowMask = GridTagLift or GridTagBlockMon;
2544 function checker (pan: TPanel; tag: Integer): Boolean;
2545 begin
2547 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2548 begin
2549 result := pan.Enabled;
2550 exit;
2551 end;
2554 if ((tag and GridTagLift) <> 0) then
2555 begin
2556 result :=
2557 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2558 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2559 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2560 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
2561 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
2562 exit;
2563 end;
2565 if ((tag and GridTagBlockMon) <> 0) then
2566 begin
2567 result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2568 exit;
2569 end;
2571 // other shit
2572 //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2573 result := true; // i found her!
2574 end;
2576 var
2577 tagmask: Integer = 0;
2578 begin
2579 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2580 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2581 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2582 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2583 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2584 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2585 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2587 if (tagmask = 0) then begin result := false; exit; end; // just in case
2589 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2590 if gdbg_map_use_accel_coldet then
2591 begin
2592 if (Width = 1) and (Height = 1) then
2593 begin
2594 if ((tagmask and SlowMask) <> 0) then
2595 begin
2596 // slow
2597 result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil);
2598 end
2599 else
2600 begin
2601 // fast
2602 result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil);
2603 end;
2604 end
2605 else
2606 begin
2607 if ((tagmask and SlowMask) <> 0) then
2608 begin
2609 // slow
2610 result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil);
2611 end
2612 else
2613 begin
2614 // fast
2615 result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil);
2616 end;
2617 end;
2618 end
2619 else
2620 begin
2621 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2622 end;
2623 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2624 end;
2627 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2628 var
2629 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2630 texid: DWORD;
2632 // slightly different from the old code, but meh...
2633 function checker (pan: TPanel; tag: Integer): Boolean;
2634 begin
2635 result := false; // don't stop, ever
2636 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2637 // check priorities
2638 case cctype of
2639 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2640 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2641 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2642 end;
2643 // collision?
2644 //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2645 // yeah
2646 texid := pan.GetTextureID();
2647 // water? water has the highest priority, so stop right here
2648 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2649 // acid2?
2650 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2651 // acid1?
2652 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2653 end;
2655 begin
2656 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2657 if gdbg_map_use_accel_coldet then
2658 begin
2659 texid := LongWord(TEXTURE_NONE);
2660 if (Width = 1) and (Height = 1) then
2661 begin
2662 mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2663 end
2664 else
2665 begin
2666 mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2667 end;
2668 result := texid;
2669 end
2670 else
2671 begin
2672 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2673 end;
2674 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2675 end;
2678 procedure g_Map_EnableWall(ID: DWORD);
2679 var
2680 pan: TPanel;
2681 begin
2682 pan := gWalls[ID];
2683 pan.Enabled := True;
2684 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, True);
2686 mapGrid.proxyEnabled[pan.proxyId] := true;
2687 //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
2688 //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
2690 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(gWalls[ID].PanelType, ID);
2692 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2693 //e_WriteLog(Format('ENABLE: wall #%d(%d) enabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY);
2694 {$ENDIF}
2695 end;
2697 procedure g_Map_DisableWall(ID: DWORD);
2698 var
2699 pan: TPanel;
2700 begin
2701 pan := gWalls[ID];
2702 pan.Enabled := False;
2703 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, False);
2705 mapGrid.proxyEnabled[pan.proxyId] := false;
2706 //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
2708 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, ID);
2710 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2711 //e_WriteLog(Format('DISABLE: wall #%d(%d) disabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY);
2712 {$ENDIF}
2713 end;
2715 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2716 var
2717 tp: TPanel;
2718 begin
2719 case PanelType of
2720 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2721 tp := gWalls[ID];
2722 PANEL_FORE:
2723 tp := gRenderForegrounds[ID];
2724 PANEL_BACK:
2725 tp := gRenderBackgrounds[ID];
2726 PANEL_WATER:
2727 tp := gWater[ID];
2728 PANEL_ACID1:
2729 tp := gAcid1[ID];
2730 PANEL_ACID2:
2731 tp := gAcid2[ID];
2732 PANEL_STEP:
2733 tp := gSteps[ID];
2734 else
2735 Exit;
2736 end;
2738 tp.NextTexture(AnimLoop);
2739 if g_Game_IsServer and g_Game_IsNet then
2740 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2741 end;
2743 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2744 begin
2745 if gLifts[ID].LiftType = t then
2746 Exit;
2748 with gLifts[ID] do
2749 begin
2750 LiftType := t;
2752 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2753 //TODO: make separate lift tags, and change tag here
2755 if LiftType = 0 then
2756 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2757 else if LiftType = 1 then
2758 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2759 else if LiftType = 2 then
2760 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2761 else if LiftType = 3 then
2762 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2764 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2765 end;
2766 end;
2768 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2769 var
2770 a: Integer;
2771 PointsArray: Array of TRespawnPoint;
2772 begin
2773 Result := False;
2774 SetLength(PointsArray, 0);
2776 if RespawnPoints = nil then
2777 Exit;
2779 for a := 0 to High(RespawnPoints) do
2780 if RespawnPoints[a].PointType = PointType then
2781 begin
2782 SetLength(PointsArray, Length(PointsArray)+1);
2783 PointsArray[High(PointsArray)] := RespawnPoints[a];
2784 end;
2786 if PointsArray = nil then
2787 Exit;
2789 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2790 Result := True;
2791 end;
2793 function g_Map_GetPointCount(PointType: Byte): Word;
2794 var
2795 a: Integer;
2796 begin
2797 Result := 0;
2799 if RespawnPoints = nil then
2800 Exit;
2802 for a := 0 to High(RespawnPoints) do
2803 if RespawnPoints[a].PointType = PointType then
2804 Result := Result + 1;
2805 end;
2807 function g_Map_HaveFlagPoints(): Boolean;
2808 begin
2809 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2810 end;
2812 procedure g_Map_ResetFlag(Flag: Byte);
2813 begin
2814 with gFlags[Flag] do
2815 begin
2816 Obj.X := -1000;
2817 Obj.Y := -1000;
2818 Obj.Vel.X := 0;
2819 Obj.Vel.Y := 0;
2820 Direction := D_LEFT;
2821 State := FLAG_STATE_NONE;
2822 if FlagPoints[Flag] <> nil then
2823 begin
2824 Obj.X := FlagPoints[Flag]^.X;
2825 Obj.Y := FlagPoints[Flag]^.Y;
2826 Direction := FlagPoints[Flag]^.Direction;
2827 State := FLAG_STATE_NORMAL;
2828 end;
2829 Count := -1;
2830 end;
2831 end;
2833 procedure g_Map_DrawFlags();
2834 var
2835 i, dx: Integer;
2836 Mirror: TMirrorType;
2837 begin
2838 if gGameSettings.GameMode <> GM_CTF then
2839 Exit;
2841 for i := FLAG_RED to FLAG_BLUE do
2842 with gFlags[i] do
2843 if State <> FLAG_STATE_CAPTURED then
2844 begin
2845 if State = FLAG_STATE_NONE then
2846 continue;
2848 if Direction = D_LEFT then
2849 begin
2850 Mirror := M_HORIZONTAL;
2851 dx := -1;
2852 end
2853 else
2854 begin
2855 Mirror := M_NONE;
2856 dx := 1;
2857 end;
2859 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2861 if g_debug_Frames then
2862 begin
2863 e_DrawQuad(Obj.X+Obj.Rect.X,
2864 Obj.Y+Obj.Rect.Y,
2865 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2866 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2867 0, 255, 0);
2868 end;
2869 end;
2870 end;
2872 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2873 var
2874 dw: DWORD;
2875 b: Byte;
2876 str: String;
2877 boo: Boolean;
2879 procedure SavePanelArray(var panels: TPanelArray);
2880 var
2881 PAMem: TBinMemoryWriter;
2882 i: Integer;
2883 begin
2884 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2885 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2887 i := 0;
2888 while i < Length(panels) do
2889 begin
2890 if panels[i].SaveIt then
2891 begin
2892 // ID ïàíåëè:
2893 PAMem.WriteInt(i);
2894 // Ñîõðàíÿåì ïàíåëü:
2895 panels[i].SaveState(PAMem);
2896 end;
2897 Inc(i);
2898 end;
2900 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2901 PAMem.SaveToMemory(Mem);
2902 PAMem.Free();
2903 end;
2905 procedure SaveFlag(flag: PFlag);
2906 begin
2907 // Ñèãíàòóðà ôëàãà:
2908 dw := FLAG_SIGNATURE; // 'FLAG'
2909 Mem.WriteDWORD(dw);
2910 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2911 Mem.WriteByte(flag^.RespawnType);
2912 // Ñîñòîÿíèå ôëàãà:
2913 Mem.WriteByte(flag^.State);
2914 // Íàïðàâëåíèå ôëàãà:
2915 if flag^.Direction = D_LEFT then
2916 b := 1
2917 else // D_RIGHT
2918 b := 2;
2919 Mem.WriteByte(b);
2920 // Îáúåêò ôëàãà:
2921 Obj_SaveState(@flag^.Obj, Mem);
2922 end;
2924 begin
2925 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2927 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2928 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2929 SavePanelArray(gWalls);
2930 // Ñîõðàíÿåì ïàíåëè ôîíà:
2931 SavePanelArray(gRenderBackgrounds);
2932 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2933 SavePanelArray(gRenderForegrounds);
2934 // Ñîõðàíÿåì ïàíåëè âîäû:
2935 SavePanelArray(gWater);
2936 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2937 SavePanelArray(gAcid1);
2938 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2939 SavePanelArray(gAcid2);
2940 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2941 SavePanelArray(gSteps);
2942 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2943 SavePanelArray(gLifts);
2944 ///// /////
2946 ///// Ñîõðàíÿåì ìóçûêó: /////
2947 // Ñèãíàòóðà ìóçûêè:
2948 dw := MUSIC_SIGNATURE; // 'MUSI'
2949 Mem.WriteDWORD(dw);
2950 // Íàçâàíèå ìóçûêè:
2951 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2952 if gMusic.NoMusic then
2953 str := ''
2954 else
2955 str := gMusic.Name;
2956 Mem.WriteString(str, 64);
2957 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2958 dw := gMusic.GetPosition();
2959 Mem.WriteDWORD(dw);
2960 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2961 boo := gMusic.SpecPause;
2962 Mem.WriteBoolean(boo);
2963 ///// /////
2965 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2966 Mem.WriteInt(gTotalMonsters);
2967 ///// /////
2969 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2970 if gGameSettings.GameMode = GM_CTF then
2971 begin
2972 // Ôëàã Êðàñíîé êîìàíäû:
2973 SaveFlag(@gFlags[FLAG_RED]);
2974 // Ôëàã Ñèíåé êîìàíäû:
2975 SaveFlag(@gFlags[FLAG_BLUE]);
2976 end;
2977 ///// /////
2979 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2980 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2981 begin
2982 // Î÷êè Êðàñíîé êîìàíäû:
2983 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2984 // Î÷êè Ñèíåé êîìàíäû:
2985 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2986 end;
2987 ///// /////
2988 end;
2990 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2991 var
2992 dw: DWORD;
2993 b: Byte;
2994 str: String;
2995 boo: Boolean;
2997 procedure LoadPanelArray(var panels: TPanelArray);
2998 var
2999 PAMem: TBinMemoryReader;
3000 i, id: Integer;
3001 begin
3002 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
3003 PAMem := TBinMemoryReader.Create();
3004 PAMem.LoadFromMemory(Mem);
3006 for i := 0 to Length(panels)-1 do
3007 begin
3008 if panels[i].SaveIt then
3009 begin
3010 // ID ïàíåëè:
3011 PAMem.ReadInt(id);
3012 if id <> i then
3013 begin
3014 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
3015 end;
3016 // Çàãðóæàåì ïàíåëü:
3017 panels[i].LoadState(PAMem);
3018 if (panels[i].arrIdx <> i) then raise Exception.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel arrIdx');
3019 if (panels[i].proxyId >= 0) then mapGrid.proxyEnabled[panels[i].proxyId] := panels[i].Enabled;
3020 end;
3021 end;
3023 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
3024 PAMem.Free();
3025 end;
3027 procedure LoadFlag(flag: PFlag);
3028 begin
3029 // Ñèãíàòóðà ôëàãà:
3030 Mem.ReadDWORD(dw);
3031 if dw <> FLAG_SIGNATURE then // 'FLAG'
3032 begin
3033 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
3034 end;
3035 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
3036 Mem.ReadByte(flag^.RespawnType);
3037 // Ñîñòîÿíèå ôëàãà:
3038 Mem.ReadByte(flag^.State);
3039 // Íàïðàâëåíèå ôëàãà:
3040 Mem.ReadByte(b);
3041 if b = 1 then
3042 flag^.Direction := D_LEFT
3043 else // b = 2
3044 flag^.Direction := D_RIGHT;
3045 // Îáúåêò ôëàãà:
3046 Obj_LoadState(@flag^.Obj, Mem);
3047 end;
3049 begin
3050 if Mem = nil then
3051 Exit;
3053 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
3054 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
3055 LoadPanelArray(gWalls);
3056 // Çàãðóæàåì ïàíåëè ôîíà:
3057 LoadPanelArray(gRenderBackgrounds);
3058 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
3059 LoadPanelArray(gRenderForegrounds);
3060 // Çàãðóæàåì ïàíåëè âîäû:
3061 LoadPanelArray(gWater);
3062 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
3063 LoadPanelArray(gAcid1);
3064 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
3065 LoadPanelArray(gAcid2);
3066 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
3067 LoadPanelArray(gSteps);
3068 // Çàãðóæàåì ïàíåëè ëèôòîâ:
3069 LoadPanelArray(gLifts);
3070 ///// /////
3072 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
3073 g_GFX_Init();
3074 //mapCreateGrid();
3076 ///// Çàãðóæàåì ìóçûêó: /////
3077 // Ñèãíàòóðà ìóçûêè:
3078 Mem.ReadDWORD(dw);
3079 if dw <> MUSIC_SIGNATURE then // 'MUSI'
3080 begin
3081 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
3082 end;
3083 // Íàçâàíèå ìóçûêè:
3084 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
3085 Mem.ReadString(str);
3086 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
3087 Mem.ReadDWORD(dw);
3088 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
3089 Mem.ReadBoolean(boo);
3090 // Çàïóñêàåì ýòó ìóçûêó:
3091 gMusic.SetByName(str);
3092 gMusic.SpecPause := boo;
3093 gMusic.Play();
3094 gMusic.Pause(True);
3095 gMusic.SetPosition(dw);
3096 ///// /////
3098 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
3099 Mem.ReadInt(gTotalMonsters);
3100 ///// /////
3102 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
3103 if gGameSettings.GameMode = GM_CTF then
3104 begin
3105 // Ôëàã Êðàñíîé êîìàíäû:
3106 LoadFlag(@gFlags[FLAG_RED]);
3107 // Ôëàã Ñèíåé êîìàíäû:
3108 LoadFlag(@gFlags[FLAG_BLUE]);
3109 end;
3110 ///// /////
3112 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3113 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3114 begin
3115 // Î÷êè Êðàñíîé êîìàíäû:
3116 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
3117 // Î÷êè Ñèíåé êîìàíäû:
3118 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
3119 end;
3120 ///// /////
3121 end;
3123 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
3124 var
3125 Arr: TPanelArray;
3126 begin
3127 Result := nil;
3128 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
3129 Arr := PanelByID[PanelID].PWhere^;
3130 PanelArrayID := PanelByID[PanelID].PArrID;
3131 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
3132 end;
3135 // trace liquid, stepping by `dx` and `dy`
3136 // return last seen liquid coords, and `false` if we're started outside of the liquid
3137 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
3138 const
3139 MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
3140 begin
3141 topx := x;
3142 topy := y;
3143 // started outside of the liquid?
3144 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end;
3145 if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check
3146 result := true;
3147 while true do
3148 begin
3149 Inc(x, dx);
3150 Inc(y, dy);
3151 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit
3152 topx := x;
3153 topy := y;
3154 end;
3155 end;
3158 end.