DEADSOFTWARE

slightly better memory locality
[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 unit g_map;
19 interface
21 uses
22 e_graphics, g_basic, MAPSTRUCT, g_textures, Classes,
23 g_phys, wadreader, BinEditor, g_panel, g_grid, z_aabbtree, md5, binheap, xprofiler;
25 type
26 TMapInfo = record
27 Map: String;
28 Name: String;
29 Description: String;
30 Author: String;
31 MusicName: String;
32 SkyName: String;
33 Height: Word;
34 Width: Word;
35 end;
37 PRespawnPoint = ^TRespawnPoint;
38 TRespawnPoint = record
39 X, Y: Integer;
40 Direction: TDirection;
41 PointType: Byte;
42 end;
44 PFlagPoint = ^TFlagPoint;
45 TFlagPoint = TRespawnPoint;
47 PFlag = ^TFlag;
48 TFlag = record
49 Obj: TObj;
50 RespawnType: Byte;
51 State: Byte;
52 Count: Integer;
53 CaptureTime: LongWord;
54 Animation: TAnimation;
55 Direction: TDirection;
56 end;
58 function g_Map_Load(Res: String): Boolean;
59 function g_Map_GetMapInfo(Res: String): TMapInfo;
60 function g_Map_GetMapsList(WADName: String): SArray;
61 function g_Map_Exist(Res: String): Boolean;
62 procedure g_Map_Free();
63 procedure g_Map_Update();
65 procedure g_Map_DrawPanels (PanelType: Word); // unaccelerated
66 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
68 procedure g_Map_DrawBack(dx, dy: Integer);
69 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
70 PanelType: Word; b1x3: Boolean): Boolean;
71 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
72 procedure g_Map_EnableWall(ID: DWORD);
73 procedure g_Map_DisableWall(ID: DWORD);
74 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
75 procedure g_Map_SetLift(ID: DWORD; t: Integer);
76 procedure g_Map_ReAdd_DieTriggers();
77 function g_Map_IsSpecialTexture(Texture: String): Boolean;
79 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
80 function g_Map_GetPointCount(PointType: Byte): Word;
82 function g_Map_HaveFlagPoints(): Boolean;
84 procedure g_Map_ResetFlag(Flag: Byte);
85 procedure g_Map_DrawFlags();
87 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
89 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
90 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
92 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
94 procedure g_Map_ProfilersBegin ();
95 procedure g_Map_ProfilersEnd ();
97 const
98 RESPAWNPOINT_PLAYER1 = 1;
99 RESPAWNPOINT_PLAYER2 = 2;
100 RESPAWNPOINT_DM = 3;
101 RESPAWNPOINT_RED = 4;
102 RESPAWNPOINT_BLUE = 5;
104 FLAG_NONE = 0;
105 FLAG_RED = 1;
106 FLAG_BLUE = 2;
107 FLAG_DOM = 3;
109 FLAG_STATE_NONE = 0;
110 FLAG_STATE_NORMAL = 1;
111 FLAG_STATE_DROPPED = 2;
112 FLAG_STATE_CAPTURED = 3;
113 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
114 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
116 FLAG_TIME = 720; // 20 seconds
118 SKY_STRETCH: Single = 1.5;
120 const
121 GridTagInvalid = 0;
123 (* draw order:
124 PANEL_BACK
125 PANEL_STEP
126 PANEL_WALL
127 PANEL_CLOSEDOOR
128 PANEL_ACID1
129 PANEL_ACID2
130 PANEL_WATER
131 PANEL_FORE
132 *)
133 // sorted by draw priority
134 GridTagBack = 1 shl 0;
135 GridTagStep = 1 shl 1;
136 GridTagWall = 1 shl 2;
137 GridTagDoor = 1 shl 3;
138 GridTagAcid1 = 1 shl 4;
139 GridTagAcid2 = 1 shl 5;
140 GridTagWater = 1 shl 6;
141 GridTagFore = 1 shl 7;
142 // the following are invisible
143 GridTagLift = 1 shl 8;
144 GridTagBlockMon = 1 shl 9;
147 var
148 gWalls: TPanelArray;
149 gRenderBackgrounds: TPanelArray;
150 gRenderForegrounds: TPanelArray;
151 gWater, gAcid1, gAcid2: TPanelArray;
152 gSteps: TPanelArray;
153 gLifts: TPanelArray;
154 gBlockMon: TPanelArray;
155 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
156 //gDOMFlags: array of TFlag;
157 gMapInfo: TMapInfo;
158 gBackSize: TPoint;
159 gDoorMap: array of array of DWORD;
160 gLiftMap: array of array of DWORD;
161 gWADHash: TMD5Digest;
162 BackID: DWORD = DWORD(-1);
163 gExternalResources: TStringList;
165 gdbg_map_use_accel_render: Boolean = true;
166 gdbg_map_use_accel_coldet: Boolean = true;
167 gdbg_map_use_tree_draw: Boolean = false;
168 gdbg_map_use_tree_coldet: Boolean = false;
169 gdbg_map_dump_coldet_tree_queries: Boolean = false;
170 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
171 gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
173 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
175 implementation
177 uses
178 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
179 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
180 g_options, MAPREADER, g_triggers, g_player, MAPDEF,
181 Math, g_monsters, g_saveload, g_language, g_netmsg,
182 utils, sfs,
183 ImagingTypes, Imaging, ImagingUtility,
184 ImagingGif, ImagingNetworkGraphics;
186 const
187 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
188 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
189 FLAG_SIGNATURE = $47414C46; // 'FLAG'
193 function panelTypeToTag (panelType: Word): Integer;
194 begin
195 case panelType of
196 PANEL_WALL: result := GridTagWall; // gWalls
197 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
198 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
199 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
200 PANEL_WATER: result := GridTagWater; // gWater
201 PANEL_ACID1: result := GridTagAcid1; // gAcid1
202 PANEL_ACID2: result := GridTagAcid2; // gAcid2
203 PANEL_STEP: result := GridTagStep; // gSteps
204 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
205 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
206 else result := GridTagInvalid;
207 end;
208 end;
211 function dplLess (a, b: TObject): Boolean;
212 var
213 pa, pb: TPanel;
214 begin
215 //result := ((a as TPanel).ArrIdx < (b as TPanel).ArrIdx);
216 pa := TPanel(a);
217 pb := TPanel(b);
218 if (pa.tag < pb.tag) then begin result := true; exit; end;
219 if (pa.tag > pb.tag) then begin result := false; exit; end;
220 result := (pa.ArrIdx < pb.ArrIdx);
221 end;
223 procedure dplClear ();
224 begin
225 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
226 end;
229 type
230 TPanelID = record
231 PWhere: ^TPanelArray;
232 PArrID: Integer;
233 end;
235 type
236 TDynAABBTreeMap = class(TDynAABBTree)
237 function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean; override;
238 end;
240 function TDynAABBTreeMap.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean;
241 var
242 pan: TPanel;
243 begin
244 result := false;
245 if (flesh = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end;
246 //pan := (flesh as TPanel);
247 pan := TPanel(flesh);
248 aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width, pan.Y+pan.Height);
249 if (pan.Width < 1) or (pan.Height < 1) then exit;
250 //if (pan.Width = 1) then aabb.maxX += 1;
251 //if (pan.Height = 1) then aabb.maxY += 1;
252 //if (pan.Width < 3) or (pan.Height < 3) then exit;
253 //aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width-2, pan.Y+pan.Height-2);
254 if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
255 result := aabb.valid;
256 end;
258 var
259 PanelById: array of TPanelID;
260 Textures: TLevelTextureArray;
261 RespawnPoints: Array of TRespawnPoint;
262 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
263 //DOMFlagPoints: Array of TFlagPoint;
264 gMapGrid: TBodyGrid = nil;
265 mapTree: TDynAABBTree = nil;
268 procedure g_Map_ProfilersBegin ();
269 begin
270 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('MAP COLLISION', g_profile_history_size);
271 profMapCollision.mainBegin(g_profile_collision);
272 // create sections
273 if g_profile_collision then
274 begin
275 profMapCollision.sectionBegin('wall coldet');
276 profMapCollision.sectionEnd();
277 profMapCollision.sectionBegin('liquid coldet');
278 profMapCollision.sectionEnd();
279 end;
280 end;
282 procedure g_Map_ProfilersEnd ();
283 begin
284 if (profMapCollision <> nil) then profMapCollision.mainEnd();
285 end;
288 function g_Map_IsSpecialTexture(Texture: String): Boolean;
289 begin
290 Result := (Texture = TEXTURE_NAME_WATER) or
291 (Texture = TEXTURE_NAME_ACID1) or
292 (Texture = TEXTURE_NAME_ACID2);
293 end;
295 procedure CreateDoorMap();
296 var
297 PanelArray: Array of record
298 X, Y: Integer;
299 Width, Height: Word;
300 Active: Boolean;
301 PanelID: DWORD;
302 end;
303 a, b, c, m, i, len: Integer;
304 ok: Boolean;
305 begin
306 if gWalls = nil then
307 Exit;
309 i := 0;
310 len := 128;
311 SetLength(PanelArray, len);
313 for a := 0 to High(gWalls) do
314 if gWalls[a].Door then
315 begin
316 PanelArray[i].X := gWalls[a].X;
317 PanelArray[i].Y := gWalls[a].Y;
318 PanelArray[i].Width := gWalls[a].Width;
319 PanelArray[i].Height := gWalls[a].Height;
320 PanelArray[i].Active := True;
321 PanelArray[i].PanelID := a;
323 i := i + 1;
324 if i = len then
325 begin
326 len := len + 128;
327 SetLength(PanelArray, len);
328 end;
329 end;
331 // Íåò äâåðåé:
332 if i = 0 then
333 begin
334 PanelArray := nil;
335 Exit;
336 end;
338 SetLength(gDoorMap, 0);
340 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
342 for a := 0 to i-1 do
343 if PanelArray[a].Active then
344 begin
345 PanelArray[a].Active := False;
346 m := Length(gDoorMap);
347 SetLength(gDoorMap, m+1);
348 SetLength(gDoorMap[m], 1);
349 gDoorMap[m, 0] := PanelArray[a].PanelID;
350 ok := True;
352 while ok do
353 begin
354 ok := False;
356 for b := 0 to i-1 do
357 if PanelArray[b].Active then
358 for c := 0 to High(gDoorMap[m]) do
359 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
360 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
361 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
362 PanelArray[b].Width, PanelArray[b].Height,
363 gWalls[gDoorMap[m, c]].X,
364 gWalls[gDoorMap[m, c]].Y,
365 gWalls[gDoorMap[m, c]].Width,
366 gWalls[gDoorMap[m, c]].Height) then
367 begin
368 PanelArray[b].Active := False;
369 SetLength(gDoorMap[m],
370 Length(gDoorMap[m])+1);
371 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
372 ok := True;
373 Break;
374 end;
375 end;
377 g_Game_StepLoading();
378 end;
380 PanelArray := nil;
381 end;
383 procedure CreateLiftMap();
384 var
385 PanelArray: Array of record
386 X, Y: Integer;
387 Width, Height: Word;
388 Active: Boolean;
389 end;
390 a, b, c, len, i, j: Integer;
391 ok: Boolean;
392 begin
393 if gLifts = nil then
394 Exit;
396 len := Length(gLifts);
397 SetLength(PanelArray, len);
399 for a := 0 to len-1 do
400 begin
401 PanelArray[a].X := gLifts[a].X;
402 PanelArray[a].Y := gLifts[a].Y;
403 PanelArray[a].Width := gLifts[a].Width;
404 PanelArray[a].Height := gLifts[a].Height;
405 PanelArray[a].Active := True;
406 end;
408 SetLength(gLiftMap, len);
409 i := 0;
411 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
413 for a := 0 to len-1 do
414 if PanelArray[a].Active then
415 begin
416 PanelArray[a].Active := False;
417 SetLength(gLiftMap[i], 32);
418 j := 0;
419 gLiftMap[i, j] := a;
420 ok := True;
422 while ok do
423 begin
424 ok := False;
425 for b := 0 to len-1 do
426 if PanelArray[b].Active then
427 for c := 0 to j do
428 if g_CollideAround(PanelArray[b].X,
429 PanelArray[b].Y,
430 PanelArray[b].Width,
431 PanelArray[b].Height,
432 PanelArray[gLiftMap[i, c]].X,
433 PanelArray[gLiftMap[i, c]].Y,
434 PanelArray[gLiftMap[i, c]].Width,
435 PanelArray[gLiftMap[i, c]].Height) then
436 begin
437 PanelArray[b].Active := False;
438 j := j+1;
439 if j > High(gLiftMap[i]) then
440 SetLength(gLiftMap[i],
441 Length(gLiftMap[i])+32);
443 gLiftMap[i, j] := b;
444 ok := True;
446 Break;
447 end;
448 end;
450 SetLength(gLiftMap[i], j+1);
451 i := i+1;
453 g_Game_StepLoading();
454 end;
456 SetLength(gLiftMap, i);
458 PanelArray := nil;
459 end;
461 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
462 CurTex: Integer; sav: Boolean): Integer;
463 var
464 len: Integer;
465 panels: ^TPanelArray;
466 begin
467 Result := -1;
469 case PanelRec.PanelType of
470 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
471 panels := @gWalls;
472 PANEL_BACK:
473 panels := @gRenderBackgrounds;
474 PANEL_FORE:
475 panels := @gRenderForegrounds;
476 PANEL_WATER:
477 panels := @gWater;
478 PANEL_ACID1:
479 panels := @gAcid1;
480 PANEL_ACID2:
481 panels := @gAcid2;
482 PANEL_STEP:
483 panels := @gSteps;
484 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
485 panels := @gLifts;
486 PANEL_BLOCKMON:
487 panels := @gBlockMon;
488 else
489 Exit;
490 end;
492 len := Length(panels^);
493 SetLength(panels^, len + 1);
495 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
496 panels^[len].ArrIdx := len;
497 panels^[len].tag := panelTypeToTag(PanelRec.PanelType);
498 if sav then
499 panels^[len].SaveIt := True;
501 Result := len;
503 len := Length(PanelByID);
504 SetLength(PanelByID, len + 1);
505 PanelByID[len].PWhere := panels;
506 PanelByID[len].PArrID := Result;
507 end;
509 function CreateNullTexture(RecName: String): Integer;
510 begin
511 SetLength(Textures, Length(Textures)+1);
512 result := High(Textures);
514 with Textures[High(Textures)] do
515 begin
516 TextureName := RecName;
517 Width := 1;
518 Height := 1;
519 Anim := False;
520 TextureID := TEXTURE_NONE;
521 end;
522 end;
524 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
525 var
526 WAD: TWADFile;
527 TextureData: Pointer;
528 WADName, txname: String;
529 a, ResLength: Integer;
530 begin
531 Result := -1;
533 if Textures <> nil then
534 for a := 0 to High(Textures) do
535 if Textures[a].TextureName = RecName then
536 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
537 Result := a;
538 Exit;
539 end;
541 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
542 if (RecName = TEXTURE_NAME_WATER) or
543 (RecName = TEXTURE_NAME_ACID1) or
544 (RecName = TEXTURE_NAME_ACID2) then
545 begin
546 SetLength(Textures, Length(Textures)+1);
548 with Textures[High(Textures)] do
549 begin
550 TextureName := RecName;
552 if TextureName = TEXTURE_NAME_WATER then
553 TextureID := TEXTURE_SPECIAL_WATER
554 else
555 if TextureName = TEXTURE_NAME_ACID1 then
556 TextureID := TEXTURE_SPECIAL_ACID1
557 else
558 if TextureName = TEXTURE_NAME_ACID2 then
559 TextureID := TEXTURE_SPECIAL_ACID2;
561 Anim := False;
562 end;
564 result := High(Textures);
565 Exit;
566 end;
568 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
569 WADName := g_ExtractWadName(RecName);
571 WAD := TWADFile.Create();
573 if WADName <> '' then
574 WADName := GameDir+'/wads/'+WADName
575 else
576 WADName := Map;
578 WAD.ReadFile(WADName);
580 txname := RecName;
582 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
583 begin
584 FreeMem(TextureData);
585 RecName := 'COMMON\ALIEN';
586 end;
589 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
590 begin
591 SetLength(Textures, Length(Textures)+1);
592 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
593 Exit;
594 e_GetTextureSize(Textures[High(Textures)].TextureID,
595 @Textures[High(Textures)].Width,
596 @Textures[High(Textures)].Height);
597 FreeMem(TextureData);
598 Textures[High(Textures)].TextureName := {RecName}txname;
599 Textures[High(Textures)].Anim := False;
601 result := High(Textures);
602 end
603 else // Íåò òàêîãî ðåóñðñà â WAD'å
604 begin
605 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
606 if log then
607 begin
608 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
609 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
610 end;
611 end;
613 WAD.Free();
614 end;
616 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
617 var
618 WAD: TWADFile;
619 TextureWAD: PChar = nil;
620 TextData: Pointer = nil;
621 TextureData: Pointer = nil;
622 cfg: TConfig = nil;
623 WADName: String;
624 ResLength: Integer;
625 TextureResource: String;
626 _width, _height, _framecount, _speed: Integer;
627 _backanimation: Boolean;
628 //imgfmt: string;
629 ia: TDynImageDataArray = nil;
630 f, c, frdelay, frloop: Integer;
631 begin
632 result := -1;
634 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
636 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
637 WADName := g_ExtractWadName(RecName);
639 WAD := TWADFile.Create();
640 try
641 if WADName <> '' then
642 WADName := GameDir+'/wads/'+WADName
643 else
644 WADName := Map;
646 WAD.ReadFile(WADName);
648 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
649 begin
650 if log then
651 begin
652 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
653 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
654 end;
655 exit;
656 end;
658 {TEST
659 if WADName = Map then
660 begin
661 //FreeMem(TextureWAD);
662 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
663 end;
666 WAD.FreeWAD();
668 if ResLength < 6 then
669 begin
670 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
671 exit;
672 end;
674 // ýòî ïòèöà? ýòî ñàìîë¸ò?
675 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
676 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
677 begin
678 // íåò, ýòî ñóïåðìåí!
679 if not WAD.ReadMemory(TextureWAD, ResLength) then
680 begin
681 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
682 exit;
683 end;
685 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
686 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
687 begin
688 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
689 exit;
690 end;
692 cfg := TConfig.CreateMem(TextData, ResLength);
694 TextureResource := cfg.ReadStr('', 'resource', '');
695 if TextureResource = '' then
696 begin
697 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
698 exit;
699 end;
701 _width := cfg.ReadInt('', 'framewidth', 0);
702 _height := cfg.ReadInt('', 'frameheight', 0);
703 _framecount := cfg.ReadInt('', 'framecount', 0);
704 _speed := cfg.ReadInt('', 'waitcount', 0);
705 _backanimation := cfg.ReadBool('', 'backanimation', False);
707 cfg.Free();
708 cfg := nil;
710 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
711 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
712 begin
713 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
714 exit;
715 end;
717 WAD.Free();
718 WAD := nil;
720 SetLength(Textures, Length(Textures)+1);
721 with Textures[High(Textures)] do
722 begin
723 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
724 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
725 begin
726 TextureName := RecName;
727 Width := _width;
728 Height := _height;
729 Anim := True;
730 FramesCount := _framecount;
731 Speed := _speed;
732 result := High(Textures);
733 end
734 else
735 begin
736 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
737 end;
738 end;
739 end
740 else
741 begin
742 // try animated image
744 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
745 if length(imgfmt) = 0 then
746 begin
747 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
748 exit;
749 end;
751 GlobalMetadata.ClearMetaItems();
752 GlobalMetadata.ClearMetaItemsForSaving();
753 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
754 begin
755 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
756 exit;
757 end;
758 if length(ia) = 0 then
759 begin
760 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
761 exit;
762 end;
764 WAD.Free();
765 WAD := nil;
767 _width := ia[0].width;
768 _height := ia[0].height;
769 _framecount := length(ia);
770 _speed := 1;
771 _backanimation := false;
772 frdelay := -1;
773 frloop := -666;
774 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
775 begin
776 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
777 try
778 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
779 frdelay := f;
780 if f < 0 then f := 0;
781 // rounding ;-)
782 c := f mod 28;
783 if c < 13 then c := 0 else c := 1;
784 f := (f div 28)+c;
785 if f < 1 then f := 1 else if f > 255 then f := 255;
786 _speed := f;
787 except
788 end;
789 end;
790 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
791 begin
792 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
793 try
794 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
795 frloop := f;
796 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
797 except
798 end;
799 end;
800 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
801 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
802 f := ord(_backanimation);
803 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);
805 SetLength(Textures, Length(Textures)+1);
806 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
807 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
808 begin
809 Textures[High(Textures)].TextureName := RecName;
810 Textures[High(Textures)].Width := _width;
811 Textures[High(Textures)].Height := _height;
812 Textures[High(Textures)].Anim := True;
813 Textures[High(Textures)].FramesCount := length(ia);
814 Textures[High(Textures)].Speed := _speed;
815 result := High(Textures);
816 //writeln(' CREATED!');
817 end
818 else
819 begin
820 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
821 end;
822 end;
823 finally
824 for f := 0 to High(ia) do FreeImage(ia[f]);
825 WAD.Free();
826 cfg.Free();
827 if TextureWAD <> nil then FreeMem(TextureWAD);
828 if TextData <> nil then FreeMem(TextData);
829 if TextureData <> nil then FreeMem(TextureData);
830 end;
831 end;
833 procedure CreateItem(Item: TItemRec_1);
834 begin
835 if g_Game_IsClient then Exit;
837 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
838 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
839 Exit;
841 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
842 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
843 end;
845 procedure CreateArea(Area: TAreaRec_1);
846 var
847 a: Integer;
848 id: DWORD;
849 begin
850 case Area.AreaType of
851 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
852 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
853 begin
854 SetLength(RespawnPoints, Length(RespawnPoints)+1);
855 with RespawnPoints[High(RespawnPoints)] do
856 begin
857 X := Area.X;
858 Y := Area.Y;
859 Direction := TDirection(Area.Direction);
861 case Area.AreaType of
862 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
863 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
864 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
865 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
866 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
867 end;
868 end;
869 end;
871 AREA_REDFLAG, AREA_BLUEFLAG:
872 begin
873 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
875 if FlagPoints[a] <> nil then Exit;
877 New(FlagPoints[a]);
879 with FlagPoints[a]^ do
880 begin
881 X := Area.X-FLAGRECT.X;
882 Y := Area.Y-FLAGRECT.Y;
883 Direction := TDirection(Area.Direction);
884 end;
886 with gFlags[a] do
887 begin
888 case a of
889 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
890 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
891 end;
893 Animation := TAnimation.Create(id, True, 8);
894 Obj.Rect := FLAGRECT;
896 g_Map_ResetFlag(a);
897 end;
898 end;
900 AREA_DOMFLAG:
901 begin
902 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
903 with DOMFlagPoints[High(DOMFlagPoints)] do
904 begin
905 X := Area.X;
906 Y := Area.Y;
907 Direction := TDirection(Area.Direction);
908 end;
910 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
911 end;
912 end;
913 end;
915 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
916 var
917 _trigger: TTrigger;
918 begin
919 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
921 with _trigger do
922 begin
923 X := Trigger.X;
924 Y := Trigger.Y;
925 Width := Trigger.Width;
926 Height := Trigger.Height;
927 Enabled := ByteBool(Trigger.Enabled);
928 TexturePanel := Trigger.TexturePanel;
929 TexturePanelType := fTexturePanel1Type;
930 ShotPanelType := fTexturePanel2Type;
931 TriggerType := Trigger.TriggerType;
932 ActivateType := Trigger.ActivateType;
933 Keys := Trigger.Keys;
934 Data.Default := Trigger.DATA;
935 end;
937 g_Triggers_Create(_trigger);
938 end;
940 procedure CreateMonster(monster: TMonsterRec_1);
941 var
942 a, i: Integer;
943 begin
944 if g_Game_IsClient then Exit;
946 if (gGameSettings.GameType = GT_SINGLE)
947 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
948 begin
949 i := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y,
950 TDirection(monster.Direction));
952 if gTriggers <> nil then
953 for a := 0 to High(gTriggers) do
954 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
955 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
956 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
957 gMonsters[i].AddTrigger(a);
959 if monster.MonsterType <> MONSTER_BARREL then
960 Inc(gTotalMonsters);
961 end;
962 end;
964 procedure g_Map_ReAdd_DieTriggers();
965 var
966 i, a: Integer;
967 begin
968 if g_Game_IsClient then Exit;
970 for i := 0 to High(gMonsters) do
971 if gMonsters[i] <> nil then
972 begin
973 gMonsters[i].ClearTriggers();
975 for a := 0 to High(gTriggers) do
976 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
977 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
978 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
979 gMonsters[i].AddTrigger(a);
980 end;
981 end;
983 function extractWadName(resourceName: string): string;
984 var
985 posN: Integer;
986 begin
987 posN := Pos(':', resourceName);
988 if posN > 0 then
989 Result:= Copy(resourceName, 0, posN-1)
990 else
991 Result := '';
992 end;
994 procedure addResToExternalResList(res: string);
995 begin
996 res := extractWadName(res);
997 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
998 gExternalResources.Add(res);
999 end;
1001 procedure generateExternalResourcesList(mapReader: TMapReader_1);
1002 var
1003 textures: TTexturesRec1Array;
1004 mapHeader: TMapHeaderRec_1;
1005 i: integer;
1006 resFile: String = '';
1007 begin
1008 if gExternalResources = nil then
1009 gExternalResources := TStringList.Create;
1011 gExternalResources.Clear;
1012 textures := mapReader.GetTextures();
1013 for i := 0 to High(textures) do
1014 begin
1015 addResToExternalResList(resFile);
1016 end;
1018 textures := nil;
1020 mapHeader := mapReader.GetMapHeader;
1022 addResToExternalResList(mapHeader.MusicName);
1023 addResToExternalResList(mapHeader.SkyName);
1024 end;
1026 procedure mapCreateGrid ();
1027 var
1028 mapX0: Integer = $3fffffff;
1029 mapY0: Integer = $3fffffff;
1030 mapX1: Integer = -$3fffffff;
1031 mapY1: Integer = -$3fffffff;
1033 procedure calcBoundingBox (var panels: TPanelArray);
1034 var
1035 idx: Integer;
1036 pan: TPanel;
1037 begin
1038 for idx := 0 to High(panels) do
1039 begin
1040 pan := panels[idx];
1041 if not pan.visvalid then continue;
1042 if (pan.Width < 1) or (pan.Height < 1) then continue;
1043 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1044 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1045 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1046 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1047 end;
1048 end;
1050 procedure addPanelsToGrid (var panels: TPanelArray; tag: Integer);
1051 var
1052 idx: Integer;
1053 pan: TPanel;
1054 begin
1055 tag := panelTypeToTag(tag);
1056 for idx := High(panels) downto 0 do
1057 begin
1058 pan := panels[idx];
1059 pan.tag := tag;
1060 if not pan.visvalid then continue;
1061 gMapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, tag);
1062 mapTree.insertObject(pan, tag, true); // as static object
1063 end;
1064 end;
1066 begin
1067 gMapGrid.Free();
1068 gMapGrid := nil;
1069 mapTree.Free();
1070 mapTree := nil;
1072 calcBoundingBox(gWalls);
1073 calcBoundingBox(gRenderBackgrounds);
1074 calcBoundingBox(gRenderForegrounds);
1075 calcBoundingBox(gWater);
1076 calcBoundingBox(gAcid1);
1077 calcBoundingBox(gAcid2);
1078 calcBoundingBox(gSteps);
1079 calcBoundingBox(gLifts);
1080 calcBoundingBox(gBlockMon);
1082 e_WriteLog(Format('map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING);
1084 gMapGrid := TBodyGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
1085 mapTree := TDynAABBTreeMap.Create();
1087 addPanelsToGrid(gWalls, PANEL_WALL);
1088 addPanelsToGrid(gWalls, PANEL_CLOSEDOOR);
1089 addPanelsToGrid(gWalls, PANEL_OPENDOOR);
1090 addPanelsToGrid(gRenderBackgrounds, PANEL_BACK);
1091 addPanelsToGrid(gRenderForegrounds, PANEL_FORE);
1092 addPanelsToGrid(gWater, PANEL_WATER);
1093 addPanelsToGrid(gAcid1, PANEL_ACID1);
1094 addPanelsToGrid(gAcid2, PANEL_ACID2);
1095 addPanelsToGrid(gSteps, PANEL_STEP);
1096 addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here
1097 addPanelsToGrid(gBlockMon, PANEL_BLOCKMON);
1099 gMapGrid.dumpStats();
1100 e_WriteLog(Format('tree depth: %d; %d nodes used, %d nodes allocated', [mapTree.computeTreeHeight, mapTree.nodeCount, mapTree.nodeAlloced]), MSG_NOTIFY);
1101 mapTree.forEachLeaf(nil);
1102 end;
1104 function g_Map_Load(Res: String): Boolean;
1105 const
1106 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1107 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1108 var
1109 WAD: TWADFile;
1110 MapReader: TMapReader_1;
1111 Header: TMapHeaderRec_1;
1112 _textures: TTexturesRec1Array;
1113 _texnummap: array of Integer; // `_textures` -> `Textures`
1114 panels: TPanelsRec1Array;
1115 items: TItemsRec1Array;
1116 monsters: TMonsterRec1Array;
1117 areas: TAreasRec1Array;
1118 triggers: TTriggersRec1Array;
1119 a, b, c, k: Integer;
1120 PanelID: DWORD;
1121 AddTextures: TAddTextureArray;
1122 texture: TTextureRec_1;
1123 TriggersTable: Array of record
1124 TexturePanel: Integer;
1125 LiftPanel: Integer;
1126 DoorPanel: Integer;
1127 ShotPanel: Integer;
1128 end;
1129 FileName, mapResName, s, TexName: String;
1130 Data: Pointer;
1131 Len: Integer;
1132 ok, isAnim, trigRef: Boolean;
1133 CurTex, ntn: Integer;
1135 begin
1136 gMapGrid.Free();
1137 gMapGrid := nil;
1138 mapTree.Free();
1139 mapTree := nil;
1141 Result := False;
1142 gMapInfo.Map := Res;
1143 TriggersTable := nil;
1144 FillChar(texture, SizeOf(texture), 0);
1146 sfsGCDisable(); // temporary disable removing of temporary volumes
1147 try
1148 // Çàãðóçêà WAD:
1149 FileName := g_ExtractWadName(Res);
1150 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1151 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1153 WAD := TWADFile.Create();
1154 if not WAD.ReadFile(FileName) then
1155 begin
1156 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1157 WAD.Free();
1158 Exit;
1159 end;
1160 //k8: why loader ignores path here?
1161 mapResName := g_ExtractFileName(Res);
1162 if not WAD.GetMapResource(mapResName, Data, Len) then
1163 begin
1164 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1165 WAD.Free();
1166 Exit;
1167 end;
1169 WAD.Free();
1171 // Çàãðóçêà êàðòû:
1172 e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
1173 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1174 MapReader := TMapReader_1.Create();
1176 if not MapReader.LoadMap(Data) then
1177 begin
1178 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1179 FreeMem(Data);
1180 MapReader.Free();
1181 Exit;
1182 end;
1184 FreeMem(Data);
1185 generateExternalResourcesList(MapReader);
1186 // Çàãðóçêà òåêñòóð:
1187 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1188 _textures := MapReader.GetTextures();
1189 _texnummap := nil;
1191 // Äîáàâëåíèå òåêñòóð â Textures[]:
1192 if _textures <> nil then
1193 begin
1194 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1195 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
1196 SetLength(_texnummap, length(_textures));
1198 for a := 0 to High(_textures) do
1199 begin
1200 SetLength(s, 64);
1201 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
1202 for b := 1 to Length(s) do
1203 if s[b] = #0 then
1204 begin
1205 SetLength(s, b-1);
1206 Break;
1207 end;
1208 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1209 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1210 // Àíèìèðîâàííàÿ òåêñòóðà:
1211 if ByteBool(_textures[a].Anim) then
1212 begin
1213 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1214 if ntn < 0 then
1215 begin
1216 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1217 ntn := CreateNullTexture(_textures[a].Resource);
1218 end;
1219 end
1220 else // Îáû÷íàÿ òåêñòóðà:
1221 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1222 if ntn < 0 then
1223 begin
1224 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1225 ntn := CreateNullTexture(_textures[a].Resource);
1226 end;
1228 _texnummap[a] := ntn; // fix texture number
1229 g_Game_StepLoading();
1230 end;
1231 end;
1233 // Çàãðóçêà òðèããåðîâ:
1234 gTriggerClientID := 0;
1235 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1236 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1237 triggers := MapReader.GetTriggers();
1239 // Çàãðóçêà ïàíåëåé:
1240 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1241 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1242 panels := MapReader.GetPanels();
1244 // check texture numbers for panels
1245 for a := 0 to High(panels) do
1246 begin
1247 if panels[a].TextureNum > High(_textures) then
1248 begin
1249 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1250 result := false;
1251 exit;
1252 end;
1253 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1254 end;
1256 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1257 if triggers <> nil then
1258 begin
1259 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1260 SetLength(TriggersTable, Length(triggers));
1261 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1263 for a := 0 to High(TriggersTable) do
1264 begin
1265 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1266 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1267 // Ëèôòû:
1268 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1269 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1270 else
1271 TriggersTable[a].LiftPanel := -1;
1272 // Äâåðè:
1273 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1274 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1275 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1276 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1277 else
1278 TriggersTable[a].DoorPanel := -1;
1279 // Òóðåëü:
1280 if triggers[a].TriggerType = TRIGGER_SHOT then
1281 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1282 else
1283 TriggersTable[a].ShotPanel := -1;
1285 g_Game_StepLoading();
1286 end;
1287 end;
1289 // Ñîçäàåì ïàíåëè:
1290 if panels <> nil then
1291 begin
1292 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1293 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1295 for a := 0 to High(panels) do
1296 begin
1297 SetLength(AddTextures, 0);
1298 trigRef := False;
1299 CurTex := -1;
1300 if _textures <> nil then
1301 begin
1302 texture := _textures[panels[a].TextureNum];
1303 ok := True;
1304 end
1305 else
1306 ok := False;
1308 if ok then
1309 begin
1310 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1311 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1312 ok := False;
1313 if (TriggersTable <> nil) and (_textures <> nil) then
1314 for b := 0 to High(TriggersTable) do
1315 if (TriggersTable[b].TexturePanel = a)
1316 or (TriggersTable[b].ShotPanel = a) then
1317 begin
1318 trigRef := True;
1319 ok := True;
1320 Break;
1321 end;
1322 end;
1324 if ok then
1325 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1326 SetLength(s, 64);
1327 CopyMemory(@s[1], @texture.Resource[0], 64);
1328 // Èçìåðÿåì äëèíó:
1329 Len := Length(s);
1330 for c := Len downto 1 do
1331 if s[c] <> #0 then
1332 begin
1333 Len := c;
1334 Break;
1335 end;
1336 SetLength(s, Len);
1338 // Ñïåö-òåêñòóðû çàïðåùåíû:
1339 if g_Map_IsSpecialTexture(s) then
1340 ok := False
1341 else
1342 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1343 ok := g_Texture_NumNameFindStart(s);
1345 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1346 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1347 if ok then
1348 begin
1349 k := NNF_NAME_BEFORE;
1350 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1351 while ok or (k = NNF_NAME_BEFORE) or
1352 (k = NNF_NAME_EQUALS) do
1353 begin
1354 k := g_Texture_NumNameFindNext(TexName);
1356 if (k = NNF_NAME_BEFORE) or
1357 (k = NNF_NAME_AFTER) then
1358 begin
1359 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1360 if ByteBool(texture.Anim) then
1361 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1362 isAnim := True;
1363 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1364 if not ok then
1365 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1366 isAnim := False;
1367 ok := CreateTexture(TexName, FileName, False) >= 0;
1368 end;
1369 end
1370 else
1371 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1372 isAnim := False;
1373 ok := CreateTexture(TexName, FileName, False) >= 0;
1374 if not ok then
1375 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1376 isAnim := True;
1377 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1378 end;
1379 end;
1381 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1382 if ok then
1383 begin
1384 for c := 0 to High(Textures) do
1385 if Textures[c].TextureName = TexName then
1386 begin
1387 SetLength(AddTextures, Length(AddTextures)+1);
1388 AddTextures[High(AddTextures)].Texture := c;
1389 AddTextures[High(AddTextures)].Anim := isAnim;
1390 Break;
1391 end;
1392 end;
1393 end
1394 else
1395 if k = NNF_NAME_EQUALS then
1396 begin
1397 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1398 SetLength(AddTextures, Length(AddTextures)+1);
1399 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1400 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1401 CurTex := High(AddTextures);
1402 ok := True;
1403 end
1404 else // NNF_NO_NAME
1405 ok := False;
1406 end; // while ok...
1408 ok := True;
1409 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1410 end; // if ok - ññûëàþòñÿ òðèããåðû
1412 if not ok then
1413 begin
1414 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1415 SetLength(AddTextures, 1);
1416 AddTextures[0].Texture := panels[a].TextureNum;
1417 AddTextures[0].Anim := ByteBool(texture.Anim);
1418 CurTex := 0;
1419 end;
1421 //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);
1423 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1424 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1426 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1427 if TriggersTable <> nil then
1428 for b := 0 to High(TriggersTable) do
1429 begin
1430 // Òðèããåð äâåðè/ëèôòà:
1431 if (TriggersTable[b].LiftPanel = a) or
1432 (TriggersTable[b].DoorPanel = a) then
1433 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1434 // Òðèããåð ñìåíû òåêñòóðû:
1435 if TriggersTable[b].TexturePanel = a then
1436 triggers[b].TexturePanel := PanelID;
1437 // Òðèããåð "Òóðåëü":
1438 if TriggersTable[b].ShotPanel = a then
1439 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1440 end;
1442 g_Game_StepLoading();
1443 end;
1444 end;
1446 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1447 if (triggers <> nil) and not gLoadGameMode then
1448 begin
1449 e_WriteLog(' Creating triggers...', MSG_NOTIFY);
1450 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1451 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1452 for a := 0 to High(triggers) do
1453 begin
1454 if triggers[a].TexturePanel <> -1 then
1455 b := panels[TriggersTable[a].TexturePanel].PanelType
1456 else
1457 b := 0;
1458 if (triggers[a].TriggerType = TRIGGER_SHOT) and
1459 (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1460 c := panels[TriggersTable[a].ShotPanel].PanelType
1461 else
1462 c := 0;
1463 CreateTrigger(triggers[a], b, c);
1464 end;
1465 end;
1467 // Çàãðóçêà ïðåäìåòîâ:
1468 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1469 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1470 items := MapReader.GetItems();
1472 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1473 if (items <> nil) and not gLoadGameMode then
1474 begin
1475 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1476 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1477 for a := 0 to High(items) do
1478 CreateItem(Items[a]);
1479 end;
1481 // Çàãðóçêà îáëàñòåé:
1482 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1483 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1484 areas := MapReader.GetAreas();
1486 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1487 if areas <> nil then
1488 begin
1489 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1490 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1491 for a := 0 to High(areas) do
1492 CreateArea(areas[a]);
1493 end;
1495 // Çàãðóçêà ìîíñòðîâ:
1496 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1497 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1498 monsters := MapReader.GetMonsters();
1500 gTotalMonsters := 0;
1502 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1503 if (monsters <> nil) and not gLoadGameMode then
1504 begin
1505 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1506 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1507 for a := 0 to High(monsters) do
1508 CreateMonster(monsters[a]);
1509 end;
1511 // Çàãðóçêà îïèñàíèÿ êàðòû:
1512 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1513 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1514 Header := MapReader.GetMapHeader();
1516 MapReader.Free();
1518 with gMapInfo do
1519 begin
1520 Name := Header.MapName;
1521 Description := Header.MapDescription;
1522 Author := Header.MapAuthor;
1523 MusicName := Header.MusicName;
1524 SkyName := Header.SkyName;
1525 Height := Header.Height;
1526 Width := Header.Width;
1527 end;
1529 // Çàãðóçêà íåáà:
1530 if gMapInfo.SkyName <> '' then
1531 begin
1532 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1533 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1534 FileName := g_ExtractWadName(gMapInfo.SkyName);
1536 if FileName <> '' then
1537 FileName := GameDir+'/wads/'+FileName
1538 else
1539 begin
1540 FileName := g_ExtractWadName(Res);
1541 end;
1543 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1544 if g_Texture_CreateWAD(BackID, s) then
1545 begin
1546 g_Game_SetupScreenSize();
1547 end
1548 else
1549 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1550 end;
1552 // Çàãðóçêà ìóçûêè:
1553 ok := False;
1554 if gMapInfo.MusicName <> '' then
1555 begin
1556 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1557 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1558 FileName := g_ExtractWadName(gMapInfo.MusicName);
1560 if FileName <> '' then
1561 FileName := GameDir+'/wads/'+FileName
1562 else
1563 begin
1564 FileName := g_ExtractWadName(Res);
1565 end;
1567 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1568 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1569 ok := True
1570 else
1571 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1572 end;
1574 // Îñòàëüíûå óñòàíâêè:
1575 CreateDoorMap();
1576 CreateLiftMap();
1578 g_Items_Init();
1579 g_Weapon_Init();
1580 g_Monsters_Init();
1582 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1583 if not gLoadGameMode then
1584 g_GFX_Init();
1586 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1587 _textures := nil;
1588 panels := nil;
1589 items := nil;
1590 areas := nil;
1591 triggers := nil;
1592 TriggersTable := nil;
1593 AddTextures := nil;
1595 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
1596 if ok and (not gLoadGameMode) then
1597 begin
1598 gMusic.SetByName(gMapInfo.MusicName);
1599 gMusic.Play();
1600 end
1601 else
1602 gMusic.SetByName('');
1603 finally
1604 sfsGCEnable(); // enable releasing unused volumes
1605 end;
1607 e_WriteLog('Creating map grid', MSG_NOTIFY);
1608 mapCreateGrid();
1610 e_WriteLog('Done loading map.', MSG_NOTIFY);
1611 Result := True;
1612 end;
1614 function g_Map_GetMapInfo(Res: String): TMapInfo;
1615 var
1616 WAD: TWADFile;
1617 MapReader: TMapReader_1;
1618 Header: TMapHeaderRec_1;
1619 FileName: String;
1620 Data: Pointer;
1621 Len: Integer;
1622 begin
1623 FillChar(Result, SizeOf(Result), 0);
1624 FileName := g_ExtractWadName(Res);
1626 WAD := TWADFile.Create();
1627 if not WAD.ReadFile(FileName) then
1628 begin
1629 WAD.Free();
1630 Exit;
1631 end;
1633 //k8: it ignores path again
1634 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
1635 begin
1636 WAD.Free();
1637 Exit;
1638 end;
1640 WAD.Free();
1642 MapReader := TMapReader_1.Create();
1644 if not MapReader.LoadMap(Data) then
1645 begin
1646 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
1647 ZeroMemory(@Header, SizeOf(Header));
1648 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
1649 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
1650 end
1651 else
1652 begin
1653 Header := MapReader.GetMapHeader();
1654 Result.Name := Header.MapName;
1655 Result.Description := Header.MapDescription;
1656 end;
1658 FreeMem(Data);
1659 MapReader.Free();
1661 Result.Map := Res;
1662 Result.Author := Header.MapAuthor;
1663 Result.Height := Header.Height;
1664 Result.Width := Header.Width;
1665 end;
1667 function g_Map_GetMapsList(WADName: string): SArray;
1668 var
1669 WAD: TWADFile;
1670 a: Integer;
1671 ResList: SArray;
1672 begin
1673 Result := nil;
1674 WAD := TWADFile.Create();
1675 if not WAD.ReadFile(WADName) then
1676 begin
1677 WAD.Free();
1678 Exit;
1679 end;
1680 ResList := WAD.GetMapResources();
1681 if ResList <> nil then
1682 begin
1683 for a := 0 to High(ResList) do
1684 begin
1685 SetLength(Result, Length(Result)+1);
1686 Result[High(Result)] := ResList[a];
1687 end;
1688 end;
1689 WAD.Free();
1690 end;
1692 function g_Map_Exist(Res: string): Boolean;
1693 var
1694 WAD: TWADFile;
1695 FileName, mnn: string;
1696 ResList: SArray;
1697 a: Integer;
1698 begin
1699 Result := False;
1701 FileName := addWadExtension(g_ExtractWadName(Res));
1703 WAD := TWADFile.Create;
1704 if not WAD.ReadFile(FileName) then
1705 begin
1706 WAD.Free();
1707 Exit;
1708 end;
1710 ResList := WAD.GetMapResources();
1711 WAD.Free();
1713 mnn := g_ExtractFileName(Res);
1714 if ResList <> nil then
1715 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
1716 begin
1717 Result := True;
1718 Exit;
1719 end;
1720 end;
1722 procedure g_Map_Free();
1723 var
1724 a: Integer;
1726 procedure FreePanelArray(var panels: TPanelArray);
1727 var
1728 i: Integer;
1730 begin
1731 if panels <> nil then
1732 begin
1733 for i := 0 to High(panels) do
1734 panels[i].Free();
1735 panels := nil;
1736 end;
1737 end;
1739 begin
1740 g_GFX_Free();
1741 g_Weapon_Free();
1742 g_Items_Free();
1743 g_Triggers_Free();
1744 g_Monsters_Free();
1746 RespawnPoints := nil;
1747 if FlagPoints[FLAG_RED] <> nil then
1748 begin
1749 Dispose(FlagPoints[FLAG_RED]);
1750 FlagPoints[FLAG_RED] := nil;
1751 end;
1752 if FlagPoints[FLAG_BLUE] <> nil then
1753 begin
1754 Dispose(FlagPoints[FLAG_BLUE]);
1755 FlagPoints[FLAG_BLUE] := nil;
1756 end;
1757 //DOMFlagPoints := nil;
1759 //gDOMFlags := nil;
1761 if Textures <> nil then
1762 begin
1763 for a := 0 to High(Textures) do
1764 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
1765 if Textures[a].Anim then
1766 g_Frames_DeleteByID(Textures[a].FramesID)
1767 else
1768 if Textures[a].TextureID <> TEXTURE_NONE then
1769 e_DeleteTexture(Textures[a].TextureID);
1771 Textures := nil;
1772 end;
1774 FreePanelArray(gWalls);
1775 FreePanelArray(gRenderBackgrounds);
1776 FreePanelArray(gRenderForegrounds);
1777 FreePanelArray(gWater);
1778 FreePanelArray(gAcid1);
1779 FreePanelArray(gAcid2);
1780 FreePanelArray(gSteps);
1781 FreePanelArray(gLifts);
1782 FreePanelArray(gBlockMon);
1784 if BackID <> DWORD(-1) then
1785 begin
1786 gBackSize.X := 0;
1787 gBackSize.Y := 0;
1788 e_DeleteTexture(BackID);
1789 BackID := DWORD(-1);
1790 end;
1792 g_Game_StopAllSounds(False);
1793 gMusic.FreeSound();
1794 g_Sound_Delete(gMapInfo.MusicName);
1796 gMapInfo.Name := '';
1797 gMapInfo.Description := '';
1798 gMapInfo.MusicName := '';
1799 gMapInfo.Height := 0;
1800 gMapInfo.Width := 0;
1802 gDoorMap := nil;
1803 gLiftMap := nil;
1805 PanelByID := nil;
1806 end;
1808 procedure g_Map_Update();
1809 var
1810 a, d, j: Integer;
1811 m: Word;
1812 s: String;
1814 procedure UpdatePanelArray(var panels: TPanelArray);
1815 var
1816 i: Integer;
1818 begin
1819 if panels <> nil then
1820 for i := 0 to High(panels) do
1821 panels[i].Update();
1822 end;
1824 begin
1825 UpdatePanelArray(gWalls);
1826 UpdatePanelArray(gRenderBackgrounds);
1827 UpdatePanelArray(gRenderForegrounds);
1828 UpdatePanelArray(gWater);
1829 UpdatePanelArray(gAcid1);
1830 UpdatePanelArray(gAcid2);
1831 UpdatePanelArray(gSteps);
1833 if gGameSettings.GameMode = GM_CTF then
1834 begin
1835 for a := FLAG_RED to FLAG_BLUE do
1836 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
1837 with gFlags[a] do
1838 begin
1839 if gFlags[a].Animation <> nil then
1840 gFlags[a].Animation.Update();
1842 m := g_Obj_Move(@Obj, True, True);
1844 if gTime mod (GAME_TICK*2) <> 0 then
1845 Continue;
1847 // Ñîïðîòèâëåíèå âîçäóõà:
1848 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
1850 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
1851 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
1852 begin
1853 g_Map_ResetFlag(a);
1854 gFlags[a].CaptureTime := 0;
1855 if a = FLAG_RED then
1856 s := _lc[I_PLAYER_FLAG_RED]
1857 else
1858 s := _lc[I_PLAYER_FLAG_BLUE];
1859 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1861 if g_Game_IsNet then
1862 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
1863 Continue;
1864 end;
1866 if Count > 0 then
1867 Count := Count - 1;
1869 // Èãðîê áåðåò ôëàã:
1870 if gPlayers <> nil then
1871 begin
1872 j := Random(Length(gPlayers)) - 1;
1874 for d := 0 to High(gPlayers) do
1875 begin
1876 Inc(j);
1877 if j > High(gPlayers) then
1878 j := 0;
1880 if gPlayers[j] <> nil then
1881 if gPlayers[j].Live and
1882 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
1883 begin
1884 if gPlayers[j].GetFlag(a) then
1885 Break;
1886 end;
1887 end;
1888 end;
1889 end;
1890 end;
1891 end;
1894 // old algo
1895 procedure g_Map_DrawPanels (PanelType: Word);
1897 procedure DrawPanels (var panels: TPanelArray; drawDoors: Boolean=False);
1898 var
1899 idx: Integer;
1900 begin
1901 if (panels <> nil) then
1902 begin
1903 // alas, no visible set
1904 for idx := 0 to High(panels) do
1905 begin
1906 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
1907 end;
1908 end;
1909 end;
1911 begin
1912 case PanelType of
1913 PANEL_WALL: DrawPanels(gWalls);
1914 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
1915 PANEL_BACK: DrawPanels(gRenderBackgrounds);
1916 PANEL_FORE: DrawPanels(gRenderForegrounds);
1917 PANEL_WATER: DrawPanels(gWater);
1918 PANEL_ACID1: DrawPanels(gAcid1);
1919 PANEL_ACID2: DrawPanels(gAcid2);
1920 PANEL_STEP: DrawPanels(gSteps);
1921 end;
1922 end;
1925 // new algo
1926 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
1927 function checker (obj: TObject; objx, objy, objw, objh: Integer; tag: Integer): Boolean;
1928 var
1929 pan: TPanel;
1930 begin
1931 result := false; // don't stop, ever
1932 //pan := (obj as TPanel);
1933 pan := TPanel(obj);
1934 //if (PanelType = PANEL_CLOSEDOOR) then begin if not pan.Door then exit; end else begin if pan.Door then exit; end;
1935 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
1936 //dplAddPanel(pan);
1937 gDrawPanelList.insert(pan);
1938 end;
1940 begin
1941 dplClear();
1942 //tagmask := panelTypeToTag(PanelType);
1944 if gdbg_map_use_tree_draw then
1945 begin
1946 mapTree.aabbQuery(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore));
1947 end
1948 else
1949 begin
1950 gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore));
1951 end;
1952 // list will be rendered in `g_game.DrawPlayer()`
1953 (*
1954 while (gDrawPanelList.count > 0) do
1955 begin
1956 //(gDrawPanelList.front() as TPanel).Draw();
1957 TPanel(gDrawPanelList.front()).Draw();
1958 gDrawPanelList.popFront();
1959 end;
1960 *)
1961 end;
1964 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
1965 function checker (obj: TObject; objx, objy, objw, objh: Integer; tag: Integer): Boolean;
1966 var
1967 pan: TPanel;
1968 begin
1969 result := false; // don't stop, ever
1970 //if (tag <> GridTagWall) and (tag <> GridTagDoor) then exit; // only walls
1971 //pan := (obj as TPanel);
1972 pan := TPanel(obj);
1973 pan.DrawShadowVolume(lightX, lightY, radius);
1974 end;
1976 begin
1977 if gdbg_map_use_tree_draw then
1978 begin
1979 mapTree.aabbQuery(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
1980 end
1981 else
1982 begin
1983 gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
1984 end;
1985 end;
1988 procedure g_Map_DrawBack(dx, dy: Integer);
1989 begin
1990 if gDrawBackGround and (BackID <> DWORD(-1)) then
1991 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
1992 else
1993 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
1994 end;
1996 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
1997 PanelType: Word; b1x3: Boolean): Boolean;
1998 var
1999 a, h: Integer;
2000 begin
2001 Result := False;
2003 if WordBool(PanelType and PANEL_WALL) then
2004 if gWalls <> nil then
2005 begin
2006 h := High(gWalls);
2008 for a := 0 to h do
2009 if gWalls[a].Enabled and
2010 g_Collide(X, Y, Width, Height,
2011 gWalls[a].X, gWalls[a].Y,
2012 gWalls[a].Width, gWalls[a].Height) then
2013 begin
2014 Result := True;
2015 Exit;
2016 end;
2017 end;
2019 if WordBool(PanelType and PANEL_WATER) then
2020 if gWater <> nil then
2021 begin
2022 h := High(gWater);
2024 for a := 0 to h do
2025 if g_Collide(X, Y, Width, Height,
2026 gWater[a].X, gWater[a].Y,
2027 gWater[a].Width, gWater[a].Height) then
2028 begin
2029 Result := True;
2030 Exit;
2031 end;
2032 end;
2034 if WordBool(PanelType and PANEL_ACID1) then
2035 if gAcid1 <> nil then
2036 begin
2037 h := High(gAcid1);
2039 for a := 0 to h do
2040 if g_Collide(X, Y, Width, Height,
2041 gAcid1[a].X, gAcid1[a].Y,
2042 gAcid1[a].Width, gAcid1[a].Height) then
2043 begin
2044 Result := True;
2045 Exit;
2046 end;
2047 end;
2049 if WordBool(PanelType and PANEL_ACID2) then
2050 if gAcid2 <> nil then
2051 begin
2052 h := High(gAcid2);
2054 for a := 0 to h do
2055 if g_Collide(X, Y, Width, Height,
2056 gAcid2[a].X, gAcid2[a].Y,
2057 gAcid2[a].Width, gAcid2[a].Height) then
2058 begin
2059 Result := True;
2060 Exit;
2061 end;
2062 end;
2064 if WordBool(PanelType and PANEL_STEP) then
2065 if gSteps <> nil then
2066 begin
2067 h := High(gSteps);
2069 for a := 0 to h do
2070 if g_Collide(X, Y, Width, Height,
2071 gSteps[a].X, gSteps[a].Y,
2072 gSteps[a].Width, gSteps[a].Height) then
2073 begin
2074 Result := True;
2075 Exit;
2076 end;
2077 end;
2079 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2080 if gLifts <> nil then
2081 begin
2082 h := High(gLifts);
2084 for a := 0 to h do
2085 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2086 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2087 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2088 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2089 g_Collide(X, Y, Width, Height,
2090 gLifts[a].X, gLifts[a].Y,
2091 gLifts[a].Width, gLifts[a].Height) then
2092 begin
2093 Result := True;
2094 Exit;
2095 end;
2096 end;
2098 if WordBool(PanelType and PANEL_BLOCKMON) then
2099 if gBlockMon <> nil then
2100 begin
2101 h := High(gBlockMon);
2103 for a := 0 to h do
2104 if ( (not b1x3) or
2105 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2106 g_Collide(X, Y, Width, Height,
2107 gBlockMon[a].X, gBlockMon[a].Y,
2108 gBlockMon[a].Width, gBlockMon[a].Height) then
2109 begin
2110 Result := True;
2111 Exit;
2112 end;
2113 end;
2114 end;
2116 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2117 var
2118 texid: DWORD;
2120 function checkPanels (var panels: TPanelArray): Boolean;
2121 var
2122 a: Integer;
2123 begin
2124 result := false;
2125 if panels = nil then exit;
2126 for a := 0 to High(panels) do
2127 begin
2128 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2129 begin
2130 result := true;
2131 texid := panels[a].GetTextureID();
2132 exit;
2133 end;
2134 end;
2135 end;
2137 begin
2138 texid := TEXTURE_NONE;
2139 result := texid;
2140 if not checkPanels(gWater) then
2141 if not checkPanels(gAcid1) then
2142 if not checkPanels(gAcid2) then exit;
2143 result := texid;
2144 end;
2147 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2149 function checker (obj: TObject; objx, objy, objwidth, objheight: Integer; tag: Integer): Boolean;
2150 var
2151 pan: TPanel;
2152 begin
2153 result := false; // don't stop, ever
2155 if ((tag and GridTagLift) <> 0) then
2156 begin
2157 pan := TPanel(obj);
2158 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (pan.LiftType = 0)) or
2159 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (pan.LiftType = 1)) or
2160 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (pan.LiftType = 2)) or
2161 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (pan.LiftType = 3))) and
2162 g_Collide(X, Y, Width, Height, objx, objy, objwidth, objheight) then
2163 begin
2164 result := true;
2165 exit;
2166 end;
2167 end;
2169 if ((tag and GridTagBlockMon) <> 0) then
2170 begin
2171 if ((not b1x3) or (objwidth+objheight >= 64)) and g_Collide(X, Y, Width, Height, objx, objy, objwidth, objheight) then
2172 begin
2173 result := True;
2174 exit;
2175 end;
2176 end;
2178 // other shit
2179 result := g_Collide(X, Y, Width, Height, objx, objy, objwidth, objheight);
2180 end;
2182 var
2183 tagmask: Integer = 0;
2184 begin
2185 //TODO: detailed profile
2186 if WordBool(PanelType and PANEL_WALL) then tagmask := tagmask or GridTagWall or GridTagDoor;
2187 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2188 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2189 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2190 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2191 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2192 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2194 if (tagmask = 0) then begin result := false; exit; end; // just in case
2196 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('wall coldet');
2197 if gdbg_map_use_accel_coldet then
2198 begin
2199 if gdbg_map_use_tree_coldet then
2200 begin
2201 //e_WriteLog(Format('coldet query: x=%d; y=%d; w=%d; h=%d', [X, Y, Width, Height]), MSG_NOTIFY);
2202 result := (mapTree.aabbQuery(X, Y, Width, Height, checker, tagmask) <> nil);
2203 if (gdbg_map_dump_coldet_tree_queries) and (mapTree.nodesVisited <> 0) then
2204 begin
2205 //e_WriteLog(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]), MSG_NOTIFY);
2206 g_Console_Add(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]));
2207 end;
2208 end
2209 else
2210 begin
2211 result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask);
2212 end;
2213 end
2214 else
2215 begin
2216 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2217 end;
2218 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2219 end;
2222 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2223 var
2224 cctype: Integer = 3; // priority: 0: water, 1: acid1, 2: acid2; 3: others (nothing)
2225 texid: DWORD;
2227 // slightly different from the old code, but meh...
2228 function checker (obj: TObject; objx, objy, objwidth, objheight: Integer; tag: Integer): Boolean;
2229 //var pan: TPanel;
2230 begin
2231 result := false; // don't stop, ever
2232 if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2233 // check priorities
2234 case cctype of
2235 0: if ((tag and GridTagWater) = 0) then exit; // only water
2236 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // water, acid1
2237 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // water, acid1, acid2
2238 end;
2239 // collision?
2240 if not g_Collide(X, Y, Width, Height, objx, objy, objwidth, objheight) then exit;
2241 // yeah
2242 //pan := (obj as TPanel);
2243 texid := TPanel(obj).GetTextureID();
2244 // water? water has the highest priority, so stop right here
2245 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2246 // acid2
2247 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2248 // acid1
2249 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2250 end;
2252 begin
2253 //TODO: detailed profile?
2254 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquid coldet');
2255 if gdbg_map_use_accel_coldet then
2256 begin
2257 texid := TEXTURE_NONE;
2258 if gdbg_map_use_tree_coldet then
2259 begin
2260 mapTree.aabbQuery(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2261 end
2262 else
2263 begin
2264 gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2265 end;
2266 result := texid;
2267 end
2268 else
2269 begin
2270 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2271 end;
2272 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2273 end;
2275 procedure g_Map_EnableWall(ID: DWORD);
2276 begin
2277 with gWalls[ID] do
2278 begin
2279 Enabled := True;
2280 g_Mark(X, Y, Width, Height, MARK_DOOR, True);
2282 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2283 end;
2284 end;
2286 procedure g_Map_DisableWall(ID: DWORD);
2287 begin
2288 with gWalls[ID] do
2289 begin
2290 Enabled := False;
2291 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
2293 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2294 end;
2295 end;
2297 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2298 var
2299 tp: TPanel;
2300 begin
2301 case PanelType of
2302 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2303 tp := gWalls[ID];
2304 PANEL_FORE:
2305 tp := gRenderForegrounds[ID];
2306 PANEL_BACK:
2307 tp := gRenderBackgrounds[ID];
2308 PANEL_WATER:
2309 tp := gWater[ID];
2310 PANEL_ACID1:
2311 tp := gAcid1[ID];
2312 PANEL_ACID2:
2313 tp := gAcid2[ID];
2314 PANEL_STEP:
2315 tp := gSteps[ID];
2316 else
2317 Exit;
2318 end;
2320 tp.NextTexture(AnimLoop);
2321 if g_Game_IsServer and g_Game_IsNet then
2322 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2323 end;
2325 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2326 begin
2327 if gLifts[ID].LiftType = t then
2328 Exit;
2330 with gLifts[ID] do
2331 begin
2332 LiftType := t;
2334 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2336 if LiftType = 0 then
2337 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2338 else if LiftType = 1 then
2339 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2340 else if LiftType = 2 then
2341 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2342 else if LiftType = 3 then
2343 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2345 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2346 end;
2347 end;
2349 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2350 var
2351 a: Integer;
2352 PointsArray: Array of TRespawnPoint;
2353 begin
2354 Result := False;
2355 SetLength(PointsArray, 0);
2357 if RespawnPoints = nil then
2358 Exit;
2360 for a := 0 to High(RespawnPoints) do
2361 if RespawnPoints[a].PointType = PointType then
2362 begin
2363 SetLength(PointsArray, Length(PointsArray)+1);
2364 PointsArray[High(PointsArray)] := RespawnPoints[a];
2365 end;
2367 if PointsArray = nil then
2368 Exit;
2370 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2371 Result := True;
2372 end;
2374 function g_Map_GetPointCount(PointType: Byte): Word;
2375 var
2376 a: Integer;
2377 begin
2378 Result := 0;
2380 if RespawnPoints = nil then
2381 Exit;
2383 for a := 0 to High(RespawnPoints) do
2384 if RespawnPoints[a].PointType = PointType then
2385 Result := Result + 1;
2386 end;
2388 function g_Map_HaveFlagPoints(): Boolean;
2389 begin
2390 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2391 end;
2393 procedure g_Map_ResetFlag(Flag: Byte);
2394 begin
2395 with gFlags[Flag] do
2396 begin
2397 Obj.X := -1000;
2398 Obj.Y := -1000;
2399 Obj.Vel.X := 0;
2400 Obj.Vel.Y := 0;
2401 Direction := D_LEFT;
2402 State := FLAG_STATE_NONE;
2403 if FlagPoints[Flag] <> nil then
2404 begin
2405 Obj.X := FlagPoints[Flag]^.X;
2406 Obj.Y := FlagPoints[Flag]^.Y;
2407 Direction := FlagPoints[Flag]^.Direction;
2408 State := FLAG_STATE_NORMAL;
2409 end;
2410 Count := -1;
2411 end;
2412 end;
2414 procedure g_Map_DrawFlags();
2415 var
2416 i, dx: Integer;
2417 Mirror: TMirrorType;
2418 begin
2419 if gGameSettings.GameMode <> GM_CTF then
2420 Exit;
2422 for i := FLAG_RED to FLAG_BLUE do
2423 with gFlags[i] do
2424 if State <> FLAG_STATE_CAPTURED then
2425 begin
2426 if State = FLAG_STATE_NONE then
2427 continue;
2429 if Direction = D_LEFT then
2430 begin
2431 Mirror := M_HORIZONTAL;
2432 dx := -1;
2433 end
2434 else
2435 begin
2436 Mirror := M_NONE;
2437 dx := 1;
2438 end;
2440 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2442 if g_debug_Frames then
2443 begin
2444 e_DrawQuad(Obj.X+Obj.Rect.X,
2445 Obj.Y+Obj.Rect.Y,
2446 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2447 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2448 0, 255, 0);
2449 end;
2450 end;
2451 end;
2453 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2454 var
2455 dw: DWORD;
2456 b: Byte;
2457 str: String;
2458 boo: Boolean;
2460 procedure SavePanelArray(var panels: TPanelArray);
2461 var
2462 PAMem: TBinMemoryWriter;
2463 i: Integer;
2464 begin
2465 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2466 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2468 i := 0;
2469 while i < Length(panels) do
2470 begin
2471 if panels[i].SaveIt then
2472 begin
2473 // ID ïàíåëè:
2474 PAMem.WriteInt(i);
2475 // Ñîõðàíÿåì ïàíåëü:
2476 panels[i].SaveState(PAMem);
2477 end;
2478 Inc(i);
2479 end;
2481 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2482 PAMem.SaveToMemory(Mem);
2483 PAMem.Free();
2484 end;
2486 procedure SaveFlag(flag: PFlag);
2487 begin
2488 // Ñèãíàòóðà ôëàãà:
2489 dw := FLAG_SIGNATURE; // 'FLAG'
2490 Mem.WriteDWORD(dw);
2491 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2492 Mem.WriteByte(flag^.RespawnType);
2493 // Ñîñòîÿíèå ôëàãà:
2494 Mem.WriteByte(flag^.State);
2495 // Íàïðàâëåíèå ôëàãà:
2496 if flag^.Direction = D_LEFT then
2497 b := 1
2498 else // D_RIGHT
2499 b := 2;
2500 Mem.WriteByte(b);
2501 // Îáúåêò ôëàãà:
2502 Obj_SaveState(@flag^.Obj, Mem);
2503 end;
2505 begin
2506 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2508 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2509 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2510 SavePanelArray(gWalls);
2511 // Ñîõðàíÿåì ïàíåëè ôîíà:
2512 SavePanelArray(gRenderBackgrounds);
2513 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2514 SavePanelArray(gRenderForegrounds);
2515 // Ñîõðàíÿåì ïàíåëè âîäû:
2516 SavePanelArray(gWater);
2517 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2518 SavePanelArray(gAcid1);
2519 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2520 SavePanelArray(gAcid2);
2521 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2522 SavePanelArray(gSteps);
2523 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2524 SavePanelArray(gLifts);
2525 ///// /////
2527 ///// Ñîõðàíÿåì ìóçûêó: /////
2528 // Ñèãíàòóðà ìóçûêè:
2529 dw := MUSIC_SIGNATURE; // 'MUSI'
2530 Mem.WriteDWORD(dw);
2531 // Íàçâàíèå ìóçûêè:
2532 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2533 if gMusic.NoMusic then
2534 str := ''
2535 else
2536 str := gMusic.Name;
2537 Mem.WriteString(str, 64);
2538 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2539 dw := gMusic.GetPosition();
2540 Mem.WriteDWORD(dw);
2541 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2542 boo := gMusic.SpecPause;
2543 Mem.WriteBoolean(boo);
2544 ///// /////
2546 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2547 Mem.WriteInt(gTotalMonsters);
2548 ///// /////
2550 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2551 if gGameSettings.GameMode = GM_CTF then
2552 begin
2553 // Ôëàã Êðàñíîé êîìàíäû:
2554 SaveFlag(@gFlags[FLAG_RED]);
2555 // Ôëàã Ñèíåé êîìàíäû:
2556 SaveFlag(@gFlags[FLAG_BLUE]);
2557 end;
2558 ///// /////
2560 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2561 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2562 begin
2563 // Î÷êè Êðàñíîé êîìàíäû:
2564 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2565 // Î÷êè Ñèíåé êîìàíäû:
2566 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2567 end;
2568 ///// /////
2569 end;
2571 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2572 var
2573 dw: DWORD;
2574 b: Byte;
2575 str: String;
2576 boo: Boolean;
2578 procedure LoadPanelArray(var panels: TPanelArray);
2579 var
2580 PAMem: TBinMemoryReader;
2581 i, id: Integer;
2582 begin
2583 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
2584 PAMem := TBinMemoryReader.Create();
2585 PAMem.LoadFromMemory(Mem);
2587 for i := 0 to Length(panels)-1 do
2588 if panels[i].SaveIt then
2589 begin
2590 // ID ïàíåëè:
2591 PAMem.ReadInt(id);
2592 if id <> i then
2593 begin
2594 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
2595 end;
2596 // Çàãðóæàåì ïàíåëü:
2597 panels[i].LoadState(PAMem);
2598 end;
2600 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
2601 PAMem.Free();
2602 end;
2604 procedure LoadFlag(flag: PFlag);
2605 begin
2606 // Ñèãíàòóðà ôëàãà:
2607 Mem.ReadDWORD(dw);
2608 if dw <> FLAG_SIGNATURE then // 'FLAG'
2609 begin
2610 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
2611 end;
2612 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2613 Mem.ReadByte(flag^.RespawnType);
2614 // Ñîñòîÿíèå ôëàãà:
2615 Mem.ReadByte(flag^.State);
2616 // Íàïðàâëåíèå ôëàãà:
2617 Mem.ReadByte(b);
2618 if b = 1 then
2619 flag^.Direction := D_LEFT
2620 else // b = 2
2621 flag^.Direction := D_RIGHT;
2622 // Îáúåêò ôëàãà:
2623 Obj_LoadState(@flag^.Obj, Mem);
2624 end;
2626 begin
2627 if Mem = nil then
2628 Exit;
2630 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
2631 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
2632 LoadPanelArray(gWalls);
2633 // Çàãðóæàåì ïàíåëè ôîíà:
2634 LoadPanelArray(gRenderBackgrounds);
2635 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
2636 LoadPanelArray(gRenderForegrounds);
2637 // Çàãðóæàåì ïàíåëè âîäû:
2638 LoadPanelArray(gWater);
2639 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
2640 LoadPanelArray(gAcid1);
2641 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
2642 LoadPanelArray(gAcid2);
2643 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
2644 LoadPanelArray(gSteps);
2645 // Çàãðóæàåì ïàíåëè ëèôòîâ:
2646 LoadPanelArray(gLifts);
2647 ///// /////
2649 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
2650 g_GFX_Init();
2651 mapCreateGrid();
2653 ///// Çàãðóæàåì ìóçûêó: /////
2654 // Ñèãíàòóðà ìóçûêè:
2655 Mem.ReadDWORD(dw);
2656 if dw <> MUSIC_SIGNATURE then // 'MUSI'
2657 begin
2658 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
2659 end;
2660 // Íàçâàíèå ìóçûêè:
2661 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
2662 Mem.ReadString(str);
2663 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2664 Mem.ReadDWORD(dw);
2665 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2666 Mem.ReadBoolean(boo);
2667 // Çàïóñêàåì ýòó ìóçûêó:
2668 gMusic.SetByName(str);
2669 gMusic.SpecPause := boo;
2670 gMusic.Play();
2671 gMusic.Pause(True);
2672 gMusic.SetPosition(dw);
2673 ///// /////
2675 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
2676 Mem.ReadInt(gTotalMonsters);
2677 ///// /////
2679 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
2680 if gGameSettings.GameMode = GM_CTF then
2681 begin
2682 // Ôëàã Êðàñíîé êîìàíäû:
2683 LoadFlag(@gFlags[FLAG_RED]);
2684 // Ôëàã Ñèíåé êîìàíäû:
2685 LoadFlag(@gFlags[FLAG_BLUE]);
2686 end;
2687 ///// /////
2689 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2690 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2691 begin
2692 // Î÷êè Êðàñíîé êîìàíäû:
2693 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
2694 // Î÷êè Ñèíåé êîìàíäû:
2695 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
2696 end;
2697 ///// /////
2698 end;
2700 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
2701 var
2702 Arr: TPanelArray;
2703 begin
2704 Result := nil;
2705 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
2706 Arr := PanelByID[PanelID].PWhere^;
2707 PanelArrayID := PanelByID[PanelID].PArrID;
2708 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
2709 end;
2711 end.