DEADSOFTWARE

29750c92009aeb5a36a4bb13fd2601bf22c521ff
[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, md5, 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(x0, y0, wdt, hgt: Integer; PanelType: Word);
67 procedure g_Map_DrawBack(dx, dy: Integer);
68 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
69 PanelType: Word; b1x3: Boolean): Boolean;
70 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
71 procedure g_Map_EnableWall(ID: DWORD);
72 procedure g_Map_DisableWall(ID: DWORD);
73 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
74 procedure g_Map_SetLift(ID: DWORD; t: Integer);
75 procedure g_Map_ReAdd_DieTriggers();
76 function g_Map_IsSpecialTexture(Texture: String): Boolean;
78 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
79 function g_Map_GetPointCount(PointType: Byte): Word;
81 function g_Map_HaveFlagPoints(): Boolean;
83 procedure g_Map_ResetFlag(Flag: Byte);
84 procedure g_Map_DrawFlags();
86 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
88 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
89 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
91 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
93 procedure g_Map_ProfilersBegin ();
94 procedure g_Map_ProfilersEnd ();
96 const
97 RESPAWNPOINT_PLAYER1 = 1;
98 RESPAWNPOINT_PLAYER2 = 2;
99 RESPAWNPOINT_DM = 3;
100 RESPAWNPOINT_RED = 4;
101 RESPAWNPOINT_BLUE = 5;
103 FLAG_NONE = 0;
104 FLAG_RED = 1;
105 FLAG_BLUE = 2;
106 FLAG_DOM = 3;
108 FLAG_STATE_NONE = 0;
109 FLAG_STATE_NORMAL = 1;
110 FLAG_STATE_DROPPED = 2;
111 FLAG_STATE_CAPTURED = 3;
112 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
113 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
115 FLAG_TIME = 720; // 20 seconds
117 SKY_STRETCH: Single = 1.5;
119 var
120 gWalls: TPanelArray;
121 gRenderBackgrounds: TPanelArray;
122 gRenderForegrounds: TPanelArray;
123 gWater, gAcid1, gAcid2: TPanelArray;
124 gSteps: TPanelArray;
125 gLifts: TPanelArray;
126 gBlockMon: TPanelArray;
127 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
128 //gDOMFlags: array of TFlag;
129 gMapInfo: TMapInfo;
130 gBackSize: TPoint;
131 gDoorMap: array of array of DWORD;
132 gLiftMap: array of array of DWORD;
133 gWADHash: TMD5Digest;
134 BackID: DWORD = DWORD(-1);
135 gExternalResources: TStringList;
137 gdbg_map_use_grid_render: Boolean = true;
138 gdbg_map_use_grid_coldet: Boolean = true;
139 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
141 implementation
143 uses
144 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
145 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
146 g_options, MAPREADER, g_triggers, g_player, MAPDEF,
147 Math, g_monsters, g_saveload, g_language, g_netmsg,
148 utils, sfs,
149 ImagingTypes, Imaging, ImagingUtility,
150 ImagingGif, ImagingNetworkGraphics;
152 const
153 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
154 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
155 FLAG_SIGNATURE = $47414C46; // 'FLAG'
157 GridTagInvalid = -1;
158 GridTagWallDoor = 0;
159 GridTagBack = 1;
160 GridTagFore = 2;
161 GridTagWater = 3;
162 GridTagAcid1 = 4;
163 GridTagAcid2 = 5;
164 GridTagStep = 6;
165 GridTagLift = 7;
166 GridTagBlockMon = 8;
169 function panelTypeToTag (panelType: Word): Integer;
170 begin
171 case panelType of
172 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagWallDoor; // gWalls
173 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
174 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
175 PANEL_WATER: result := GridTagWater; // gWater
176 PANEL_ACID1: result := GridTagAcid1; // gAcid1
177 PANEL_ACID2: result := GridTagAcid2; // gAcid2
178 PANEL_STEP: result := GridTagStep; // gSteps
179 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
180 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
181 else result := GridTagInvalid;
182 end;
183 end;
186 type
187 TPanelID = record
188 PWhere: ^TPanelArray;
189 PArrID: Integer;
190 end;
192 var
193 PanelById: array of TPanelID;
194 Textures: TLevelTextureArray;
195 RespawnPoints: Array of TRespawnPoint;
196 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
197 //DOMFlagPoints: Array of TFlagPoint;
198 gMapGrid: TBodyGrid = nil;
201 procedure g_Map_ProfilersBegin ();
202 begin
203 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('MAP COLLISION', g_profile_history_size);
204 profMapCollision.mainBegin(g_profile_collision);
205 // create sections
206 if g_profile_collision then
207 begin
208 profMapCollision.sectionBegin('wall coldet');
209 profMapCollision.sectionEnd();
210 profMapCollision.sectionBegin('liquid coldet');
211 profMapCollision.sectionEnd();
212 end;
213 end;
215 procedure g_Map_ProfilersEnd ();
216 begin
217 if (profMapCollision <> nil) then profMapCollision.mainEnd();
218 end;
221 function g_Map_IsSpecialTexture(Texture: String): Boolean;
222 begin
223 Result := (Texture = TEXTURE_NAME_WATER) or
224 (Texture = TEXTURE_NAME_ACID1) or
225 (Texture = TEXTURE_NAME_ACID2);
226 end;
228 procedure CreateDoorMap();
229 var
230 PanelArray: Array of record
231 X, Y: Integer;
232 Width, Height: Word;
233 Active: Boolean;
234 PanelID: DWORD;
235 end;
236 a, b, c, m, i, len: Integer;
237 ok: Boolean;
238 begin
239 if gWalls = nil then
240 Exit;
242 i := 0;
243 len := 128;
244 SetLength(PanelArray, len);
246 for a := 0 to High(gWalls) do
247 if gWalls[a].Door then
248 begin
249 PanelArray[i].X := gWalls[a].X;
250 PanelArray[i].Y := gWalls[a].Y;
251 PanelArray[i].Width := gWalls[a].Width;
252 PanelArray[i].Height := gWalls[a].Height;
253 PanelArray[i].Active := True;
254 PanelArray[i].PanelID := a;
256 i := i + 1;
257 if i = len then
258 begin
259 len := len + 128;
260 SetLength(PanelArray, len);
261 end;
262 end;
264 // Íåò äâåðåé:
265 if i = 0 then
266 begin
267 PanelArray := nil;
268 Exit;
269 end;
271 SetLength(gDoorMap, 0);
273 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
275 for a := 0 to i-1 do
276 if PanelArray[a].Active then
277 begin
278 PanelArray[a].Active := False;
279 m := Length(gDoorMap);
280 SetLength(gDoorMap, m+1);
281 SetLength(gDoorMap[m], 1);
282 gDoorMap[m, 0] := PanelArray[a].PanelID;
283 ok := True;
285 while ok do
286 begin
287 ok := False;
289 for b := 0 to i-1 do
290 if PanelArray[b].Active then
291 for c := 0 to High(gDoorMap[m]) do
292 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
293 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
294 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
295 PanelArray[b].Width, PanelArray[b].Height,
296 gWalls[gDoorMap[m, c]].X,
297 gWalls[gDoorMap[m, c]].Y,
298 gWalls[gDoorMap[m, c]].Width,
299 gWalls[gDoorMap[m, c]].Height) then
300 begin
301 PanelArray[b].Active := False;
302 SetLength(gDoorMap[m],
303 Length(gDoorMap[m])+1);
304 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
305 ok := True;
306 Break;
307 end;
308 end;
310 g_Game_StepLoading();
311 end;
313 PanelArray := nil;
314 end;
316 procedure CreateLiftMap();
317 var
318 PanelArray: Array of record
319 X, Y: Integer;
320 Width, Height: Word;
321 Active: Boolean;
322 end;
323 a, b, c, len, i, j: Integer;
324 ok: Boolean;
325 begin
326 if gLifts = nil then
327 Exit;
329 len := Length(gLifts);
330 SetLength(PanelArray, len);
332 for a := 0 to len-1 do
333 begin
334 PanelArray[a].X := gLifts[a].X;
335 PanelArray[a].Y := gLifts[a].Y;
336 PanelArray[a].Width := gLifts[a].Width;
337 PanelArray[a].Height := gLifts[a].Height;
338 PanelArray[a].Active := True;
339 end;
341 SetLength(gLiftMap, len);
342 i := 0;
344 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
346 for a := 0 to len-1 do
347 if PanelArray[a].Active then
348 begin
349 PanelArray[a].Active := False;
350 SetLength(gLiftMap[i], 32);
351 j := 0;
352 gLiftMap[i, j] := a;
353 ok := True;
355 while ok do
356 begin
357 ok := False;
358 for b := 0 to len-1 do
359 if PanelArray[b].Active then
360 for c := 0 to j do
361 if g_CollideAround(PanelArray[b].X,
362 PanelArray[b].Y,
363 PanelArray[b].Width,
364 PanelArray[b].Height,
365 PanelArray[gLiftMap[i, c]].X,
366 PanelArray[gLiftMap[i, c]].Y,
367 PanelArray[gLiftMap[i, c]].Width,
368 PanelArray[gLiftMap[i, c]].Height) then
369 begin
370 PanelArray[b].Active := False;
371 j := j+1;
372 if j > High(gLiftMap[i]) then
373 SetLength(gLiftMap[i],
374 Length(gLiftMap[i])+32);
376 gLiftMap[i, j] := b;
377 ok := True;
379 Break;
380 end;
381 end;
383 SetLength(gLiftMap[i], j+1);
384 i := i+1;
386 g_Game_StepLoading();
387 end;
389 SetLength(gLiftMap, i);
391 PanelArray := nil;
392 end;
394 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
395 CurTex: Integer; sav: Boolean): Integer;
396 var
397 len: Integer;
398 panels: ^TPanelArray;
399 begin
400 Result := -1;
402 case PanelRec.PanelType of
403 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
404 panels := @gWalls;
405 PANEL_BACK:
406 panels := @gRenderBackgrounds;
407 PANEL_FORE:
408 panels := @gRenderForegrounds;
409 PANEL_WATER:
410 panels := @gWater;
411 PANEL_ACID1:
412 panels := @gAcid1;
413 PANEL_ACID2:
414 panels := @gAcid2;
415 PANEL_STEP:
416 panels := @gSteps;
417 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
418 panels := @gLifts;
419 PANEL_BLOCKMON:
420 panels := @gBlockMon;
421 else
422 Exit;
423 end;
425 len := Length(panels^);
426 SetLength(panels^, len + 1);
428 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
429 panels^[len].ArrIdx := len;
430 if sav then
431 panels^[len].SaveIt := True;
433 Result := len;
435 len := Length(PanelByID);
436 SetLength(PanelByID, len + 1);
437 PanelByID[len].PWhere := panels;
438 PanelByID[len].PArrID := Result;
439 end;
441 function CreateNullTexture(RecName: String): Integer;
442 begin
443 SetLength(Textures, Length(Textures)+1);
444 result := High(Textures);
446 with Textures[High(Textures)] do
447 begin
448 TextureName := RecName;
449 Width := 1;
450 Height := 1;
451 Anim := False;
452 TextureID := TEXTURE_NONE;
453 end;
454 end;
456 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
457 var
458 WAD: TWADFile;
459 TextureData: Pointer;
460 WADName, txname: String;
461 a, ResLength: Integer;
462 begin
463 Result := -1;
465 if Textures <> nil then
466 for a := 0 to High(Textures) do
467 if Textures[a].TextureName = RecName then
468 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
469 Result := a;
470 Exit;
471 end;
473 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
474 if (RecName = TEXTURE_NAME_WATER) or
475 (RecName = TEXTURE_NAME_ACID1) or
476 (RecName = TEXTURE_NAME_ACID2) then
477 begin
478 SetLength(Textures, Length(Textures)+1);
480 with Textures[High(Textures)] do
481 begin
482 TextureName := RecName;
484 if TextureName = TEXTURE_NAME_WATER then
485 TextureID := TEXTURE_SPECIAL_WATER
486 else
487 if TextureName = TEXTURE_NAME_ACID1 then
488 TextureID := TEXTURE_SPECIAL_ACID1
489 else
490 if TextureName = TEXTURE_NAME_ACID2 then
491 TextureID := TEXTURE_SPECIAL_ACID2;
493 Anim := False;
494 end;
496 result := High(Textures);
497 Exit;
498 end;
500 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
501 WADName := g_ExtractWadName(RecName);
503 WAD := TWADFile.Create();
505 if WADName <> '' then
506 WADName := GameDir+'/wads/'+WADName
507 else
508 WADName := Map;
510 WAD.ReadFile(WADName);
512 txname := RecName;
514 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
515 begin
516 FreeMem(TextureData);
517 RecName := 'COMMON\ALIEN';
518 end;
521 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
522 begin
523 SetLength(Textures, Length(Textures)+1);
524 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
525 Exit;
526 e_GetTextureSize(Textures[High(Textures)].TextureID,
527 @Textures[High(Textures)].Width,
528 @Textures[High(Textures)].Height);
529 FreeMem(TextureData);
530 Textures[High(Textures)].TextureName := {RecName}txname;
531 Textures[High(Textures)].Anim := False;
533 result := High(Textures);
534 end
535 else // Íåò òàêîãî ðåóñðñà â WAD'å
536 begin
537 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
538 if log then
539 begin
540 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
541 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
542 end;
543 end;
545 WAD.Free();
546 end;
548 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
549 var
550 WAD: TWADFile;
551 TextureWAD: PChar = nil;
552 TextData: Pointer = nil;
553 TextureData: Pointer = nil;
554 cfg: TConfig = nil;
555 WADName: String;
556 ResLength: Integer;
557 TextureResource: String;
558 _width, _height, _framecount, _speed: Integer;
559 _backanimation: Boolean;
560 //imgfmt: string;
561 ia: TDynImageDataArray = nil;
562 f, c, frdelay, frloop: Integer;
563 begin
564 result := -1;
566 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
568 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
569 WADName := g_ExtractWadName(RecName);
571 WAD := TWADFile.Create();
572 try
573 if WADName <> '' then
574 WADName := GameDir+'/wads/'+WADName
575 else
576 WADName := Map;
578 WAD.ReadFile(WADName);
580 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
581 begin
582 if log then
583 begin
584 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
585 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
586 end;
587 exit;
588 end;
590 {TEST
591 if WADName = Map then
592 begin
593 //FreeMem(TextureWAD);
594 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
595 end;
598 WAD.FreeWAD();
600 if ResLength < 6 then
601 begin
602 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
603 exit;
604 end;
606 // ýòî ïòèöà? ýòî ñàìîë¸ò?
607 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
608 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
609 begin
610 // íåò, ýòî ñóïåðìåí!
611 if not WAD.ReadMemory(TextureWAD, ResLength) then
612 begin
613 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
614 exit;
615 end;
617 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
618 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
619 begin
620 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
621 exit;
622 end;
624 cfg := TConfig.CreateMem(TextData, ResLength);
626 TextureResource := cfg.ReadStr('', 'resource', '');
627 if TextureResource = '' then
628 begin
629 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
630 exit;
631 end;
633 _width := cfg.ReadInt('', 'framewidth', 0);
634 _height := cfg.ReadInt('', 'frameheight', 0);
635 _framecount := cfg.ReadInt('', 'framecount', 0);
636 _speed := cfg.ReadInt('', 'waitcount', 0);
637 _backanimation := cfg.ReadBool('', 'backanimation', False);
639 cfg.Free();
640 cfg := nil;
642 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
643 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
644 begin
645 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
646 exit;
647 end;
649 WAD.Free();
650 WAD := nil;
652 SetLength(Textures, Length(Textures)+1);
653 with Textures[High(Textures)] do
654 begin
655 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
656 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
657 begin
658 TextureName := RecName;
659 Width := _width;
660 Height := _height;
661 Anim := True;
662 FramesCount := _framecount;
663 Speed := _speed;
664 result := High(Textures);
665 end
666 else
667 begin
668 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
669 end;
670 end;
671 end
672 else
673 begin
674 // try animated image
676 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
677 if length(imgfmt) = 0 then
678 begin
679 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
680 exit;
681 end;
683 GlobalMetadata.ClearMetaItems();
684 GlobalMetadata.ClearMetaItemsForSaving();
685 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
686 begin
687 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
688 exit;
689 end;
690 if length(ia) = 0 then
691 begin
692 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
693 exit;
694 end;
696 WAD.Free();
697 WAD := nil;
699 _width := ia[0].width;
700 _height := ia[0].height;
701 _framecount := length(ia);
702 _speed := 1;
703 _backanimation := false;
704 frdelay := -1;
705 frloop := -666;
706 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
707 begin
708 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
709 try
710 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
711 frdelay := f;
712 if f < 0 then f := 0;
713 // rounding ;-)
714 c := f mod 28;
715 if c < 13 then c := 0 else c := 1;
716 f := (f div 28)+c;
717 if f < 1 then f := 1 else if f > 255 then f := 255;
718 _speed := f;
719 except
720 end;
721 end;
722 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
723 begin
724 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
725 try
726 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
727 frloop := f;
728 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
729 except
730 end;
731 end;
732 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
733 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
734 f := ord(_backanimation);
735 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);
737 SetLength(Textures, Length(Textures)+1);
738 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
739 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
740 begin
741 Textures[High(Textures)].TextureName := RecName;
742 Textures[High(Textures)].Width := _width;
743 Textures[High(Textures)].Height := _height;
744 Textures[High(Textures)].Anim := True;
745 Textures[High(Textures)].FramesCount := length(ia);
746 Textures[High(Textures)].Speed := _speed;
747 result := High(Textures);
748 //writeln(' CREATED!');
749 end
750 else
751 begin
752 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
753 end;
754 end;
755 finally
756 for f := 0 to High(ia) do FreeImage(ia[f]);
757 WAD.Free();
758 cfg.Free();
759 if TextureWAD <> nil then FreeMem(TextureWAD);
760 if TextData <> nil then FreeMem(TextData);
761 if TextureData <> nil then FreeMem(TextureData);
762 end;
763 end;
765 procedure CreateItem(Item: TItemRec_1);
766 begin
767 if g_Game_IsClient then Exit;
769 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
770 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
771 Exit;
773 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
774 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
775 end;
777 procedure CreateArea(Area: TAreaRec_1);
778 var
779 a: Integer;
780 id: DWORD;
781 begin
782 case Area.AreaType of
783 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
784 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
785 begin
786 SetLength(RespawnPoints, Length(RespawnPoints)+1);
787 with RespawnPoints[High(RespawnPoints)] do
788 begin
789 X := Area.X;
790 Y := Area.Y;
791 Direction := TDirection(Area.Direction);
793 case Area.AreaType of
794 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
795 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
796 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
797 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
798 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
799 end;
800 end;
801 end;
803 AREA_REDFLAG, AREA_BLUEFLAG:
804 begin
805 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
807 if FlagPoints[a] <> nil then Exit;
809 New(FlagPoints[a]);
811 with FlagPoints[a]^ do
812 begin
813 X := Area.X-FLAGRECT.X;
814 Y := Area.Y-FLAGRECT.Y;
815 Direction := TDirection(Area.Direction);
816 end;
818 with gFlags[a] do
819 begin
820 case a of
821 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
822 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
823 end;
825 Animation := TAnimation.Create(id, True, 8);
826 Obj.Rect := FLAGRECT;
828 g_Map_ResetFlag(a);
829 end;
830 end;
832 AREA_DOMFLAG:
833 begin
834 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
835 with DOMFlagPoints[High(DOMFlagPoints)] do
836 begin
837 X := Area.X;
838 Y := Area.Y;
839 Direction := TDirection(Area.Direction);
840 end;
842 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
843 end;
844 end;
845 end;
847 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
848 var
849 _trigger: TTrigger;
850 begin
851 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
853 with _trigger do
854 begin
855 X := Trigger.X;
856 Y := Trigger.Y;
857 Width := Trigger.Width;
858 Height := Trigger.Height;
859 Enabled := ByteBool(Trigger.Enabled);
860 TexturePanel := Trigger.TexturePanel;
861 TexturePanelType := fTexturePanel1Type;
862 ShotPanelType := fTexturePanel2Type;
863 TriggerType := Trigger.TriggerType;
864 ActivateType := Trigger.ActivateType;
865 Keys := Trigger.Keys;
866 Data.Default := Trigger.DATA;
867 end;
869 g_Triggers_Create(_trigger);
870 end;
872 procedure CreateMonster(monster: TMonsterRec_1);
873 var
874 a, i: Integer;
875 begin
876 if g_Game_IsClient then Exit;
878 if (gGameSettings.GameType = GT_SINGLE)
879 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
880 begin
881 i := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y,
882 TDirection(monster.Direction));
884 if gTriggers <> nil then
885 for a := 0 to High(gTriggers) do
886 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
887 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
888 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
889 gMonsters[i].AddTrigger(a);
891 if monster.MonsterType <> MONSTER_BARREL then
892 Inc(gTotalMonsters);
893 end;
894 end;
896 procedure g_Map_ReAdd_DieTriggers();
897 var
898 i, a: Integer;
899 begin
900 if g_Game_IsClient then Exit;
902 for i := 0 to High(gMonsters) do
903 if gMonsters[i] <> nil then
904 begin
905 gMonsters[i].ClearTriggers();
907 for a := 0 to High(gTriggers) do
908 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
909 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
910 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
911 gMonsters[i].AddTrigger(a);
912 end;
913 end;
915 function extractWadName(resourceName: string): string;
916 var
917 posN: Integer;
918 begin
919 posN := Pos(':', resourceName);
920 if posN > 0 then
921 Result:= Copy(resourceName, 0, posN-1)
922 else
923 Result := '';
924 end;
926 procedure addResToExternalResList(res: string);
927 begin
928 res := extractWadName(res);
929 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
930 gExternalResources.Add(res);
931 end;
933 procedure generateExternalResourcesList(mapReader: TMapReader_1);
934 var
935 textures: TTexturesRec1Array;
936 mapHeader: TMapHeaderRec_1;
937 i: integer;
938 resFile: String = '';
939 begin
940 if gExternalResources = nil then
941 gExternalResources := TStringList.Create;
943 gExternalResources.Clear;
944 textures := mapReader.GetTextures();
945 for i := 0 to High(textures) do
946 begin
947 addResToExternalResList(resFile);
948 end;
950 textures := nil;
952 mapHeader := mapReader.GetMapHeader;
954 addResToExternalResList(mapHeader.MusicName);
955 addResToExternalResList(mapHeader.SkyName);
956 end;
958 procedure mapCreateGrid ();
959 var
960 mapX0: Integer = $3fffffff;
961 mapY0: Integer = $3fffffff;
962 mapX1: Integer = -$3fffffff;
963 mapY1: Integer = -$3fffffff;
965 procedure fixMinMax (var panels: TPanelArray);
966 var
967 idx: Integer;
968 begin
969 for idx := 0 to High(panels) do
970 begin
971 if (panels[idx].Width < 1) or (panels[idx].Height < 1) then continue;
972 if mapX0 > panels[idx].X then mapX0 := panels[idx].X;
973 if mapY0 > panels[idx].Y then mapY0 := panels[idx].Y;
974 if mapX1 < panels[idx].X+panels[idx].Width-1 then mapX1 := panels[idx].X+panels[idx].Width-1;
975 if mapY1 < panels[idx].Y+panels[idx].Height-1 then mapY1 := panels[idx].Y+panels[idx].Height-1;
976 end;
977 end;
979 procedure addPanelsToGrid (var panels: TPanelArray; tag: Integer);
980 var
981 idx: Integer;
982 begin
983 tag := panelTypeToTag(tag);
984 for idx := High(panels) downto 0 do
985 begin
986 gMapGrid.insertBody(panels[idx], panels[idx].X, panels[idx].Y, panels[idx].Width, panels[idx].Height, tag);
987 end;
988 end;
990 begin
991 gMapGrid.Free();
992 gMapGrid := nil;
994 fixMinMax(gWalls);
995 fixMinMax(gRenderBackgrounds);
996 fixMinMax(gRenderForegrounds);
997 fixMinMax(gWater);
998 fixMinMax(gAcid1);
999 fixMinMax(gAcid2);
1000 fixMinMax(gSteps);
1001 fixMinMax(gLifts);
1002 fixMinMax(gBlockMon);
1004 if (mapX0 < 0) or (mapY0 < 0) then
1005 begin
1006 e_WriteLog(Format('funny map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING);
1007 //raise Exception.Create('we are fucked');
1008 end;
1010 gMapGrid := TBodyGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
1012 addPanelsToGrid(gWalls, PANEL_WALL); // and PANEL_CLOSEDOOR
1013 addPanelsToGrid(gRenderBackgrounds, PANEL_BACK);
1014 addPanelsToGrid(gRenderForegrounds, PANEL_FORE);
1015 addPanelsToGrid(gWater, PANEL_WATER);
1016 addPanelsToGrid(gAcid1, PANEL_ACID1);
1017 addPanelsToGrid(gAcid2, PANEL_ACID2);
1018 addPanelsToGrid(gSteps, PANEL_STEP);
1019 addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here
1020 addPanelsToGrid(gBlockMon, PANEL_BLOCKMON);
1022 gMapGrid.dumpStats();
1023 end;
1025 function g_Map_Load(Res: String): Boolean;
1026 const
1027 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1028 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1029 var
1030 WAD: TWADFile;
1031 MapReader: TMapReader_1;
1032 Header: TMapHeaderRec_1;
1033 _textures: TTexturesRec1Array;
1034 _texnummap: array of Integer; // `_textures` -> `Textures`
1035 panels: TPanelsRec1Array;
1036 items: TItemsRec1Array;
1037 monsters: TMonsterRec1Array;
1038 areas: TAreasRec1Array;
1039 triggers: TTriggersRec1Array;
1040 a, b, c, k: Integer;
1041 PanelID: DWORD;
1042 AddTextures: TAddTextureArray;
1043 texture: TTextureRec_1;
1044 TriggersTable: Array of record
1045 TexturePanel: Integer;
1046 LiftPanel: Integer;
1047 DoorPanel: Integer;
1048 ShotPanel: Integer;
1049 end;
1050 FileName, mapResName, s, TexName: String;
1051 Data: Pointer;
1052 Len: Integer;
1053 ok, isAnim, trigRef: Boolean;
1054 CurTex, ntn: Integer;
1056 begin
1057 gMapGrid.Free();
1058 gMapGrid := nil;
1060 Result := False;
1061 gMapInfo.Map := Res;
1062 TriggersTable := nil;
1063 FillChar(texture, SizeOf(texture), 0);
1065 sfsGCDisable(); // temporary disable removing of temporary volumes
1066 try
1067 // Çàãðóçêà WAD:
1068 FileName := g_ExtractWadName(Res);
1069 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1070 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1072 WAD := TWADFile.Create();
1073 if not WAD.ReadFile(FileName) then
1074 begin
1075 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1076 WAD.Free();
1077 Exit;
1078 end;
1079 //k8: why loader ignores path here?
1080 mapResName := g_ExtractFileName(Res);
1081 if not WAD.GetMapResource(mapResName, Data, Len) then
1082 begin
1083 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1084 WAD.Free();
1085 Exit;
1086 end;
1088 WAD.Free();
1090 // Çàãðóçêà êàðòû:
1091 e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
1092 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1093 MapReader := TMapReader_1.Create();
1095 if not MapReader.LoadMap(Data) then
1096 begin
1097 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1098 FreeMem(Data);
1099 MapReader.Free();
1100 Exit;
1101 end;
1103 FreeMem(Data);
1104 generateExternalResourcesList(MapReader);
1105 // Çàãðóçêà òåêñòóð:
1106 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1107 _textures := MapReader.GetTextures();
1108 _texnummap := nil;
1110 // Äîáàâëåíèå òåêñòóð â Textures[]:
1111 if _textures <> nil then
1112 begin
1113 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1114 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
1115 SetLength(_texnummap, length(_textures));
1117 for a := 0 to High(_textures) do
1118 begin
1119 SetLength(s, 64);
1120 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
1121 for b := 1 to Length(s) do
1122 if s[b] = #0 then
1123 begin
1124 SetLength(s, b-1);
1125 Break;
1126 end;
1127 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1128 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1129 // Àíèìèðîâàííàÿ òåêñòóðà:
1130 if ByteBool(_textures[a].Anim) then
1131 begin
1132 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1133 if ntn < 0 then
1134 begin
1135 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1136 ntn := CreateNullTexture(_textures[a].Resource);
1137 end;
1138 end
1139 else // Îáû÷íàÿ òåêñòóðà:
1140 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1141 if ntn < 0 then
1142 begin
1143 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1144 ntn := CreateNullTexture(_textures[a].Resource);
1145 end;
1147 _texnummap[a] := ntn; // fix texture number
1148 g_Game_StepLoading();
1149 end;
1150 end;
1152 // Çàãðóçêà òðèããåðîâ:
1153 gTriggerClientID := 0;
1154 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1155 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1156 triggers := MapReader.GetTriggers();
1158 // Çàãðóçêà ïàíåëåé:
1159 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1160 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1161 panels := MapReader.GetPanels();
1163 // check texture numbers for panels
1164 for a := 0 to High(panels) do
1165 begin
1166 if panels[a].TextureNum > High(_textures) then
1167 begin
1168 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1169 result := false;
1170 exit;
1171 end;
1172 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1173 end;
1175 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1176 if triggers <> nil then
1177 begin
1178 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1179 SetLength(TriggersTable, Length(triggers));
1180 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1182 for a := 0 to High(TriggersTable) do
1183 begin
1184 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1185 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1186 // Ëèôòû:
1187 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1188 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1189 else
1190 TriggersTable[a].LiftPanel := -1;
1191 // Äâåðè:
1192 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1193 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1194 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1195 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1196 else
1197 TriggersTable[a].DoorPanel := -1;
1198 // Òóðåëü:
1199 if triggers[a].TriggerType = TRIGGER_SHOT then
1200 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1201 else
1202 TriggersTable[a].ShotPanel := -1;
1204 g_Game_StepLoading();
1205 end;
1206 end;
1208 // Ñîçäàåì ïàíåëè:
1209 if panels <> nil then
1210 begin
1211 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1212 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1214 for a := 0 to High(panels) do
1215 begin
1216 SetLength(AddTextures, 0);
1217 trigRef := False;
1218 CurTex := -1;
1219 if _textures <> nil then
1220 begin
1221 texture := _textures[panels[a].TextureNum];
1222 ok := True;
1223 end
1224 else
1225 ok := False;
1227 if ok then
1228 begin
1229 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1230 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1231 ok := False;
1232 if (TriggersTable <> nil) and (_textures <> nil) then
1233 for b := 0 to High(TriggersTable) do
1234 if (TriggersTable[b].TexturePanel = a)
1235 or (TriggersTable[b].ShotPanel = a) then
1236 begin
1237 trigRef := True;
1238 ok := True;
1239 Break;
1240 end;
1241 end;
1243 if ok then
1244 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1245 SetLength(s, 64);
1246 CopyMemory(@s[1], @texture.Resource[0], 64);
1247 // Èçìåðÿåì äëèíó:
1248 Len := Length(s);
1249 for c := Len downto 1 do
1250 if s[c] <> #0 then
1251 begin
1252 Len := c;
1253 Break;
1254 end;
1255 SetLength(s, Len);
1257 // Ñïåö-òåêñòóðû çàïðåùåíû:
1258 if g_Map_IsSpecialTexture(s) then
1259 ok := False
1260 else
1261 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1262 ok := g_Texture_NumNameFindStart(s);
1264 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1265 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1266 if ok then
1267 begin
1268 k := NNF_NAME_BEFORE;
1269 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1270 while ok or (k = NNF_NAME_BEFORE) or
1271 (k = NNF_NAME_EQUALS) do
1272 begin
1273 k := g_Texture_NumNameFindNext(TexName);
1275 if (k = NNF_NAME_BEFORE) or
1276 (k = NNF_NAME_AFTER) then
1277 begin
1278 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1279 if ByteBool(texture.Anim) then
1280 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1281 isAnim := True;
1282 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1283 if not ok then
1284 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1285 isAnim := False;
1286 ok := CreateTexture(TexName, FileName, False) >= 0;
1287 end;
1288 end
1289 else
1290 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1291 isAnim := False;
1292 ok := CreateTexture(TexName, FileName, False) >= 0;
1293 if not ok then
1294 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1295 isAnim := True;
1296 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1297 end;
1298 end;
1300 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1301 if ok then
1302 begin
1303 for c := 0 to High(Textures) do
1304 if Textures[c].TextureName = TexName then
1305 begin
1306 SetLength(AddTextures, Length(AddTextures)+1);
1307 AddTextures[High(AddTextures)].Texture := c;
1308 AddTextures[High(AddTextures)].Anim := isAnim;
1309 Break;
1310 end;
1311 end;
1312 end
1313 else
1314 if k = NNF_NAME_EQUALS then
1315 begin
1316 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1317 SetLength(AddTextures, Length(AddTextures)+1);
1318 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1319 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1320 CurTex := High(AddTextures);
1321 ok := True;
1322 end
1323 else // NNF_NO_NAME
1324 ok := False;
1325 end; // while ok...
1327 ok := True;
1328 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1329 end; // if ok - ññûëàþòñÿ òðèããåðû
1331 if not ok then
1332 begin
1333 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1334 SetLength(AddTextures, 1);
1335 AddTextures[0].Texture := panels[a].TextureNum;
1336 AddTextures[0].Anim := ByteBool(texture.Anim);
1337 CurTex := 0;
1338 end;
1340 //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);
1342 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1343 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1345 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1346 if TriggersTable <> nil then
1347 for b := 0 to High(TriggersTable) do
1348 begin
1349 // Òðèããåð äâåðè/ëèôòà:
1350 if (TriggersTable[b].LiftPanel = a) or
1351 (TriggersTable[b].DoorPanel = a) then
1352 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1353 // Òðèããåð ñìåíû òåêñòóðû:
1354 if TriggersTable[b].TexturePanel = a then
1355 triggers[b].TexturePanel := PanelID;
1356 // Òðèããåð "Òóðåëü":
1357 if TriggersTable[b].ShotPanel = a then
1358 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1359 end;
1361 g_Game_StepLoading();
1362 end;
1363 end;
1365 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1366 if (triggers <> nil) and not gLoadGameMode then
1367 begin
1368 e_WriteLog(' Creating triggers...', MSG_NOTIFY);
1369 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1370 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1371 for a := 0 to High(triggers) do
1372 begin
1373 if triggers[a].TexturePanel <> -1 then
1374 b := panels[TriggersTable[a].TexturePanel].PanelType
1375 else
1376 b := 0;
1377 if (triggers[a].TriggerType = TRIGGER_SHOT) and
1378 (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1379 c := panels[TriggersTable[a].ShotPanel].PanelType
1380 else
1381 c := 0;
1382 CreateTrigger(triggers[a], b, c);
1383 end;
1384 end;
1386 // Çàãðóçêà ïðåäìåòîâ:
1387 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1388 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1389 items := MapReader.GetItems();
1391 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1392 if (items <> nil) and not gLoadGameMode then
1393 begin
1394 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1395 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1396 for a := 0 to High(items) do
1397 CreateItem(Items[a]);
1398 end;
1400 // Çàãðóçêà îáëàñòåé:
1401 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1402 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1403 areas := MapReader.GetAreas();
1405 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1406 if areas <> nil then
1407 begin
1408 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1409 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1410 for a := 0 to High(areas) do
1411 CreateArea(areas[a]);
1412 end;
1414 // Çàãðóçêà ìîíñòðîâ:
1415 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1416 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1417 monsters := MapReader.GetMonsters();
1419 gTotalMonsters := 0;
1421 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1422 if (monsters <> nil) and not gLoadGameMode then
1423 begin
1424 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1425 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1426 for a := 0 to High(monsters) do
1427 CreateMonster(monsters[a]);
1428 end;
1430 // Çàãðóçêà îïèñàíèÿ êàðòû:
1431 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1432 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1433 Header := MapReader.GetMapHeader();
1435 MapReader.Free();
1437 with gMapInfo do
1438 begin
1439 Name := Header.MapName;
1440 Description := Header.MapDescription;
1441 Author := Header.MapAuthor;
1442 MusicName := Header.MusicName;
1443 SkyName := Header.SkyName;
1444 Height := Header.Height;
1445 Width := Header.Width;
1446 end;
1448 // Çàãðóçêà íåáà:
1449 if gMapInfo.SkyName <> '' then
1450 begin
1451 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1452 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1453 FileName := g_ExtractWadName(gMapInfo.SkyName);
1455 if FileName <> '' then
1456 FileName := GameDir+'/wads/'+FileName
1457 else
1458 begin
1459 FileName := g_ExtractWadName(Res);
1460 end;
1462 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1463 if g_Texture_CreateWAD(BackID, s) then
1464 begin
1465 g_Game_SetupScreenSize();
1466 end
1467 else
1468 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1469 end;
1471 // Çàãðóçêà ìóçûêè:
1472 ok := False;
1473 if gMapInfo.MusicName <> '' then
1474 begin
1475 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1476 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1477 FileName := g_ExtractWadName(gMapInfo.MusicName);
1479 if FileName <> '' then
1480 FileName := GameDir+'/wads/'+FileName
1481 else
1482 begin
1483 FileName := g_ExtractWadName(Res);
1484 end;
1486 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1487 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1488 ok := True
1489 else
1490 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1491 end;
1493 // Îñòàëüíûå óñòàíâêè:
1494 CreateDoorMap();
1495 CreateLiftMap();
1497 g_Items_Init();
1498 g_Weapon_Init();
1499 g_Monsters_Init();
1501 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1502 if not gLoadGameMode then
1503 g_GFX_Init();
1505 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1506 _textures := nil;
1507 panels := nil;
1508 items := nil;
1509 areas := nil;
1510 triggers := nil;
1511 TriggersTable := nil;
1512 AddTextures := nil;
1514 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
1515 if ok and (not gLoadGameMode) then
1516 begin
1517 gMusic.SetByName(gMapInfo.MusicName);
1518 gMusic.Play();
1519 end
1520 else
1521 gMusic.SetByName('');
1522 finally
1523 sfsGCEnable(); // enable releasing unused volumes
1524 end;
1526 e_WriteLog('Creating map grid', MSG_NOTIFY);
1527 mapCreateGrid();
1529 e_WriteLog('Done loading map.', MSG_NOTIFY);
1530 Result := True;
1531 end;
1533 function g_Map_GetMapInfo(Res: String): TMapInfo;
1534 var
1535 WAD: TWADFile;
1536 MapReader: TMapReader_1;
1537 Header: TMapHeaderRec_1;
1538 FileName: String;
1539 Data: Pointer;
1540 Len: Integer;
1541 begin
1542 FillChar(Result, SizeOf(Result), 0);
1543 FileName := g_ExtractWadName(Res);
1545 WAD := TWADFile.Create();
1546 if not WAD.ReadFile(FileName) then
1547 begin
1548 WAD.Free();
1549 Exit;
1550 end;
1552 //k8: it ignores path again
1553 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
1554 begin
1555 WAD.Free();
1556 Exit;
1557 end;
1559 WAD.Free();
1561 MapReader := TMapReader_1.Create();
1563 if not MapReader.LoadMap(Data) then
1564 begin
1565 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
1566 ZeroMemory(@Header, SizeOf(Header));
1567 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
1568 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
1569 end
1570 else
1571 begin
1572 Header := MapReader.GetMapHeader();
1573 Result.Name := Header.MapName;
1574 Result.Description := Header.MapDescription;
1575 end;
1577 FreeMem(Data);
1578 MapReader.Free();
1580 Result.Map := Res;
1581 Result.Author := Header.MapAuthor;
1582 Result.Height := Header.Height;
1583 Result.Width := Header.Width;
1584 end;
1586 function g_Map_GetMapsList(WADName: string): SArray;
1587 var
1588 WAD: TWADFile;
1589 a: Integer;
1590 ResList: SArray;
1591 begin
1592 Result := nil;
1593 WAD := TWADFile.Create();
1594 if not WAD.ReadFile(WADName) then
1595 begin
1596 WAD.Free();
1597 Exit;
1598 end;
1599 ResList := WAD.GetMapResources();
1600 if ResList <> nil then
1601 begin
1602 for a := 0 to High(ResList) do
1603 begin
1604 SetLength(Result, Length(Result)+1);
1605 Result[High(Result)] := ResList[a];
1606 end;
1607 end;
1608 WAD.Free();
1609 end;
1611 function g_Map_Exist(Res: string): Boolean;
1612 var
1613 WAD: TWADFile;
1614 FileName, mnn: string;
1615 ResList: SArray;
1616 a: Integer;
1617 begin
1618 Result := False;
1620 FileName := addWadExtension(g_ExtractWadName(Res));
1622 WAD := TWADFile.Create;
1623 if not WAD.ReadFile(FileName) then
1624 begin
1625 WAD.Free();
1626 Exit;
1627 end;
1629 ResList := WAD.GetMapResources();
1630 WAD.Free();
1632 mnn := g_ExtractFileName(Res);
1633 if ResList <> nil then
1634 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
1635 begin
1636 Result := True;
1637 Exit;
1638 end;
1639 end;
1641 procedure g_Map_Free();
1642 var
1643 a: Integer;
1645 procedure FreePanelArray(var panels: TPanelArray);
1646 var
1647 i: Integer;
1649 begin
1650 if panels <> nil then
1651 begin
1652 for i := 0 to High(panels) do
1653 panels[i].Free();
1654 panels := nil;
1655 end;
1656 end;
1658 begin
1659 g_GFX_Free();
1660 g_Weapon_Free();
1661 g_Items_Free();
1662 g_Triggers_Free();
1663 g_Monsters_Free();
1665 RespawnPoints := nil;
1666 if FlagPoints[FLAG_RED] <> nil then
1667 begin
1668 Dispose(FlagPoints[FLAG_RED]);
1669 FlagPoints[FLAG_RED] := nil;
1670 end;
1671 if FlagPoints[FLAG_BLUE] <> nil then
1672 begin
1673 Dispose(FlagPoints[FLAG_BLUE]);
1674 FlagPoints[FLAG_BLUE] := nil;
1675 end;
1676 //DOMFlagPoints := nil;
1678 //gDOMFlags := nil;
1680 if Textures <> nil then
1681 begin
1682 for a := 0 to High(Textures) do
1683 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
1684 if Textures[a].Anim then
1685 g_Frames_DeleteByID(Textures[a].FramesID)
1686 else
1687 if Textures[a].TextureID <> TEXTURE_NONE then
1688 e_DeleteTexture(Textures[a].TextureID);
1690 Textures := nil;
1691 end;
1693 FreePanelArray(gWalls);
1694 FreePanelArray(gRenderBackgrounds);
1695 FreePanelArray(gRenderForegrounds);
1696 FreePanelArray(gWater);
1697 FreePanelArray(gAcid1);
1698 FreePanelArray(gAcid2);
1699 FreePanelArray(gSteps);
1700 FreePanelArray(gLifts);
1701 FreePanelArray(gBlockMon);
1703 if BackID <> DWORD(-1) then
1704 begin
1705 gBackSize.X := 0;
1706 gBackSize.Y := 0;
1707 e_DeleteTexture(BackID);
1708 BackID := DWORD(-1);
1709 end;
1711 g_Game_StopAllSounds(False);
1712 gMusic.FreeSound();
1713 g_Sound_Delete(gMapInfo.MusicName);
1715 gMapInfo.Name := '';
1716 gMapInfo.Description := '';
1717 gMapInfo.MusicName := '';
1718 gMapInfo.Height := 0;
1719 gMapInfo.Width := 0;
1721 gDoorMap := nil;
1722 gLiftMap := nil;
1724 PanelByID := nil;
1725 end;
1727 procedure g_Map_Update();
1728 var
1729 a, d, j: Integer;
1730 m: Word;
1731 s: String;
1733 procedure UpdatePanelArray(var panels: TPanelArray);
1734 var
1735 i: Integer;
1737 begin
1738 if panels <> nil then
1739 for i := 0 to High(panels) do
1740 panels[i].Update();
1741 end;
1743 begin
1744 UpdatePanelArray(gWalls);
1745 UpdatePanelArray(gRenderBackgrounds);
1746 UpdatePanelArray(gRenderForegrounds);
1747 UpdatePanelArray(gWater);
1748 UpdatePanelArray(gAcid1);
1749 UpdatePanelArray(gAcid2);
1750 UpdatePanelArray(gSteps);
1752 if gGameSettings.GameMode = GM_CTF then
1753 begin
1754 for a := FLAG_RED to FLAG_BLUE do
1755 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
1756 with gFlags[a] do
1757 begin
1758 if gFlags[a].Animation <> nil then
1759 gFlags[a].Animation.Update();
1761 m := g_Obj_Move(@Obj, True, True);
1763 if gTime mod (GAME_TICK*2) <> 0 then
1764 Continue;
1766 // Ñîïðîòèâëåíèå âîçäóõà:
1767 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
1769 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
1770 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
1771 begin
1772 g_Map_ResetFlag(a);
1773 gFlags[a].CaptureTime := 0;
1774 if a = FLAG_RED then
1775 s := _lc[I_PLAYER_FLAG_RED]
1776 else
1777 s := _lc[I_PLAYER_FLAG_BLUE];
1778 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1780 if g_Game_IsNet then
1781 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
1782 Continue;
1783 end;
1785 if Count > 0 then
1786 Count := Count - 1;
1788 // Èãðîê áåðåò ôëàã:
1789 if gPlayers <> nil then
1790 begin
1791 j := Random(Length(gPlayers)) - 1;
1793 for d := 0 to High(gPlayers) do
1794 begin
1795 Inc(j);
1796 if j > High(gPlayers) then
1797 j := 0;
1799 if gPlayers[j] <> nil then
1800 if gPlayers[j].Live and
1801 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
1802 begin
1803 if gPlayers[j].GetFlag(a) then
1804 Break;
1805 end;
1806 end;
1807 end;
1808 end;
1809 end;
1810 end;
1813 procedure g_Map_DrawPanelsOld(PanelType: Word);
1815 procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
1816 var
1817 idx: Integer;
1818 begin
1819 if (panels <> nil) and (stp >= 0) and (stp <= 6) then
1820 begin
1821 // alas, no visible set
1822 for idx := 0 to High(panels) do
1823 begin
1824 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
1825 end;
1826 end;
1827 end;
1829 begin
1830 case PanelType of
1831 PANEL_WALL: DrawPanels(0, gWalls);
1832 PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True);
1833 PANEL_BACK: DrawPanels(1, gRenderBackgrounds);
1834 PANEL_FORE: DrawPanels(2, gRenderForegrounds);
1835 PANEL_WATER: DrawPanels(3, gWater);
1836 PANEL_ACID1: DrawPanels(4, gAcid1);
1837 PANEL_ACID2: DrawPanels(5, gAcid2);
1838 PANEL_STEP: DrawPanels(6, gSteps);
1839 end;
1840 end;
1843 var
1844 gDrawPanelList: TBinaryHeapObj = nil;
1846 function dplLess (a, b: TObject): Boolean;
1847 begin
1848 result := ((a as TPanel).ArrIdx < (b as TPanel).ArrIdx);
1849 end;
1851 procedure dplClear ();
1852 begin
1853 if gDrawPanelList = nil then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
1854 end;
1856 procedure dplAddPanel (pan: TPanel);
1857 begin
1858 if pan = nil then exit;
1859 gDrawPanelList.insert(pan);
1860 end;
1863 procedure g_Map_DrawPanels(x0, y0, wdt, hgt: Integer; PanelType: Word);
1864 var
1865 ptag: Integer;
1867 function checker (obj: TObject; tag: Integer): Boolean;
1868 var
1869 pan: TPanel;
1870 begin
1871 result := false; // don't stop, ever
1872 if (tag <> ptag) then exit;
1873 //e_WriteLog(Format(' *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
1875 if obj = nil then begin e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end;
1876 if not (obj is TPanel) then begin e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end;
1877 //pan := (obj as TPanel);
1878 //e_WriteLog(Format(' !body: (%d,%d)-(%dx%d) tag:%d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, tag, PanelType]), MSG_NOTIFY);
1880 pan := (obj as TPanel);
1881 if (PanelType = PANEL_CLOSEDOOR) then begin if not pan.Door then exit; end else begin if pan.Door then exit; end;
1882 //e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d) tag: %d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, tag, PanelType]), MSG_NOTIFY);
1883 dplAddPanel(pan);
1884 end;
1886 procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
1887 var
1888 idx: Integer;
1889 pan: TPanel;
1890 begin
1891 if (panels <> nil) and (stp >= 0) and (stp <= 6) then
1892 begin
1893 // alas, no visible set
1894 for idx := 0 to High(panels) do
1895 begin
1896 if not (drawDoors xor panels[idx].Door) then
1897 begin
1898 pan := panels[idx];
1900 if (pan.Width < 1) or (pan.Height < 1) then continue;
1901 if (pan.X+pan.Width <= x0) or (pan.Y+pan.Height <= y0) then continue;
1902 if (pan.X >= x0+wdt) or (pan.Y >= y0+hgt) then continue;
1904 pan.Draw();
1905 //e_WriteLog(Format(' *body hit: (%d,%d)-(%dx%d) tag: %d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, PanelType, PanelType]), MSG_NOTIFY);
1906 end;
1907 end;
1908 end;
1909 end;
1911 begin
1912 //g_Map_DrawPanelsOld(PanelType); exit;
1913 //e_WriteLog('==================', MSG_NOTIFY);
1914 //e_WriteLog(Format('***QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
1915 dplClear();
1916 ptag := panelTypeToTag(PanelType);
1918 if gdbg_map_use_grid_render then
1919 begin
1920 gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker);
1921 // sort and draw the list (we need to sort it, or rendering is fucked)
1922 while gDrawPanelList.count > 0 do
1923 begin
1924 (gDrawPanelList.front() as TPanel).Draw();
1925 gDrawPanelList.popFront();
1926 end;
1927 end
1928 else
1929 begin
1930 //e_WriteLog(Format('+++QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
1931 case PanelType of
1932 PANEL_WALL: DrawPanels(0, gWalls);
1933 PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True);
1934 PANEL_BACK: DrawPanels(1, gRenderBackgrounds);
1935 PANEL_FORE: DrawPanels(2, gRenderForegrounds);
1936 PANEL_WATER: DrawPanels(3, gWater);
1937 PANEL_ACID1: DrawPanels(4, gAcid1);
1938 PANEL_ACID2: DrawPanels(5, gAcid2);
1939 PANEL_STEP: DrawPanels(6, gSteps);
1940 end;
1941 end;
1943 //e_WriteLog('==================', MSG_NOTIFY);
1944 end;
1947 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
1948 function checker (obj: TObject; tag: Integer): Boolean;
1949 var
1950 pan: TPanel;
1951 begin
1952 result := false; // don't stop, ever
1953 if (tag <> GridTagWallDoor) then exit; // only walls
1954 pan := (obj as TPanel);
1955 pan.DrawShadowVolume(lightX, lightY, radius);
1956 end;
1958 begin
1959 gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker);
1960 end;
1963 procedure g_Map_DrawBack(dx, dy: Integer);
1964 begin
1965 if gDrawBackGround and (BackID <> DWORD(-1)) then
1966 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
1967 else
1968 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
1969 end;
1971 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
1972 PanelType: Word; b1x3: Boolean): Boolean;
1973 var
1974 a, h: Integer;
1975 begin
1976 Result := False;
1978 if WordBool(PanelType and PANEL_WALL) then
1979 if gWalls <> nil then
1980 begin
1981 h := High(gWalls);
1983 for a := 0 to h do
1984 if gWalls[a].Enabled and
1985 g_Collide(X, Y, Width, Height,
1986 gWalls[a].X, gWalls[a].Y,
1987 gWalls[a].Width, gWalls[a].Height) then
1988 begin
1989 Result := True;
1990 Exit;
1991 end;
1992 end;
1994 if WordBool(PanelType and PANEL_WATER) then
1995 if gWater <> nil then
1996 begin
1997 h := High(gWater);
1999 for a := 0 to h do
2000 if g_Collide(X, Y, Width, Height,
2001 gWater[a].X, gWater[a].Y,
2002 gWater[a].Width, gWater[a].Height) then
2003 begin
2004 Result := True;
2005 Exit;
2006 end;
2007 end;
2009 if WordBool(PanelType and PANEL_ACID1) then
2010 if gAcid1 <> nil then
2011 begin
2012 h := High(gAcid1);
2014 for a := 0 to h do
2015 if g_Collide(X, Y, Width, Height,
2016 gAcid1[a].X, gAcid1[a].Y,
2017 gAcid1[a].Width, gAcid1[a].Height) then
2018 begin
2019 Result := True;
2020 Exit;
2021 end;
2022 end;
2024 if WordBool(PanelType and PANEL_ACID2) then
2025 if gAcid2 <> nil then
2026 begin
2027 h := High(gAcid2);
2029 for a := 0 to h do
2030 if g_Collide(X, Y, Width, Height,
2031 gAcid2[a].X, gAcid2[a].Y,
2032 gAcid2[a].Width, gAcid2[a].Height) then
2033 begin
2034 Result := True;
2035 Exit;
2036 end;
2037 end;
2039 if WordBool(PanelType and PANEL_STEP) then
2040 if gSteps <> nil then
2041 begin
2042 h := High(gSteps);
2044 for a := 0 to h do
2045 if g_Collide(X, Y, Width, Height,
2046 gSteps[a].X, gSteps[a].Y,
2047 gSteps[a].Width, gSteps[a].Height) then
2048 begin
2049 Result := True;
2050 Exit;
2051 end;
2052 end;
2054 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2055 if gLifts <> nil then
2056 begin
2057 h := High(gLifts);
2059 for a := 0 to h do
2060 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2061 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2062 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2063 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2064 g_Collide(X, Y, Width, Height,
2065 gLifts[a].X, gLifts[a].Y,
2066 gLifts[a].Width, gLifts[a].Height) then
2067 begin
2068 Result := True;
2069 Exit;
2070 end;
2071 end;
2073 if WordBool(PanelType and PANEL_BLOCKMON) then
2074 if gBlockMon <> nil then
2075 begin
2076 h := High(gBlockMon);
2078 for a := 0 to h do
2079 if ( (not b1x3) or
2080 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2081 g_Collide(X, Y, Width, Height,
2082 gBlockMon[a].X, gBlockMon[a].Y,
2083 gBlockMon[a].Width, gBlockMon[a].Height) then
2084 begin
2085 Result := True;
2086 Exit;
2087 end;
2088 end;
2089 end;
2091 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2092 var
2093 texid: DWORD;
2095 function checkPanels (var panels: TPanelArray): Boolean;
2096 var
2097 a: Integer;
2098 begin
2099 result := false;
2100 if panels = nil then exit;
2101 for a := 0 to High(panels) do
2102 begin
2103 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2104 begin
2105 result := true;
2106 texid := panels[a].GetTextureID();
2107 exit;
2108 end;
2109 end;
2110 end;
2112 begin
2113 texid := TEXTURE_NONE;
2114 result := texid;
2115 if not checkPanels(gWater) then
2116 if not checkPanels(gAcid1) then
2117 if not checkPanels(gAcid2) then exit;
2118 result := texid;
2119 end;
2122 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2124 function checker (obj: TObject; tag: Integer): Boolean;
2125 var
2126 pan: TPanel;
2127 a: Integer;
2128 begin
2129 result := false; // don't stop, ever
2131 //e_WriteLog(Format(' *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2133 if obj = nil then
2134 begin
2135 e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2136 end;
2137 if not (obj is TPanel) then
2138 begin
2139 e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2140 exit;
2141 end;
2143 pan := (obj as TPanel);
2144 a := pan.ArrIdx;
2146 if WordBool(PanelType and PANEL_WALL) and (tag = GridTagWallDoor) then
2147 begin
2148 if gWalls[a].Enabled and g_Collide(X, Y, Width, Height, gWalls[a].X, gWalls[a].Y, gWalls[a].Width, gWalls[a].Height) then
2149 begin
2150 result := true;
2151 exit;
2152 end;
2153 end;
2155 if WordBool(PanelType and PANEL_WATER) and (tag = GridTagWater) then
2156 begin
2157 if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then
2158 begin
2159 result := True;
2160 exit;
2161 end;
2162 end;
2164 if WordBool(PanelType and PANEL_ACID1) and (tag = GridTagAcid1) then
2165 begin
2166 if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then
2167 begin
2168 result := True;
2169 exit;
2170 end;
2171 end;
2173 if WordBool(PanelType and PANEL_ACID2) and (tag = GridTagAcid2) then
2174 begin
2175 if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then
2176 begin
2177 result := True;
2178 exit;
2179 end;
2180 end;
2182 if WordBool(PanelType and PANEL_STEP) and (tag = GridTagStep) then
2183 begin
2184 if g_Collide(X, Y, Width, Height, gSteps[a].X, gSteps[a].Y, gSteps[a].Width, gSteps[a].Height) then
2185 begin
2186 result := True;
2187 exit;
2188 end;
2189 end;
2191 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) and (tag = GridTagLift) then
2192 begin
2193 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2194 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2195 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2196 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2197 g_Collide(X, Y, Width, Height, gLifts[a].X, gLifts[a].Y, gLifts[a].Width, gLifts[a].Height) then
2198 begin
2199 result := true;
2200 exit;
2201 end;
2202 end;
2204 if WordBool(PanelType and PANEL_BLOCKMON) and (tag = GridTagBlockMon) then
2205 begin
2206 if ((not b1x3) or ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64)) and
2207 g_Collide(X, Y, Width, Height, gBlockMon[a].X, gBlockMon[a].Y, gBlockMon[a].Width, gBlockMon[a].Height) then
2208 begin
2209 result := True;
2210 exit;
2211 end;
2212 end;
2213 end;
2215 begin
2216 //TODO: detailed profile
2217 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('wall coldet');
2218 try
2219 if gdbg_map_use_grid_coldet then
2220 begin
2221 result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker);
2222 end
2223 else
2224 begin
2225 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2226 end;
2227 finally
2228 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2229 end;
2230 end;
2233 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2234 var
2235 cctype: Integer = 3; // priority: 0: water, 1: acid1, 2: acid2; 3: others (nothing)
2236 texid: DWORD;
2238 // slightly different from the old code, but meh...
2239 function checker (obj: TObject; tag: Integer): Boolean;
2240 var
2241 pan: TPanel;
2242 a: Integer;
2243 begin
2244 result := false; // don't stop, ever
2245 pan := (obj as TPanel);
2246 a := pan.ArrIdx;
2247 // water
2248 if (tag = GridTagWater) then
2249 begin
2250 if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then
2251 begin
2252 result := true; // water has highest priority, so stop right here
2253 texid := gWater[a].GetTextureID();
2254 exit;
2255 end;
2256 end;
2257 // acid1
2258 if (cctype > 1) and (tag = GridTagAcid1) then
2259 begin
2260 if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then
2261 begin
2262 cctype := 1;
2263 texid := gAcid1[a].GetTextureID();
2264 exit;
2265 end;
2266 end;
2267 // acid2
2268 if (cctype > 2) and (tag = GridTagAcid2) then
2269 begin
2270 if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then
2271 begin
2272 cctype := 2;
2273 texid := gAcid2[a].GetTextureID();
2274 exit;
2275 end;
2276 end;
2277 end;
2279 begin
2280 //TODO: detailed profile?
2281 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquid coldet');
2282 try
2283 if gdbg_map_use_grid_coldet then
2284 begin
2285 texid := TEXTURE_NONE;
2286 gMapGrid.forEachInAABB(X, Y, Width, Height, checker);
2287 result := texid;
2288 end
2289 else
2290 begin
2291 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2292 end;
2293 finally
2294 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2295 end;
2296 end;
2298 procedure g_Map_EnableWall(ID: DWORD);
2299 begin
2300 with gWalls[ID] do
2301 begin
2302 Enabled := True;
2303 g_Mark(X, Y, Width, Height, MARK_DOOR, True);
2305 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2306 end;
2307 end;
2309 procedure g_Map_DisableWall(ID: DWORD);
2310 begin
2311 with gWalls[ID] do
2312 begin
2313 Enabled := False;
2314 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
2316 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2317 end;
2318 end;
2320 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2321 var
2322 tp: TPanel;
2323 begin
2324 case PanelType of
2325 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2326 tp := gWalls[ID];
2327 PANEL_FORE:
2328 tp := gRenderForegrounds[ID];
2329 PANEL_BACK:
2330 tp := gRenderBackgrounds[ID];
2331 PANEL_WATER:
2332 tp := gWater[ID];
2333 PANEL_ACID1:
2334 tp := gAcid1[ID];
2335 PANEL_ACID2:
2336 tp := gAcid2[ID];
2337 PANEL_STEP:
2338 tp := gSteps[ID];
2339 else
2340 Exit;
2341 end;
2343 tp.NextTexture(AnimLoop);
2344 if g_Game_IsServer and g_Game_IsNet then
2345 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2346 end;
2348 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2349 begin
2350 if gLifts[ID].LiftType = t then
2351 Exit;
2353 with gLifts[ID] do
2354 begin
2355 LiftType := t;
2357 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2359 if LiftType = 0 then
2360 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2361 else if LiftType = 1 then
2362 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2363 else if LiftType = 2 then
2364 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2365 else if LiftType = 3 then
2366 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2368 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2369 end;
2370 end;
2372 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2373 var
2374 a: Integer;
2375 PointsArray: Array of TRespawnPoint;
2376 begin
2377 Result := False;
2378 SetLength(PointsArray, 0);
2380 if RespawnPoints = nil then
2381 Exit;
2383 for a := 0 to High(RespawnPoints) do
2384 if RespawnPoints[a].PointType = PointType then
2385 begin
2386 SetLength(PointsArray, Length(PointsArray)+1);
2387 PointsArray[High(PointsArray)] := RespawnPoints[a];
2388 end;
2390 if PointsArray = nil then
2391 Exit;
2393 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2394 Result := True;
2395 end;
2397 function g_Map_GetPointCount(PointType: Byte): Word;
2398 var
2399 a: Integer;
2400 begin
2401 Result := 0;
2403 if RespawnPoints = nil then
2404 Exit;
2406 for a := 0 to High(RespawnPoints) do
2407 if RespawnPoints[a].PointType = PointType then
2408 Result := Result + 1;
2409 end;
2411 function g_Map_HaveFlagPoints(): Boolean;
2412 begin
2413 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2414 end;
2416 procedure g_Map_ResetFlag(Flag: Byte);
2417 begin
2418 with gFlags[Flag] do
2419 begin
2420 Obj.X := -1000;
2421 Obj.Y := -1000;
2422 Obj.Vel.X := 0;
2423 Obj.Vel.Y := 0;
2424 Direction := D_LEFT;
2425 State := FLAG_STATE_NONE;
2426 if FlagPoints[Flag] <> nil then
2427 begin
2428 Obj.X := FlagPoints[Flag]^.X;
2429 Obj.Y := FlagPoints[Flag]^.Y;
2430 Direction := FlagPoints[Flag]^.Direction;
2431 State := FLAG_STATE_NORMAL;
2432 end;
2433 Count := -1;
2434 end;
2435 end;
2437 procedure g_Map_DrawFlags();
2438 var
2439 i, dx: Integer;
2440 Mirror: TMirrorType;
2441 begin
2442 if gGameSettings.GameMode <> GM_CTF then
2443 Exit;
2445 for i := FLAG_RED to FLAG_BLUE do
2446 with gFlags[i] do
2447 if State <> FLAG_STATE_CAPTURED then
2448 begin
2449 if State = FLAG_STATE_NONE then
2450 continue;
2452 if Direction = D_LEFT then
2453 begin
2454 Mirror := M_HORIZONTAL;
2455 dx := -1;
2456 end
2457 else
2458 begin
2459 Mirror := M_NONE;
2460 dx := 1;
2461 end;
2463 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2465 if g_debug_Frames then
2466 begin
2467 e_DrawQuad(Obj.X+Obj.Rect.X,
2468 Obj.Y+Obj.Rect.Y,
2469 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2470 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2471 0, 255, 0);
2472 end;
2473 end;
2474 end;
2476 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2477 var
2478 dw: DWORD;
2479 b: Byte;
2480 str: String;
2481 boo: Boolean;
2483 procedure SavePanelArray(var panels: TPanelArray);
2484 var
2485 PAMem: TBinMemoryWriter;
2486 i: Integer;
2487 begin
2488 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2489 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2491 i := 0;
2492 while i < Length(panels) do
2493 begin
2494 if panels[i].SaveIt then
2495 begin
2496 // ID ïàíåëè:
2497 PAMem.WriteInt(i);
2498 // Ñîõðàíÿåì ïàíåëü:
2499 panels[i].SaveState(PAMem);
2500 end;
2501 Inc(i);
2502 end;
2504 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2505 PAMem.SaveToMemory(Mem);
2506 PAMem.Free();
2507 end;
2509 procedure SaveFlag(flag: PFlag);
2510 begin
2511 // Ñèãíàòóðà ôëàãà:
2512 dw := FLAG_SIGNATURE; // 'FLAG'
2513 Mem.WriteDWORD(dw);
2514 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2515 Mem.WriteByte(flag^.RespawnType);
2516 // Ñîñòîÿíèå ôëàãà:
2517 Mem.WriteByte(flag^.State);
2518 // Íàïðàâëåíèå ôëàãà:
2519 if flag^.Direction = D_LEFT then
2520 b := 1
2521 else // D_RIGHT
2522 b := 2;
2523 Mem.WriteByte(b);
2524 // Îáúåêò ôëàãà:
2525 Obj_SaveState(@flag^.Obj, Mem);
2526 end;
2528 begin
2529 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2531 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2532 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2533 SavePanelArray(gWalls);
2534 // Ñîõðàíÿåì ïàíåëè ôîíà:
2535 SavePanelArray(gRenderBackgrounds);
2536 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2537 SavePanelArray(gRenderForegrounds);
2538 // Ñîõðàíÿåì ïàíåëè âîäû:
2539 SavePanelArray(gWater);
2540 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2541 SavePanelArray(gAcid1);
2542 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2543 SavePanelArray(gAcid2);
2544 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2545 SavePanelArray(gSteps);
2546 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2547 SavePanelArray(gLifts);
2548 ///// /////
2550 ///// Ñîõðàíÿåì ìóçûêó: /////
2551 // Ñèãíàòóðà ìóçûêè:
2552 dw := MUSIC_SIGNATURE; // 'MUSI'
2553 Mem.WriteDWORD(dw);
2554 // Íàçâàíèå ìóçûêè:
2555 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2556 if gMusic.NoMusic then
2557 str := ''
2558 else
2559 str := gMusic.Name;
2560 Mem.WriteString(str, 64);
2561 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2562 dw := gMusic.GetPosition();
2563 Mem.WriteDWORD(dw);
2564 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2565 boo := gMusic.SpecPause;
2566 Mem.WriteBoolean(boo);
2567 ///// /////
2569 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2570 Mem.WriteInt(gTotalMonsters);
2571 ///// /////
2573 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2574 if gGameSettings.GameMode = GM_CTF then
2575 begin
2576 // Ôëàã Êðàñíîé êîìàíäû:
2577 SaveFlag(@gFlags[FLAG_RED]);
2578 // Ôëàã Ñèíåé êîìàíäû:
2579 SaveFlag(@gFlags[FLAG_BLUE]);
2580 end;
2581 ///// /////
2583 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2584 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2585 begin
2586 // Î÷êè Êðàñíîé êîìàíäû:
2587 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2588 // Î÷êè Ñèíåé êîìàíäû:
2589 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2590 end;
2591 ///// /////
2592 end;
2594 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2595 var
2596 dw: DWORD;
2597 b: Byte;
2598 str: String;
2599 boo: Boolean;
2601 procedure LoadPanelArray(var panels: TPanelArray);
2602 var
2603 PAMem: TBinMemoryReader;
2604 i, id: Integer;
2605 begin
2606 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
2607 PAMem := TBinMemoryReader.Create();
2608 PAMem.LoadFromMemory(Mem);
2610 for i := 0 to Length(panels)-1 do
2611 if panels[i].SaveIt then
2612 begin
2613 // ID ïàíåëè:
2614 PAMem.ReadInt(id);
2615 if id <> i then
2616 begin
2617 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
2618 end;
2619 // Çàãðóæàåì ïàíåëü:
2620 panels[i].LoadState(PAMem);
2621 end;
2623 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
2624 PAMem.Free();
2625 end;
2627 procedure LoadFlag(flag: PFlag);
2628 begin
2629 // Ñèãíàòóðà ôëàãà:
2630 Mem.ReadDWORD(dw);
2631 if dw <> FLAG_SIGNATURE then // 'FLAG'
2632 begin
2633 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
2634 end;
2635 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2636 Mem.ReadByte(flag^.RespawnType);
2637 // Ñîñòîÿíèå ôëàãà:
2638 Mem.ReadByte(flag^.State);
2639 // Íàïðàâëåíèå ôëàãà:
2640 Mem.ReadByte(b);
2641 if b = 1 then
2642 flag^.Direction := D_LEFT
2643 else // b = 2
2644 flag^.Direction := D_RIGHT;
2645 // Îáúåêò ôëàãà:
2646 Obj_LoadState(@flag^.Obj, Mem);
2647 end;
2649 begin
2650 if Mem = nil then
2651 Exit;
2653 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
2654 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
2655 LoadPanelArray(gWalls);
2656 // Çàãðóæàåì ïàíåëè ôîíà:
2657 LoadPanelArray(gRenderBackgrounds);
2658 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
2659 LoadPanelArray(gRenderForegrounds);
2660 // Çàãðóæàåì ïàíåëè âîäû:
2661 LoadPanelArray(gWater);
2662 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
2663 LoadPanelArray(gAcid1);
2664 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
2665 LoadPanelArray(gAcid2);
2666 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
2667 LoadPanelArray(gSteps);
2668 // Çàãðóæàåì ïàíåëè ëèôòîâ:
2669 LoadPanelArray(gLifts);
2670 ///// /////
2672 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
2673 g_GFX_Init();
2674 mapCreateGrid();
2676 ///// Çàãðóæàåì ìóçûêó: /////
2677 // Ñèãíàòóðà ìóçûêè:
2678 Mem.ReadDWORD(dw);
2679 if dw <> MUSIC_SIGNATURE then // 'MUSI'
2680 begin
2681 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
2682 end;
2683 // Íàçâàíèå ìóçûêè:
2684 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
2685 Mem.ReadString(str);
2686 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2687 Mem.ReadDWORD(dw);
2688 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2689 Mem.ReadBoolean(boo);
2690 // Çàïóñêàåì ýòó ìóçûêó:
2691 gMusic.SetByName(str);
2692 gMusic.SpecPause := boo;
2693 gMusic.Play();
2694 gMusic.Pause(True);
2695 gMusic.SetPosition(dw);
2696 ///// /////
2698 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
2699 Mem.ReadInt(gTotalMonsters);
2700 ///// /////
2702 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
2703 if gGameSettings.GameMode = GM_CTF then
2704 begin
2705 // Ôëàã Êðàñíîé êîìàíäû:
2706 LoadFlag(@gFlags[FLAG_RED]);
2707 // Ôëàã Ñèíåé êîìàíäû:
2708 LoadFlag(@gFlags[FLAG_BLUE]);
2709 end;
2710 ///// /////
2712 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2713 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2714 begin
2715 // Î÷êè Êðàñíîé êîìàíäû:
2716 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
2717 // Î÷êè Ñèíåé êîìàíäû:
2718 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
2719 end;
2720 ///// /////
2721 end;
2723 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
2724 var
2725 Arr: TPanelArray;
2726 begin
2727 Result := nil;
2728 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
2729 Arr := PanelByID[PanelID].PWhere^;
2730 PanelArrayID := PanelByID[PanelID].PArrID;
2731 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
2732 end;
2734 end.