DEADSOFTWARE

grid
[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 {$MODE DELPHI}
17 {$modeswitch nestedprocvars}
18 unit g_map;
20 interface
22 uses
23 e_graphics, g_basic, MAPSTRUCT, g_textures, Classes,
24 g_phys, wadreader, BinEditor, g_panel, g_grid, md5;
26 type
27 TMapInfo = record
28 Map: String;
29 Name: String;
30 Description: String;
31 Author: String;
32 MusicName: String;
33 SkyName: String;
34 Height: Word;
35 Width: Word;
36 end;
38 PRespawnPoint = ^TRespawnPoint;
39 TRespawnPoint = record
40 X, Y: Integer;
41 Direction: TDirection;
42 PointType: Byte;
43 end;
45 PFlagPoint = ^TFlagPoint;
46 TFlagPoint = TRespawnPoint;
48 PFlag = ^TFlag;
49 TFlag = record
50 Obj: TObj;
51 RespawnType: Byte;
52 State: Byte;
53 Count: Integer;
54 CaptureTime: LongWord;
55 Animation: TAnimation;
56 Direction: TDirection;
57 end;
59 function g_Map_Load(Res: String): Boolean;
60 function g_Map_GetMapInfo(Res: String): TMapInfo;
61 function g_Map_GetMapsList(WADName: String): SArray;
62 function g_Map_Exist(Res: String): Boolean;
63 procedure g_Map_Free();
64 procedure g_Map_Update();
66 // build "potentially visible panels" set, so we can avoid looping over all level panels again and again
67 procedure g_Map_BuildPVP (minx, miny, maxx, maxy: Integer);
68 procedure g_Map_ResetPVP ();
69 // do not call this without calling `g_Map_BuildPVP()` or `g_Map_ResetPVP()` first!
70 procedure g_Map_DrawPanels(x0, y0, wdt, hgt: Integer; PanelType: Word);
72 procedure g_Map_DrawBack(dx, dy: Integer);
73 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
74 PanelType: Word; b1x3: Boolean): Boolean;
75 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
76 procedure g_Map_EnableWall(ID: DWORD);
77 procedure g_Map_DisableWall(ID: DWORD);
78 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
79 procedure g_Map_SetLift(ID: DWORD; t: Integer);
80 procedure g_Map_ReAdd_DieTriggers();
81 function g_Map_IsSpecialTexture(Texture: String): Boolean;
83 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
84 function g_Map_GetPointCount(PointType: Byte): Word;
86 function g_Map_HaveFlagPoints(): Boolean;
88 procedure g_Map_ResetFlag(Flag: Byte);
89 procedure g_Map_DrawFlags();
91 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
93 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
94 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
96 // build "possibly lit panels" index, so we can avoid looping over all level panels again and again
97 function g_Map_BuildPLP (ltminx, ltminy, ltmaxx, ltmaxy: Integer): Boolean; // returns `false` if no patels lit
98 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
100 const
101 RESPAWNPOINT_PLAYER1 = 1;
102 RESPAWNPOINT_PLAYER2 = 2;
103 RESPAWNPOINT_DM = 3;
104 RESPAWNPOINT_RED = 4;
105 RESPAWNPOINT_BLUE = 5;
107 FLAG_NONE = 0;
108 FLAG_RED = 1;
109 FLAG_BLUE = 2;
110 FLAG_DOM = 3;
112 FLAG_STATE_NONE = 0;
113 FLAG_STATE_NORMAL = 1;
114 FLAG_STATE_DROPPED = 2;
115 FLAG_STATE_CAPTURED = 3;
116 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
117 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
119 FLAG_TIME = 720; // 20 seconds
121 SKY_STRETCH: Single = 1.5;
123 var
124 gWalls: TPanelArray;
125 gRenderBackgrounds: TPanelArray;
126 gRenderForegrounds: TPanelArray;
127 gWater, gAcid1, gAcid2: TPanelArray;
128 gSteps: TPanelArray;
129 gLifts: TPanelArray;
130 gBlockMon: TPanelArray;
131 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
132 //gDOMFlags: array of TFlag;
133 gMapInfo: TMapInfo;
134 gBackSize: TPoint;
135 gDoorMap: array of array of DWORD;
136 gLiftMap: array of array of DWORD;
137 gWADHash: TMD5Digest;
138 BackID: DWORD = DWORD(-1);
139 gExternalResources: TStringList;
140 gMapGrid: TBodyGrid = nil;
142 implementation
144 uses
145 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
146 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
147 g_options, MAPREADER, g_triggers, g_player, MAPDEF,
148 Math, g_monsters, g_saveload, g_language, g_netmsg,
149 utils, sfs,
150 ImagingTypes, Imaging, ImagingUtility,
151 ImagingGif, ImagingNetworkGraphics,
152 libc;
154 const
155 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
156 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
157 FLAG_SIGNATURE = $47414C46; // 'FLAG'
159 type
160 TPanelID = record
161 PWhere: ^TPanelArray;
162 PArrID: Integer;
163 end;
165 var
166 PanelById: array of TPanelID;
167 Textures: TLevelTextureArray;
168 RespawnPoints: Array of TRespawnPoint;
169 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
170 //DOMFlagPoints: Array of TFlagPoint;
173 function g_Map_IsSpecialTexture(Texture: String): Boolean;
174 begin
175 Result := (Texture = TEXTURE_NAME_WATER) or
176 (Texture = TEXTURE_NAME_ACID1) or
177 (Texture = TEXTURE_NAME_ACID2);
178 end;
180 procedure CreateDoorMap();
181 var
182 PanelArray: Array of record
183 X, Y: Integer;
184 Width, Height: Word;
185 Active: Boolean;
186 PanelID: DWORD;
187 end;
188 a, b, c, m, i, len: Integer;
189 ok: Boolean;
190 begin
191 if gWalls = nil then
192 Exit;
194 i := 0;
195 len := 128;
196 SetLength(PanelArray, len);
198 for a := 0 to High(gWalls) do
199 if gWalls[a].Door then
200 begin
201 PanelArray[i].X := gWalls[a].X;
202 PanelArray[i].Y := gWalls[a].Y;
203 PanelArray[i].Width := gWalls[a].Width;
204 PanelArray[i].Height := gWalls[a].Height;
205 PanelArray[i].Active := True;
206 PanelArray[i].PanelID := a;
208 i := i + 1;
209 if i = len then
210 begin
211 len := len + 128;
212 SetLength(PanelArray, len);
213 end;
214 end;
216 // Íåò äâåðåé:
217 if i = 0 then
218 begin
219 PanelArray := nil;
220 Exit;
221 end;
223 SetLength(gDoorMap, 0);
225 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
227 for a := 0 to i-1 do
228 if PanelArray[a].Active then
229 begin
230 PanelArray[a].Active := False;
231 m := Length(gDoorMap);
232 SetLength(gDoorMap, m+1);
233 SetLength(gDoorMap[m], 1);
234 gDoorMap[m, 0] := PanelArray[a].PanelID;
235 ok := True;
237 while ok do
238 begin
239 ok := False;
241 for b := 0 to i-1 do
242 if PanelArray[b].Active then
243 for c := 0 to High(gDoorMap[m]) do
244 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
245 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
246 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
247 PanelArray[b].Width, PanelArray[b].Height,
248 gWalls[gDoorMap[m, c]].X,
249 gWalls[gDoorMap[m, c]].Y,
250 gWalls[gDoorMap[m, c]].Width,
251 gWalls[gDoorMap[m, c]].Height) then
252 begin
253 PanelArray[b].Active := False;
254 SetLength(gDoorMap[m],
255 Length(gDoorMap[m])+1);
256 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
257 ok := True;
258 Break;
259 end;
260 end;
262 g_Game_StepLoading();
263 end;
265 PanelArray := nil;
266 end;
268 procedure CreateLiftMap();
269 var
270 PanelArray: Array of record
271 X, Y: Integer;
272 Width, Height: Word;
273 Active: Boolean;
274 end;
275 a, b, c, len, i, j: Integer;
276 ok: Boolean;
277 begin
278 if gLifts = nil then
279 Exit;
281 len := Length(gLifts);
282 SetLength(PanelArray, len);
284 for a := 0 to len-1 do
285 begin
286 PanelArray[a].X := gLifts[a].X;
287 PanelArray[a].Y := gLifts[a].Y;
288 PanelArray[a].Width := gLifts[a].Width;
289 PanelArray[a].Height := gLifts[a].Height;
290 PanelArray[a].Active := True;
291 end;
293 SetLength(gLiftMap, len);
294 i := 0;
296 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
298 for a := 0 to len-1 do
299 if PanelArray[a].Active then
300 begin
301 PanelArray[a].Active := False;
302 SetLength(gLiftMap[i], 32);
303 j := 0;
304 gLiftMap[i, j] := a;
305 ok := True;
307 while ok do
308 begin
309 ok := False;
310 for b := 0 to len-1 do
311 if PanelArray[b].Active then
312 for c := 0 to j do
313 if g_CollideAround(PanelArray[b].X,
314 PanelArray[b].Y,
315 PanelArray[b].Width,
316 PanelArray[b].Height,
317 PanelArray[gLiftMap[i, c]].X,
318 PanelArray[gLiftMap[i, c]].Y,
319 PanelArray[gLiftMap[i, c]].Width,
320 PanelArray[gLiftMap[i, c]].Height) then
321 begin
322 PanelArray[b].Active := False;
323 j := j+1;
324 if j > High(gLiftMap[i]) then
325 SetLength(gLiftMap[i],
326 Length(gLiftMap[i])+32);
328 gLiftMap[i, j] := b;
329 ok := True;
331 Break;
332 end;
333 end;
335 SetLength(gLiftMap[i], j+1);
336 i := i+1;
338 g_Game_StepLoading();
339 end;
341 SetLength(gLiftMap, i);
343 PanelArray := nil;
344 end;
346 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
347 CurTex: Integer; sav: Boolean): Integer;
348 var
349 len: Integer;
350 panels: ^TPanelArray;
351 begin
352 Result := -1;
354 case PanelRec.PanelType of
355 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
356 panels := @gWalls;
357 PANEL_BACK:
358 panels := @gRenderBackgrounds;
359 PANEL_FORE:
360 panels := @gRenderForegrounds;
361 PANEL_WATER:
362 panels := @gWater;
363 PANEL_ACID1:
364 panels := @gAcid1;
365 PANEL_ACID2:
366 panels := @gAcid2;
367 PANEL_STEP:
368 panels := @gSteps;
369 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
370 panels := @gLifts;
371 PANEL_BLOCKMON:
372 panels := @gBlockMon;
373 else
374 Exit;
375 end;
377 len := Length(panels^);
378 SetLength(panels^, len + 1);
380 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
381 panels^[len].ArrIdx := len;
382 if sav then
383 panels^[len].SaveIt := True;
385 Result := len;
387 len := Length(PanelByID);
388 SetLength(PanelByID, len + 1);
389 PanelByID[len].PWhere := panels;
390 PanelByID[len].PArrID := Result;
391 end;
393 function CreateNullTexture(RecName: String): Integer;
394 begin
395 SetLength(Textures, Length(Textures)+1);
396 result := High(Textures);
398 with Textures[High(Textures)] do
399 begin
400 TextureName := RecName;
401 Width := 1;
402 Height := 1;
403 Anim := False;
404 TextureID := TEXTURE_NONE;
405 end;
406 end;
408 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
409 var
410 WAD: TWADFile;
411 TextureData: Pointer;
412 WADName, txname: String;
413 a, ResLength: Integer;
414 begin
415 Result := -1;
417 if Textures <> nil then
418 for a := 0 to High(Textures) do
419 if Textures[a].TextureName = RecName then
420 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
421 Result := a;
422 Exit;
423 end;
425 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
426 if (RecName = TEXTURE_NAME_WATER) or
427 (RecName = TEXTURE_NAME_ACID1) or
428 (RecName = TEXTURE_NAME_ACID2) then
429 begin
430 SetLength(Textures, Length(Textures)+1);
432 with Textures[High(Textures)] do
433 begin
434 TextureName := RecName;
436 if TextureName = TEXTURE_NAME_WATER then
437 TextureID := TEXTURE_SPECIAL_WATER
438 else
439 if TextureName = TEXTURE_NAME_ACID1 then
440 TextureID := TEXTURE_SPECIAL_ACID1
441 else
442 if TextureName = TEXTURE_NAME_ACID2 then
443 TextureID := TEXTURE_SPECIAL_ACID2;
445 Anim := False;
446 end;
448 result := High(Textures);
449 Exit;
450 end;
452 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
453 WADName := g_ExtractWadName(RecName);
455 WAD := TWADFile.Create();
457 if WADName <> '' then
458 WADName := GameDir+'/wads/'+WADName
459 else
460 WADName := Map;
462 WAD.ReadFile(WADName);
464 txname := RecName;
466 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
467 begin
468 FreeMem(TextureData);
469 RecName := 'COMMON\ALIEN';
470 end;
473 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
474 begin
475 SetLength(Textures, Length(Textures)+1);
476 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
477 Exit;
478 e_GetTextureSize(Textures[High(Textures)].TextureID,
479 @Textures[High(Textures)].Width,
480 @Textures[High(Textures)].Height);
481 FreeMem(TextureData);
482 Textures[High(Textures)].TextureName := {RecName}txname;
483 Textures[High(Textures)].Anim := False;
485 result := High(Textures);
486 end
487 else // Íåò òàêîãî ðåóñðñà â WAD'å
488 begin
489 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
490 if log then
491 begin
492 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
493 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
494 end;
495 end;
497 WAD.Free();
498 end;
500 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
501 var
502 WAD: TWADFile;
503 TextureWAD: PChar = nil;
504 TextData: Pointer = nil;
505 TextureData: Pointer = nil;
506 cfg: TConfig = nil;
507 WADName: String;
508 ResLength: Integer;
509 TextureResource: String;
510 _width, _height, _framecount, _speed: Integer;
511 _backanimation: Boolean;
512 //imgfmt: string;
513 ia: TDynImageDataArray = nil;
514 f, c, frdelay, frloop: Integer;
515 begin
516 result := -1;
518 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
520 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
521 WADName := g_ExtractWadName(RecName);
523 WAD := TWADFile.Create();
524 try
525 if WADName <> '' then
526 WADName := GameDir+'/wads/'+WADName
527 else
528 WADName := Map;
530 WAD.ReadFile(WADName);
532 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
533 begin
534 if log then
535 begin
536 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
537 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
538 end;
539 exit;
540 end;
542 {TEST
543 if WADName = Map then
544 begin
545 //FreeMem(TextureWAD);
546 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
547 end;
550 WAD.FreeWAD();
552 if ResLength < 6 then
553 begin
554 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
555 exit;
556 end;
558 // ýòî ïòèöà? ýòî ñàìîë¸ò?
559 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
560 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
561 begin
562 // íåò, ýòî ñóïåðìåí!
563 if not WAD.ReadMemory(TextureWAD, ResLength) then
564 begin
565 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
566 exit;
567 end;
569 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
570 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
571 begin
572 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
573 exit;
574 end;
576 cfg := TConfig.CreateMem(TextData, ResLength);
578 TextureResource := cfg.ReadStr('', 'resource', '');
579 if TextureResource = '' then
580 begin
581 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
582 exit;
583 end;
585 _width := cfg.ReadInt('', 'framewidth', 0);
586 _height := cfg.ReadInt('', 'frameheight', 0);
587 _framecount := cfg.ReadInt('', 'framecount', 0);
588 _speed := cfg.ReadInt('', 'waitcount', 0);
589 _backanimation := cfg.ReadBool('', 'backanimation', False);
591 cfg.Free();
592 cfg := nil;
594 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
595 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
596 begin
597 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
598 exit;
599 end;
601 WAD.Free();
602 WAD := nil;
604 SetLength(Textures, Length(Textures)+1);
605 with Textures[High(Textures)] do
606 begin
607 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
608 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
609 begin
610 TextureName := RecName;
611 Width := _width;
612 Height := _height;
613 Anim := True;
614 FramesCount := _framecount;
615 Speed := _speed;
616 result := High(Textures);
617 end
618 else
619 begin
620 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
621 end;
622 end;
623 end
624 else
625 begin
626 // try animated image
628 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
629 if length(imgfmt) = 0 then
630 begin
631 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
632 exit;
633 end;
635 GlobalMetadata.ClearMetaItems();
636 GlobalMetadata.ClearMetaItemsForSaving();
637 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
638 begin
639 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
640 exit;
641 end;
642 if length(ia) = 0 then
643 begin
644 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
645 exit;
646 end;
648 WAD.Free();
649 WAD := nil;
651 _width := ia[0].width;
652 _height := ia[0].height;
653 _framecount := length(ia);
654 _speed := 1;
655 _backanimation := false;
656 frdelay := -1;
657 frloop := -666;
658 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
659 begin
660 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
661 try
662 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
663 frdelay := f;
664 if f < 0 then f := 0;
665 // rounding ;-)
666 c := f mod 28;
667 if c < 13 then c := 0 else c := 1;
668 f := (f div 28)+c;
669 if f < 1 then f := 1 else if f > 255 then f := 255;
670 _speed := f;
671 except
672 end;
673 end;
674 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
675 begin
676 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
677 try
678 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
679 frloop := f;
680 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
681 except
682 end;
683 end;
684 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
685 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
686 f := ord(_backanimation);
687 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);
689 SetLength(Textures, Length(Textures)+1);
690 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
691 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
692 begin
693 Textures[High(Textures)].TextureName := RecName;
694 Textures[High(Textures)].Width := _width;
695 Textures[High(Textures)].Height := _height;
696 Textures[High(Textures)].Anim := True;
697 Textures[High(Textures)].FramesCount := length(ia);
698 Textures[High(Textures)].Speed := _speed;
699 result := High(Textures);
700 //writeln(' CREATED!');
701 end
702 else
703 begin
704 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
705 end;
706 end;
707 finally
708 for f := 0 to High(ia) do FreeImage(ia[f]);
709 WAD.Free();
710 cfg.Free();
711 if TextureWAD <> nil then FreeMem(TextureWAD);
712 if TextData <> nil then FreeMem(TextData);
713 if TextureData <> nil then FreeMem(TextureData);
714 end;
715 end;
717 procedure CreateItem(Item: TItemRec_1);
718 begin
719 if g_Game_IsClient then Exit;
721 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
722 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
723 Exit;
725 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
726 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
727 end;
729 procedure CreateArea(Area: TAreaRec_1);
730 var
731 a: Integer;
732 id: DWORD;
733 begin
734 case Area.AreaType of
735 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
736 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
737 begin
738 SetLength(RespawnPoints, Length(RespawnPoints)+1);
739 with RespawnPoints[High(RespawnPoints)] do
740 begin
741 X := Area.X;
742 Y := Area.Y;
743 Direction := TDirection(Area.Direction);
745 case Area.AreaType of
746 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
747 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
748 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
749 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
750 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
751 end;
752 end;
753 end;
755 AREA_REDFLAG, AREA_BLUEFLAG:
756 begin
757 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
759 if FlagPoints[a] <> nil then Exit;
761 New(FlagPoints[a]);
763 with FlagPoints[a]^ do
764 begin
765 X := Area.X-FLAGRECT.X;
766 Y := Area.Y-FLAGRECT.Y;
767 Direction := TDirection(Area.Direction);
768 end;
770 with gFlags[a] do
771 begin
772 case a of
773 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
774 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
775 end;
777 Animation := TAnimation.Create(id, True, 8);
778 Obj.Rect := FLAGRECT;
780 g_Map_ResetFlag(a);
781 end;
782 end;
784 AREA_DOMFLAG:
785 begin
786 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
787 with DOMFlagPoints[High(DOMFlagPoints)] do
788 begin
789 X := Area.X;
790 Y := Area.Y;
791 Direction := TDirection(Area.Direction);
792 end;
794 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
795 end;
796 end;
797 end;
799 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
800 var
801 _trigger: TTrigger;
802 begin
803 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
805 with _trigger do
806 begin
807 X := Trigger.X;
808 Y := Trigger.Y;
809 Width := Trigger.Width;
810 Height := Trigger.Height;
811 Enabled := ByteBool(Trigger.Enabled);
812 TexturePanel := Trigger.TexturePanel;
813 TexturePanelType := fTexturePanel1Type;
814 ShotPanelType := fTexturePanel2Type;
815 TriggerType := Trigger.TriggerType;
816 ActivateType := Trigger.ActivateType;
817 Keys := Trigger.Keys;
818 Data.Default := Trigger.DATA;
819 end;
821 g_Triggers_Create(_trigger);
822 end;
824 procedure CreateMonster(monster: TMonsterRec_1);
825 var
826 a, i: Integer;
827 begin
828 if g_Game_IsClient then Exit;
830 if (gGameSettings.GameType = GT_SINGLE)
831 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
832 begin
833 i := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y,
834 TDirection(monster.Direction));
836 if gTriggers <> nil then
837 for a := 0 to High(gTriggers) do
838 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
839 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
840 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
841 gMonsters[i].AddTrigger(a);
843 if monster.MonsterType <> MONSTER_BARREL then
844 Inc(gTotalMonsters);
845 end;
846 end;
848 procedure g_Map_ReAdd_DieTriggers();
849 var
850 i, a: Integer;
851 begin
852 if g_Game_IsClient then Exit;
854 for i := 0 to High(gMonsters) do
855 if gMonsters[i] <> nil then
856 begin
857 gMonsters[i].ClearTriggers();
859 for a := 0 to High(gTriggers) do
860 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
861 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
862 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
863 gMonsters[i].AddTrigger(a);
864 end;
865 end;
867 function extractWadName(resourceName: string): string;
868 var
869 posN: Integer;
870 begin
871 posN := Pos(':', resourceName);
872 if posN > 0 then
873 Result:= Copy(resourceName, 0, posN-1)
874 else
875 Result := '';
876 end;
878 procedure addResToExternalResList(res: string);
879 begin
880 res := extractWadName(res);
881 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
882 gExternalResources.Add(res);
883 end;
885 procedure generateExternalResourcesList(mapReader: TMapReader_1);
886 var
887 textures: TTexturesRec1Array;
888 mapHeader: TMapHeaderRec_1;
889 i: integer;
890 resFile: String = '';
891 begin
892 if gExternalResources = nil then
893 gExternalResources := TStringList.Create;
895 gExternalResources.Clear;
896 textures := mapReader.GetTextures();
897 for i := 0 to High(textures) do
898 begin
899 addResToExternalResList(resFile);
900 end;
902 textures := nil;
904 mapHeader := mapReader.GetMapHeader;
906 addResToExternalResList(mapHeader.MusicName);
907 addResToExternalResList(mapHeader.SkyName);
908 end;
910 function g_Map_Load(Res: String): Boolean;
911 const
912 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
913 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
914 var
915 WAD: TWADFile;
916 MapReader: TMapReader_1;
917 Header: TMapHeaderRec_1;
918 _textures: TTexturesRec1Array;
919 _texnummap: array of Integer; // `_textures` -> `Textures`
920 panels: TPanelsRec1Array;
921 items: TItemsRec1Array;
922 monsters: TMonsterRec1Array;
923 areas: TAreasRec1Array;
924 triggers: TTriggersRec1Array;
925 a, b, c, k: Integer;
926 PanelID: DWORD;
927 AddTextures: TAddTextureArray;
928 texture: TTextureRec_1;
929 TriggersTable: Array of record
930 TexturePanel: Integer;
931 LiftPanel: Integer;
932 DoorPanel: Integer;
933 ShotPanel: Integer;
934 end;
935 FileName, mapResName, s, TexName: String;
936 Data: Pointer;
937 Len: Integer;
938 ok, isAnim, trigRef: Boolean;
939 CurTex, ntn: Integer;
941 mapX0: Integer = $3fffffff;
942 mapY0: Integer = $3fffffff;
943 mapX1: Integer = -$3fffffff;
944 mapY1: Integer = -$3fffffff;
946 procedure fixMinMax (var panels: TPanelArray);
947 var
948 idx: Integer;
949 begin
950 for idx := 0 to High(panels) do
951 begin
952 if (panels[idx].Width < 1) or (panels[idx].Height < 1) then continue;
953 if mapX0 > panels[idx].X then mapX0 := panels[idx].X;
954 if mapY0 > panels[idx].Y then mapY0 := panels[idx].Y;
955 if mapX1 < panels[idx].X+panels[idx].Width-1 then mapX1 := panels[idx].X+panels[idx].Width-1;
956 if mapY1 < panels[idx].Y+panels[idx].Height-1 then mapY1 := panels[idx].Y+panels[idx].Height-1;
957 end;
958 end;
960 procedure addPanelsToGrid (var panels: TPanelArray; tag: Integer);
961 var
962 idx: Integer;
963 begin
964 for idx := 0 to High(panels) do
965 begin
966 gMapGrid.insertBody(panels[idx], panels[idx].X, panels[idx].Y, panels[idx].Width, panels[idx].Height, tag);
967 end;
968 end;
970 begin
971 gMapGrid.Free();
972 gMapGrid := nil;
974 Result := False;
975 gMapInfo.Map := Res;
976 TriggersTable := nil;
977 FillChar(texture, SizeOf(texture), 0);
979 sfsGCDisable(); // temporary disable removing of temporary volumes
980 try
981 // Çàãðóçêà WAD:
982 FileName := g_ExtractWadName(Res);
983 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
984 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
986 WAD := TWADFile.Create();
987 if not WAD.ReadFile(FileName) then
988 begin
989 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
990 WAD.Free();
991 Exit;
992 end;
993 //k8: why loader ignores path here?
994 mapResName := g_ExtractFileName(Res);
995 if not WAD.GetMapResource(mapResName, Data, Len) then
996 begin
997 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
998 WAD.Free();
999 Exit;
1000 end;
1002 WAD.Free();
1004 // Çàãðóçêà êàðòû:
1005 e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
1006 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1007 MapReader := TMapReader_1.Create();
1009 if not MapReader.LoadMap(Data) then
1010 begin
1011 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1012 FreeMem(Data);
1013 MapReader.Free();
1014 Exit;
1015 end;
1017 FreeMem(Data);
1018 generateExternalResourcesList(MapReader);
1019 // Çàãðóçêà òåêñòóð:
1020 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1021 _textures := MapReader.GetTextures();
1022 _texnummap := nil;
1024 // Äîáàâëåíèå òåêñòóð â Textures[]:
1025 if _textures <> nil then
1026 begin
1027 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1028 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
1029 SetLength(_texnummap, length(_textures));
1031 for a := 0 to High(_textures) do
1032 begin
1033 SetLength(s, 64);
1034 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
1035 for b := 1 to Length(s) do
1036 if s[b] = #0 then
1037 begin
1038 SetLength(s, b-1);
1039 Break;
1040 end;
1041 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1042 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1043 // Àíèìèðîâàííàÿ òåêñòóðà:
1044 if ByteBool(_textures[a].Anim) then
1045 begin
1046 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1047 if ntn < 0 then
1048 begin
1049 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1050 ntn := CreateNullTexture(_textures[a].Resource);
1051 end;
1052 end
1053 else // Îáû÷íàÿ òåêñòóðà:
1054 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1055 if ntn < 0 then
1056 begin
1057 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1058 ntn := CreateNullTexture(_textures[a].Resource);
1059 end;
1061 _texnummap[a] := ntn; // fix texture number
1062 g_Game_StepLoading();
1063 end;
1064 end;
1066 // Çàãðóçêà òðèããåðîâ:
1067 gTriggerClientID := 0;
1068 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1069 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1070 triggers := MapReader.GetTriggers();
1072 // Çàãðóçêà ïàíåëåé:
1073 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1074 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1075 panels := MapReader.GetPanels();
1077 // check texture numbers for panels
1078 for a := 0 to High(panels) do
1079 begin
1080 if panels[a].TextureNum > High(_textures) then
1081 begin
1082 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1083 result := false;
1084 exit;
1085 end;
1086 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1087 end;
1089 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1090 if triggers <> nil then
1091 begin
1092 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1093 SetLength(TriggersTable, Length(triggers));
1094 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1096 for a := 0 to High(TriggersTable) do
1097 begin
1098 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1099 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1100 // Ëèôòû:
1101 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1102 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1103 else
1104 TriggersTable[a].LiftPanel := -1;
1105 // Äâåðè:
1106 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1107 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1108 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1109 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1110 else
1111 TriggersTable[a].DoorPanel := -1;
1112 // Òóðåëü:
1113 if triggers[a].TriggerType = TRIGGER_SHOT then
1114 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1115 else
1116 TriggersTable[a].ShotPanel := -1;
1118 g_Game_StepLoading();
1119 end;
1120 end;
1122 // Ñîçäàåì ïàíåëè:
1123 if panels <> nil then
1124 begin
1125 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1126 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1128 for a := 0 to High(panels) do
1129 begin
1130 SetLength(AddTextures, 0);
1131 trigRef := False;
1132 CurTex := -1;
1133 if _textures <> nil then
1134 begin
1135 texture := _textures[panels[a].TextureNum];
1136 ok := True;
1137 end
1138 else
1139 ok := False;
1141 if ok then
1142 begin
1143 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1144 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1145 ok := False;
1146 if (TriggersTable <> nil) and (_textures <> nil) then
1147 for b := 0 to High(TriggersTable) do
1148 if (TriggersTable[b].TexturePanel = a)
1149 or (TriggersTable[b].ShotPanel = a) then
1150 begin
1151 trigRef := True;
1152 ok := True;
1153 Break;
1154 end;
1155 end;
1157 if ok then
1158 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1159 SetLength(s, 64);
1160 CopyMemory(@s[1], @texture.Resource[0], 64);
1161 // Èçìåðÿåì äëèíó:
1162 Len := Length(s);
1163 for c := Len downto 1 do
1164 if s[c] <> #0 then
1165 begin
1166 Len := c;
1167 Break;
1168 end;
1169 SetLength(s, Len);
1171 // Ñïåö-òåêñòóðû çàïðåùåíû:
1172 if g_Map_IsSpecialTexture(s) then
1173 ok := False
1174 else
1175 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1176 ok := g_Texture_NumNameFindStart(s);
1178 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1179 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1180 if ok then
1181 begin
1182 k := NNF_NAME_BEFORE;
1183 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1184 while ok or (k = NNF_NAME_BEFORE) or
1185 (k = NNF_NAME_EQUALS) do
1186 begin
1187 k := g_Texture_NumNameFindNext(TexName);
1189 if (k = NNF_NAME_BEFORE) or
1190 (k = NNF_NAME_AFTER) then
1191 begin
1192 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1193 if ByteBool(texture.Anim) then
1194 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1195 isAnim := True;
1196 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1197 if not ok then
1198 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1199 isAnim := False;
1200 ok := CreateTexture(TexName, FileName, False) >= 0;
1201 end;
1202 end
1203 else
1204 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1205 isAnim := False;
1206 ok := CreateTexture(TexName, FileName, False) >= 0;
1207 if not ok then
1208 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1209 isAnim := True;
1210 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1211 end;
1212 end;
1214 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1215 if ok then
1216 begin
1217 for c := 0 to High(Textures) do
1218 if Textures[c].TextureName = TexName then
1219 begin
1220 SetLength(AddTextures, Length(AddTextures)+1);
1221 AddTextures[High(AddTextures)].Texture := c;
1222 AddTextures[High(AddTextures)].Anim := isAnim;
1223 Break;
1224 end;
1225 end;
1226 end
1227 else
1228 if k = NNF_NAME_EQUALS then
1229 begin
1230 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1231 SetLength(AddTextures, Length(AddTextures)+1);
1232 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1233 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1234 CurTex := High(AddTextures);
1235 ok := True;
1236 end
1237 else // NNF_NO_NAME
1238 ok := False;
1239 end; // while ok...
1241 ok := True;
1242 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1243 end; // if ok - ññûëàþòñÿ òðèããåðû
1245 if not ok then
1246 begin
1247 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1248 SetLength(AddTextures, 1);
1249 AddTextures[0].Texture := panels[a].TextureNum;
1250 AddTextures[0].Anim := ByteBool(texture.Anim);
1251 CurTex := 0;
1252 end;
1254 //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);
1256 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1257 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1259 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1260 if TriggersTable <> nil then
1261 for b := 0 to High(TriggersTable) do
1262 begin
1263 // Òðèããåð äâåðè/ëèôòà:
1264 if (TriggersTable[b].LiftPanel = a) or
1265 (TriggersTable[b].DoorPanel = a) then
1266 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1267 // Òðèããåð ñìåíû òåêñòóðû:
1268 if TriggersTable[b].TexturePanel = a then
1269 triggers[b].TexturePanel := PanelID;
1270 // Òðèããåð "Òóðåëü":
1271 if TriggersTable[b].ShotPanel = a then
1272 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1273 end;
1275 g_Game_StepLoading();
1276 end;
1277 end;
1279 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1280 if (triggers <> nil) and not gLoadGameMode then
1281 begin
1282 e_WriteLog(' Creating triggers...', MSG_NOTIFY);
1283 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1284 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1285 for a := 0 to High(triggers) do
1286 begin
1287 if triggers[a].TexturePanel <> -1 then
1288 b := panels[TriggersTable[a].TexturePanel].PanelType
1289 else
1290 b := 0;
1291 if (triggers[a].TriggerType = TRIGGER_SHOT) and
1292 (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1293 c := panels[TriggersTable[a].ShotPanel].PanelType
1294 else
1295 c := 0;
1296 CreateTrigger(triggers[a], b, c);
1297 end;
1298 end;
1300 // Çàãðóçêà ïðåäìåòîâ:
1301 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1302 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1303 items := MapReader.GetItems();
1305 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1306 if (items <> nil) and not gLoadGameMode then
1307 begin
1308 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1309 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1310 for a := 0 to High(items) do
1311 CreateItem(Items[a]);
1312 end;
1314 // Çàãðóçêà îáëàñòåé:
1315 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1316 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1317 areas := MapReader.GetAreas();
1319 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1320 if areas <> nil then
1321 begin
1322 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1323 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1324 for a := 0 to High(areas) do
1325 CreateArea(areas[a]);
1326 end;
1328 // Çàãðóçêà ìîíñòðîâ:
1329 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1330 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1331 monsters := MapReader.GetMonsters();
1333 gTotalMonsters := 0;
1335 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1336 if (monsters <> nil) and not gLoadGameMode then
1337 begin
1338 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1339 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1340 for a := 0 to High(monsters) do
1341 CreateMonster(monsters[a]);
1342 end;
1344 // Çàãðóçêà îïèñàíèÿ êàðòû:
1345 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1346 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1347 Header := MapReader.GetMapHeader();
1349 MapReader.Free();
1351 with gMapInfo do
1352 begin
1353 Name := Header.MapName;
1354 Description := Header.MapDescription;
1355 Author := Header.MapAuthor;
1356 MusicName := Header.MusicName;
1357 SkyName := Header.SkyName;
1358 Height := Header.Height;
1359 Width := Header.Width;
1360 end;
1362 // Çàãðóçêà íåáà:
1363 if gMapInfo.SkyName <> '' then
1364 begin
1365 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1366 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1367 FileName := g_ExtractWadName(gMapInfo.SkyName);
1369 if FileName <> '' then
1370 FileName := GameDir+'/wads/'+FileName
1371 else
1372 begin
1373 FileName := g_ExtractWadName(Res);
1374 end;
1376 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1377 if g_Texture_CreateWAD(BackID, s) then
1378 begin
1379 g_Game_SetupScreenSize();
1380 end
1381 else
1382 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1383 end;
1385 // Çàãðóçêà ìóçûêè:
1386 ok := False;
1387 if gMapInfo.MusicName <> '' then
1388 begin
1389 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1390 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1391 FileName := g_ExtractWadName(gMapInfo.MusicName);
1393 if FileName <> '' then
1394 FileName := GameDir+'/wads/'+FileName
1395 else
1396 begin
1397 FileName := g_ExtractWadName(Res);
1398 end;
1400 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1401 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1402 ok := True
1403 else
1404 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1405 end;
1407 // Îñòàëüíûå óñòàíâêè:
1408 CreateDoorMap();
1409 CreateLiftMap();
1411 g_Items_Init();
1412 g_Weapon_Init();
1413 g_Monsters_Init();
1415 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1416 if not gLoadGameMode then
1417 g_GFX_Init();
1419 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1420 _textures := nil;
1421 panels := nil;
1422 items := nil;
1423 areas := nil;
1424 triggers := nil;
1425 TriggersTable := nil;
1426 AddTextures := nil;
1428 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
1429 if ok and (not gLoadGameMode) then
1430 begin
1431 gMusic.SetByName(gMapInfo.MusicName);
1432 gMusic.Play();
1433 end
1434 else
1435 gMusic.SetByName('');
1436 finally
1437 sfsGCEnable(); // enable releasing unused volumes
1438 end;
1440 e_WriteLog('Creating map grid', MSG_NOTIFY);
1442 fixMinMax(gWalls);
1443 fixMinMax(gRenderBackgrounds);
1444 fixMinMax(gRenderForegrounds);
1445 fixMinMax(gWater);
1446 fixMinMax(gAcid1);
1447 fixMinMax(gAcid2);
1448 fixMinMax(gSteps);
1450 gMapGrid := TBodyGrid.Create(mapX1+1, mapY1+1);
1452 addPanelsToGrid(gWalls, PANEL_WALL); // and PANEL_CLOSEDOOR
1453 addPanelsToGrid(gRenderBackgrounds, PANEL_BACK);
1454 addPanelsToGrid(gRenderForegrounds, PANEL_FORE);
1455 addPanelsToGrid(gWater, PANEL_WATER);
1456 addPanelsToGrid(gAcid1, PANEL_ACID1);
1457 addPanelsToGrid(gAcid2, PANEL_ACID2);
1458 addPanelsToGrid(gSteps, PANEL_STEP);
1460 gMapGrid.dumpStats();
1462 e_WriteLog('Done loading map.', MSG_NOTIFY);
1463 Result := True;
1464 end;
1466 function g_Map_GetMapInfo(Res: String): TMapInfo;
1467 var
1468 WAD: TWADFile;
1469 MapReader: TMapReader_1;
1470 Header: TMapHeaderRec_1;
1471 FileName: String;
1472 Data: Pointer;
1473 Len: Integer;
1474 begin
1475 FillChar(Result, SizeOf(Result), 0);
1476 FileName := g_ExtractWadName(Res);
1478 WAD := TWADFile.Create();
1479 if not WAD.ReadFile(FileName) then
1480 begin
1481 WAD.Free();
1482 Exit;
1483 end;
1485 //k8: it ignores path again
1486 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
1487 begin
1488 WAD.Free();
1489 Exit;
1490 end;
1492 WAD.Free();
1494 MapReader := TMapReader_1.Create();
1496 if not MapReader.LoadMap(Data) then
1497 begin
1498 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
1499 ZeroMemory(@Header, SizeOf(Header));
1500 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
1501 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
1502 end
1503 else
1504 begin
1505 Header := MapReader.GetMapHeader();
1506 Result.Name := Header.MapName;
1507 Result.Description := Header.MapDescription;
1508 end;
1510 FreeMem(Data);
1511 MapReader.Free();
1513 Result.Map := Res;
1514 Result.Author := Header.MapAuthor;
1515 Result.Height := Header.Height;
1516 Result.Width := Header.Width;
1517 end;
1519 function g_Map_GetMapsList(WADName: string): SArray;
1520 var
1521 WAD: TWADFile;
1522 a: Integer;
1523 ResList: SArray;
1524 begin
1525 Result := nil;
1526 WAD := TWADFile.Create();
1527 if not WAD.ReadFile(WADName) then
1528 begin
1529 WAD.Free();
1530 Exit;
1531 end;
1532 ResList := WAD.GetMapResources();
1533 if ResList <> nil then
1534 begin
1535 for a := 0 to High(ResList) do
1536 begin
1537 SetLength(Result, Length(Result)+1);
1538 Result[High(Result)] := ResList[a];
1539 end;
1540 end;
1541 WAD.Free();
1542 end;
1544 function g_Map_Exist(Res: string): Boolean;
1545 var
1546 WAD: TWADFile;
1547 FileName, mnn: string;
1548 ResList: SArray;
1549 a: Integer;
1550 begin
1551 Result := False;
1553 FileName := addWadExtension(g_ExtractWadName(Res));
1555 WAD := TWADFile.Create;
1556 if not WAD.ReadFile(FileName) then
1557 begin
1558 WAD.Free();
1559 Exit;
1560 end;
1562 ResList := WAD.GetMapResources();
1563 WAD.Free();
1565 mnn := g_ExtractFileName(Res);
1566 if ResList <> nil then
1567 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
1568 begin
1569 Result := True;
1570 Exit;
1571 end;
1572 end;
1574 procedure g_Map_Free();
1575 var
1576 a: Integer;
1578 procedure FreePanelArray(var panels: TPanelArray);
1579 var
1580 i: Integer;
1582 begin
1583 if panels <> nil then
1584 begin
1585 for i := 0 to High(panels) do
1586 panels[i].Free();
1587 panels := nil;
1588 end;
1589 end;
1591 begin
1592 g_GFX_Free();
1593 g_Weapon_Free();
1594 g_Items_Free();
1595 g_Triggers_Free();
1596 g_Monsters_Free();
1598 RespawnPoints := nil;
1599 if FlagPoints[FLAG_RED] <> nil then
1600 begin
1601 Dispose(FlagPoints[FLAG_RED]);
1602 FlagPoints[FLAG_RED] := nil;
1603 end;
1604 if FlagPoints[FLAG_BLUE] <> nil then
1605 begin
1606 Dispose(FlagPoints[FLAG_BLUE]);
1607 FlagPoints[FLAG_BLUE] := nil;
1608 end;
1609 //DOMFlagPoints := nil;
1611 //gDOMFlags := nil;
1613 if Textures <> nil then
1614 begin
1615 for a := 0 to High(Textures) do
1616 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
1617 if Textures[a].Anim then
1618 g_Frames_DeleteByID(Textures[a].FramesID)
1619 else
1620 if Textures[a].TextureID <> TEXTURE_NONE then
1621 e_DeleteTexture(Textures[a].TextureID);
1623 Textures := nil;
1624 end;
1626 FreePanelArray(gWalls);
1627 FreePanelArray(gRenderBackgrounds);
1628 FreePanelArray(gRenderForegrounds);
1629 FreePanelArray(gWater);
1630 FreePanelArray(gAcid1);
1631 FreePanelArray(gAcid2);
1632 FreePanelArray(gSteps);
1633 FreePanelArray(gLifts);
1634 FreePanelArray(gBlockMon);
1636 if BackID <> DWORD(-1) then
1637 begin
1638 gBackSize.X := 0;
1639 gBackSize.Y := 0;
1640 e_DeleteTexture(BackID);
1641 BackID := DWORD(-1);
1642 end;
1644 g_Game_StopAllSounds(False);
1645 gMusic.FreeSound();
1646 g_Sound_Delete(gMapInfo.MusicName);
1648 gMapInfo.Name := '';
1649 gMapInfo.Description := '';
1650 gMapInfo.MusicName := '';
1651 gMapInfo.Height := 0;
1652 gMapInfo.Width := 0;
1654 gDoorMap := nil;
1655 gLiftMap := nil;
1657 PanelByID := nil;
1658 end;
1660 procedure g_Map_Update();
1661 var
1662 a, d, j: Integer;
1663 m: Word;
1664 s: String;
1666 procedure UpdatePanelArray(var panels: TPanelArray);
1667 var
1668 i: Integer;
1670 begin
1671 if panels <> nil then
1672 for i := 0 to High(panels) do
1673 panels[i].Update();
1674 end;
1676 begin
1677 UpdatePanelArray(gWalls);
1678 UpdatePanelArray(gRenderBackgrounds);
1679 UpdatePanelArray(gRenderForegrounds);
1680 UpdatePanelArray(gWater);
1681 UpdatePanelArray(gAcid1);
1682 UpdatePanelArray(gAcid2);
1683 UpdatePanelArray(gSteps);
1685 if gGameSettings.GameMode = GM_CTF then
1686 begin
1687 for a := FLAG_RED to FLAG_BLUE do
1688 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
1689 with gFlags[a] do
1690 begin
1691 if gFlags[a].Animation <> nil then
1692 gFlags[a].Animation.Update();
1694 m := g_Obj_Move(@Obj, True, True);
1696 if gTime mod (GAME_TICK*2) <> 0 then
1697 Continue;
1699 // Ñîïðîòèâëåíèå âîçäóõà:
1700 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
1702 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
1703 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
1704 begin
1705 g_Map_ResetFlag(a);
1706 gFlags[a].CaptureTime := 0;
1707 if a = FLAG_RED then
1708 s := _lc[I_PLAYER_FLAG_RED]
1709 else
1710 s := _lc[I_PLAYER_FLAG_BLUE];
1711 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1713 if g_Game_IsNet then
1714 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
1715 Continue;
1716 end;
1718 if Count > 0 then
1719 Count := Count - 1;
1721 // Èãðîê áåðåò ôëàã:
1722 if gPlayers <> nil then
1723 begin
1724 j := Random(Length(gPlayers)) - 1;
1726 for d := 0 to High(gPlayers) do
1727 begin
1728 Inc(j);
1729 if j > High(gPlayers) then
1730 j := 0;
1732 if gPlayers[j] <> nil then
1733 if gPlayers[j].Live and
1734 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
1735 begin
1736 if gPlayers[j].GetFlag(a) then
1737 Break;
1738 end;
1739 end;
1740 end;
1741 end;
1742 end;
1743 end;
1746 var
1747 pvpset: array of PPanel = nil; // potentially lit panels
1748 pvpb, pvpe: array [0..7] of Integer; // start/end (inclusive) of the correspoinding type panels in pvpset
1749 pvpcount: Integer = -1; // to avoid constant reallocations
1751 function pvpType (panelType: Word): Integer;
1752 begin
1753 case panelType of
1754 PANEL_WALL, PANEL_CLOSEDOOR: result := 0; // gWalls
1755 PANEL_BACK: result := 1; // gRenderBackgrounds
1756 PANEL_FORE: result := 2; // gRenderForegrounds
1757 PANEL_WATER: result := 3; // gWater
1758 PANEL_ACID1: result := 4; // gAcid1
1759 PANEL_ACID2: result := 5; // gAcid2
1760 PANEL_STEP: result := 6; // gSteps
1761 else result := -1;
1762 end;
1763 end;
1765 procedure g_Map_ResetPVP ();
1766 begin
1767 pvpcount := -1; // special
1768 end;
1770 procedure g_Map_BuildPVP (minx, miny, maxx, maxy: Integer);
1771 var
1772 idx: Integer;
1773 tpc: Integer;
1775 procedure checkPanels (var panels: TPanelArray; stp: Integer);
1776 var
1777 idx, x, y, w, h: Integer;
1778 begin
1779 if panels = nil then exit;
1780 tpc := tpc+Length(panels);
1781 if (stp < 0) or (stp > 6) then exit;
1782 pvpb[stp] := pvpcount;
1783 for idx := 0 to High(panels) do
1784 begin
1785 w := panels[idx].Width;
1786 h := panels[idx].Height;
1787 if (w < 1) or (h < 1) then continue;
1788 x := panels[idx].X;
1789 y := panels[idx].Y;
1790 if (x > maxx) or (y > maxy) then continue;
1791 if (x+w <= minx) or (y+h <= miny) then continue;
1792 if pvpcount = length(pvpset) then SetLength(pvpset, pvpcount+32768);
1793 pvpset[pvpcount] := @panels[idx];
1794 Inc(pvpcount);
1795 end;
1796 pvpe[stp] := pvpcount-1;
1797 end;
1799 begin
1800 //e_WriteLog(Format('visible rect: (%d,%d)-(%d,%d)', [minx, miny, maxx, maxy]), MSG_NOTIFY);
1801 pvpcount := 0;
1802 for idx := 0 to High(pvpb) do begin pvpb[idx] := 0; pvpe[idx] := -1; end;
1803 tpc := 0;
1804 checkPanels(gWalls, 0);
1805 checkPanels(gRenderBackgrounds, 1);
1806 checkPanels(gRenderForegrounds, 2);
1807 checkPanels(gWater, 3);
1808 checkPanels(gAcid1, 4);
1809 checkPanels(gAcid2, 5);
1810 checkPanels(gSteps, 6);
1811 //e_WriteLog(Format('total panels: %d; visible panels: %d', [tpc, pvpcount]), MSG_NOTIFY);
1812 end;
1814 procedure g_Map_DrawPanelsOld(x0, y0, wdt, hgt: Integer; PanelType: Word);
1816 procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
1817 var
1818 idx: Integer;
1819 begin
1820 if (panels <> nil) and (stp >= 0) and (stp <= 6) then
1821 begin
1822 if pvpcount < 0 then
1823 begin
1824 // alas, no visible set
1825 for idx := 0 to High(panels) do
1826 begin
1827 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
1828 end;
1829 end
1830 else
1831 begin
1832 // wow, use visible set
1833 if pvpb[stp] <= pvpe[stp] then
1834 begin
1835 for idx := pvpb[stp] to pvpe[stp] do
1836 begin
1837 if not (drawDoors xor pvpset[idx].Door) then pvpset[idx].Draw();
1838 end;
1839 end;
1840 end;
1841 end;
1842 end;
1844 procedure dumpPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
1845 var
1846 idx: Integer;
1847 begin
1848 if (panels <> nil) and (stp >= 0) and (stp <= 6) then
1849 begin
1850 if pvpcount < 0 then
1851 begin
1852 // alas, no visible set
1853 for idx := 0 to High(panels) do
1854 begin
1855 if not (drawDoors xor panels[idx].Door) then
1856 begin
1857 e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [panels[idx].X, panels[idx].Y, panels[idx].Width, panels[idx].Height]), MSG_NOTIFY);
1858 end;
1859 end;
1860 end
1861 else
1862 begin
1863 // wow, use visible set
1864 if pvpb[stp] <= pvpe[stp] then
1865 begin
1866 for idx := pvpb[stp] to pvpe[stp] do
1867 begin
1868 if not (drawDoors xor pvpset[idx].Door) then
1869 begin
1870 e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [pvpset[idx].X, pvpset[idx].Y, pvpset[idx].Width, pvpset[idx].Height]), MSG_NOTIFY);
1871 end;
1872 end;
1873 end;
1874 end;
1875 end;
1876 end;
1878 function qq (obj: TObject; tag: Integer): Boolean;
1879 var
1880 pan: TPanel;
1881 begin
1882 result := false; // don't stop, ever
1884 //e_WriteLog(Format(' *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
1886 if obj = nil then
1887 begin
1888 e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
1889 end;
1890 if not (obj is TPanel) then
1891 begin
1892 e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
1893 exit;
1894 end;
1895 //pan := (obj as TPanel);
1896 //e_WriteLog(Format(' !body: (%d,%d)-(%dx%d) tag:%d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, tag, PanelType]), MSG_NOTIFY);
1898 if (tag = PANEL_WALL) then
1899 begin
1900 if (PanelType = PANEL_WALL) then
1901 begin
1902 pan := (obj as TPanel);
1903 if not pan.Door then
1904 begin
1905 //e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
1906 pan.Draw();
1907 end;
1908 end
1909 else if (PanelType = PANEL_CLOSEDOOR) then
1910 begin
1911 pan := (obj as TPanel);
1912 if pan.Door then
1913 begin
1914 //e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
1915 pan.Draw();
1916 end;
1917 end
1918 end
1919 else if (PanelType = tag) then
1920 begin
1921 pan := (obj as TPanel);
1922 if not pan.Door then
1923 begin
1924 //e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
1925 pan.Draw();
1926 end;
1927 end;
1928 end;
1930 begin
1931 //e_WriteLog(Format('QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
1932 gMapGrid.forEachInAABB(x0, y0, wdt, hgt, qq);
1933 (*
1934 case PanelType of
1935 PANEL_WALL: dumpPanels(0, gWalls);
1936 PANEL_CLOSEDOOR: dumpPanels(0, gWalls, True);
1937 PANEL_BACK: dumpPanels(1, gRenderBackgrounds);
1938 PANEL_FORE: dumpPanels(2, gRenderForegrounds);
1939 PANEL_WATER: dumpPanels(3, gWater);
1940 PANEL_ACID1: dumpPanels(4, gAcid1);
1941 PANEL_ACID2: dumpPanels(5, gAcid2);
1942 PANEL_STEP: dumpPanels(6, gSteps);
1943 end;
1944 *)
1946 (*
1947 case PanelType of
1948 PANEL_WALL: DrawPanels(0, gWalls);
1949 PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True);
1950 PANEL_BACK: DrawPanels(1, gRenderBackgrounds);
1951 PANEL_FORE: DrawPanels(2, gRenderForegrounds);
1952 PANEL_WATER: DrawPanels(3, gWater);
1953 PANEL_ACID1: DrawPanels(4, gAcid1);
1954 PANEL_ACID2: DrawPanels(5, gAcid2);
1955 PANEL_STEP: DrawPanels(6, gSteps);
1956 end;
1957 *)
1958 end;
1960 var
1961 gDrawPanelList: array of TPanel = nil;
1962 gDrawPanelListUsed: Integer = 0;
1964 procedure dplClear ();
1965 begin
1966 gDrawPanelListUsed := 0;
1967 end;
1969 procedure dplAddPanel (pan: TPanel);
1970 begin
1971 if pan = nil then exit;
1972 if gDrawPanelListUsed = Length(gDrawPanelList) then SetLength(gDrawPanelList, gDrawPanelListUsed+4096); // arbitrary number
1973 gDrawPanelList[gDrawPanelListUsed] := pan;
1974 Inc(gDrawPanelListUsed);
1975 end;
1977 function dplCompare (p0, p1: Pointer): LongInt; cdecl;
1978 var
1979 pan0, pan1: PPanel;
1980 begin
1981 pan0 := PPanel(p0);
1982 pan1 := PPanel(p1);
1983 if pan0.ArrIdx < pan1.ArrIdx then result := -1
1984 else if pan0.ArrIdx > pan1.ArrIdx then result := 1
1985 else result := 0;
1986 end;
1988 procedure dplSort();
1989 begin
1990 qsort(@gDrawPanelList[0], gDrawPanelListUsed, sizeof(TPanel), &dplCompare);
1991 end;
1993 procedure g_Map_DrawPanels(x0, y0, wdt, hgt: Integer; PanelType: Word);
1995 function qq (obj: TObject; tag: Integer): Boolean;
1996 var
1997 pan: TPanel;
1998 begin
1999 result := false; // don't stop, ever
2001 //e_WriteLog(Format(' *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2003 if obj = nil then
2004 begin
2005 e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2006 end;
2007 if not (obj is TPanel) then
2008 begin
2009 e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2010 exit;
2011 end;
2012 //pan := (obj as TPanel);
2013 //e_WriteLog(Format(' !body: (%d,%d)-(%dx%d) tag:%d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, tag, PanelType]), MSG_NOTIFY);
2015 if (tag = PANEL_WALL) then
2016 begin
2017 if (PanelType = PANEL_WALL) then
2018 begin
2019 pan := (obj as TPanel);
2020 if not pan.Door then
2021 begin
2022 //e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
2023 dplAddPanel(pan);
2024 end;
2025 end
2026 else if (PanelType = PANEL_CLOSEDOOR) then
2027 begin
2028 pan := (obj as TPanel);
2029 if pan.Door then
2030 begin
2031 //e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
2032 dplAddPanel(pan);
2033 end;
2034 end
2035 end
2036 else if (PanelType = tag) then
2037 begin
2038 pan := (obj as TPanel);
2039 if not pan.Door then
2040 begin
2041 //e_WriteLog(Format(' body hit: (%d,%d)-(%dx%d)', [pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
2042 dplAddPanel(pan);
2043 end;
2044 end;
2045 end;
2047 var
2048 idx: Integer;
2049 begin
2050 //e_WriteLog(Format('QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
2051 dplClear();
2052 gMapGrid.forEachInAABB(x0, y0, wdt, hgt, qq);
2053 // sort and draw the list (we need to sort it, or rendering is fucked)
2054 if gDrawPanelListUsed > 0 then
2055 begin
2056 dplSort();
2057 for idx := 0 to gDrawPanelListUsed-1 do gDrawPanelList[idx].Draw();
2058 end;
2059 dplClear();
2060 end;
2062 var
2063 plpset: array of Integer = nil; // potentially lit panels
2064 plpcount: Integer; // to avoid constant reallocations
2066 function g_Map_BuildPLP (ltminx, ltminy, ltmaxx, ltmaxy: Integer): Boolean;
2067 var
2068 idx: Integer;
2069 panels: TPanelArray;
2070 begin
2071 panels := gWalls;
2072 plpcount := 0;
2073 if (ltminx < ltmaxx) and (ltminy < ltmaxy) then
2074 begin
2075 if panels <> nil then
2076 begin
2077 for idx := 0 to High(panels) do
2078 begin
2079 if (panels[idx].Width < 1) or (panels[idx].Height < 1) then continue;
2080 if (panels[idx].X+panels[idx].Width <= ltminx) then continue;
2081 if (panels[idx].Y+panels[idx].Height <= ltminy) then continue;
2082 if (panels[idx].X > ltmaxx) then continue;
2083 if (panels[idx].Y > ltmaxy) then continue;
2084 if plpcount = length(plpset) then SetLength(plpset, plpcount+32768);
2085 plpset[plpcount] := idx;
2086 Inc(plpcount);
2087 end;
2088 //e_WriteLog(Format('%d panels left out of %d', [plpcount, Length(panels)]), MSG_NOTIFY);
2089 end;
2090 end;
2091 result := (plpcount > 0);
2092 end;
2094 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2096 (* old
2097 procedure drawPanels (var panels: TPanelArray);
2098 var
2099 a: Integer;
2100 begin
2101 if panels <> nil then
2102 begin
2103 for a := 0 to High(panels) do
2104 begin
2105 panels[a].DrawShadowVolume(lightX, lightY, radius);
2106 end;
2107 end;
2108 end;
2109 *)
2110 var
2111 idx: Integer;
2112 begin
2113 (*
2114 drawPanels(gWalls);
2115 //drawPanels(gRenderForegrounds);
2116 *)
2117 for idx := 0 to plpcount-1 do
2118 begin
2119 gWalls[plpset[idx]].DrawShadowVolume(lightX, lightY, radius);
2120 end;
2121 end;
2123 procedure g_Map_DrawBack(dx, dy: Integer);
2124 begin
2125 if gDrawBackGround and (BackID <> DWORD(-1)) then
2126 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2127 else
2128 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2129 end;
2131 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2132 PanelType: Word; b1x3: Boolean): Boolean;
2133 var
2134 a, h: Integer;
2135 begin
2136 Result := False;
2138 if WordBool(PanelType and PANEL_WALL) then
2139 if gWalls <> nil then
2140 begin
2141 h := High(gWalls);
2143 for a := 0 to h do
2144 if gWalls[a].Enabled and
2145 g_Collide(X, Y, Width, Height,
2146 gWalls[a].X, gWalls[a].Y,
2147 gWalls[a].Width, gWalls[a].Height) then
2148 begin
2149 Result := True;
2150 Exit;
2151 end;
2152 end;
2154 if WordBool(PanelType and PANEL_WATER) then
2155 if gWater <> nil then
2156 begin
2157 h := High(gWater);
2159 for a := 0 to h do
2160 if g_Collide(X, Y, Width, Height,
2161 gWater[a].X, gWater[a].Y,
2162 gWater[a].Width, gWater[a].Height) then
2163 begin
2164 Result := True;
2165 Exit;
2166 end;
2167 end;
2169 if WordBool(PanelType and PANEL_ACID1) then
2170 if gAcid1 <> nil then
2171 begin
2172 h := High(gAcid1);
2174 for a := 0 to h do
2175 if g_Collide(X, Y, Width, Height,
2176 gAcid1[a].X, gAcid1[a].Y,
2177 gAcid1[a].Width, gAcid1[a].Height) then
2178 begin
2179 Result := True;
2180 Exit;
2181 end;
2182 end;
2184 if WordBool(PanelType and PANEL_ACID2) then
2185 if gAcid2 <> nil then
2186 begin
2187 h := High(gAcid2);
2189 for a := 0 to h do
2190 if g_Collide(X, Y, Width, Height,
2191 gAcid2[a].X, gAcid2[a].Y,
2192 gAcid2[a].Width, gAcid2[a].Height) then
2193 begin
2194 Result := True;
2195 Exit;
2196 end;
2197 end;
2199 if WordBool(PanelType and PANEL_STEP) then
2200 if gSteps <> nil then
2201 begin
2202 h := High(gSteps);
2204 for a := 0 to h do
2205 if g_Collide(X, Y, Width, Height,
2206 gSteps[a].X, gSteps[a].Y,
2207 gSteps[a].Width, gSteps[a].Height) then
2208 begin
2209 Result := True;
2210 Exit;
2211 end;
2212 end;
2214 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2215 if gLifts <> nil then
2216 begin
2217 h := High(gLifts);
2219 for a := 0 to h do
2220 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2221 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2222 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2223 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2224 g_Collide(X, Y, Width, Height,
2225 gLifts[a].X, gLifts[a].Y,
2226 gLifts[a].Width, gLifts[a].Height) then
2227 begin
2228 Result := True;
2229 Exit;
2230 end;
2231 end;
2233 if WordBool(PanelType and PANEL_BLOCKMON) then
2234 if gBlockMon <> nil then
2235 begin
2236 h := High(gBlockMon);
2238 for a := 0 to h do
2239 if ( (not b1x3) or
2240 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2241 g_Collide(X, Y, Width, Height,
2242 gBlockMon[a].X, gBlockMon[a].Y,
2243 gBlockMon[a].Width, gBlockMon[a].Height) then
2244 begin
2245 Result := True;
2246 Exit;
2247 end;
2248 end;
2249 end;
2251 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2253 function qq (obj: TObject; tag: Integer): Boolean;
2254 var
2255 pan: TPanel;
2256 a: Integer;
2257 begin
2258 result := false; // don't stop, ever
2260 //e_WriteLog(Format(' *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2262 if obj = nil then
2263 begin
2264 e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2265 end;
2266 if not (obj is TPanel) then
2267 begin
2268 e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2269 exit;
2270 end;
2272 pan := (obj as TPanel);
2273 a := pan.ArrIdx;
2275 if WordBool(PanelType and PANEL_WALL) and WordBool(tag and PANEL_WALL) then
2276 begin
2277 if gWalls[a].Enabled and g_Collide(X, Y, Width, Height, gWalls[a].X, gWalls[a].Y, gWalls[a].Width, gWalls[a].Height) then
2278 begin
2279 result := true;
2280 exit;
2281 end;
2282 end;
2284 if WordBool(PanelType and PANEL_WATER) and WordBool(tag and PANEL_WATER) then
2285 begin
2286 if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then
2287 begin
2288 result := True;
2289 exit;
2290 end;
2291 end;
2293 if WordBool(PanelType and PANEL_ACID1) and WordBool(tag and PANEL_ACID1) then
2294 begin
2295 if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then
2296 begin
2297 result := True;
2298 exit;
2299 end;
2300 end;
2302 if WordBool(PanelType and PANEL_ACID2) and WordBool(tag and PANEL_ACID2) then
2303 begin
2304 if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then
2305 begin
2306 result := True;
2307 exit;
2308 end;
2309 end;
2311 if WordBool(PanelType and PANEL_STEP) and WordBool(tag and PANEL_STEP) then
2312 begin
2313 if g_Collide(X, Y, Width, Height, gSteps[a].X, gSteps[a].Y, gSteps[a].Width, gSteps[a].Height) then
2314 begin
2315 result := True;
2316 exit;
2317 end;
2318 end;
2319 end;
2321 var
2322 a, h: Integer;
2323 begin
2324 result := gMapGrid.forEachInAABB(X, Y, Width, Height, qq);
2325 if not result then
2326 begin
2327 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) and (gLifts <> nil) then
2328 begin
2329 h := High(gLifts);
2330 for a := 0 to h do
2331 begin
2332 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2333 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2334 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2335 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2336 g_Collide(X, Y, Width, Height,
2337 gLifts[a].X, gLifts[a].Y,
2338 gLifts[a].Width, gLifts[a].Height) then
2339 begin
2340 Result := True;
2341 Exit;
2342 end;
2343 end;
2344 end;
2346 if WordBool(PanelType and PANEL_BLOCKMON) and (gBlockMon <> nil) then
2347 begin
2348 h := High(gBlockMon);
2349 for a := 0 to h do
2350 begin
2351 if ( (not b1x3) or
2352 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2353 g_Collide(X, Y, Width, Height,
2354 gBlockMon[a].X, gBlockMon[a].Y,
2355 gBlockMon[a].Width, gBlockMon[a].Height) then
2356 begin
2357 Result := True;
2358 Exit;
2359 end;
2360 end;
2361 end;
2362 end;
2363 end;
2366 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2367 var
2368 a, h: Integer;
2369 begin
2370 Result := TEXTURE_NONE;
2372 if gWater <> nil then
2373 begin
2374 h := High(gWater);
2376 for a := 0 to h do
2377 if g_Collide(X, Y, Width, Height,
2378 gWater[a].X, gWater[a].Y,
2379 gWater[a].Width, gWater[a].Height) then
2380 begin
2381 Result := gWater[a].GetTextureID();
2382 Exit;
2383 end;
2384 end;
2386 if gAcid1 <> nil then
2387 begin
2388 h := High(gAcid1);
2390 for a := 0 to h do
2391 if g_Collide(X, Y, Width, Height,
2392 gAcid1[a].X, gAcid1[a].Y,
2393 gAcid1[a].Width, gAcid1[a].Height) then
2394 begin
2395 Result := gAcid1[a].GetTextureID();
2396 Exit;
2397 end;
2398 end;
2400 if gAcid2 <> nil then
2401 begin
2402 h := High(gAcid2);
2404 for a := 0 to h do
2405 if g_Collide(X, Y, Width, Height,
2406 gAcid2[a].X, gAcid2[a].Y,
2407 gAcid2[a].Width, gAcid2[a].Height) then
2408 begin
2409 Result := gAcid2[a].GetTextureID();
2410 Exit;
2411 end;
2412 end;
2413 end;
2415 procedure g_Map_EnableWall(ID: DWORD);
2416 begin
2417 with gWalls[ID] do
2418 begin
2419 Enabled := True;
2420 g_Mark(X, Y, Width, Height, MARK_DOOR, True);
2422 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2423 end;
2424 end;
2426 procedure g_Map_DisableWall(ID: DWORD);
2427 begin
2428 with gWalls[ID] do
2429 begin
2430 Enabled := False;
2431 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
2433 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2434 end;
2435 end;
2437 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2438 var
2439 tp: TPanel;
2440 begin
2441 case PanelType of
2442 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2443 tp := gWalls[ID];
2444 PANEL_FORE:
2445 tp := gRenderForegrounds[ID];
2446 PANEL_BACK:
2447 tp := gRenderBackgrounds[ID];
2448 PANEL_WATER:
2449 tp := gWater[ID];
2450 PANEL_ACID1:
2451 tp := gAcid1[ID];
2452 PANEL_ACID2:
2453 tp := gAcid2[ID];
2454 PANEL_STEP:
2455 tp := gSteps[ID];
2456 else
2457 Exit;
2458 end;
2460 tp.NextTexture(AnimLoop);
2461 if g_Game_IsServer and g_Game_IsNet then
2462 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2463 end;
2465 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2466 begin
2467 if gLifts[ID].LiftType = t then
2468 Exit;
2470 with gLifts[ID] do
2471 begin
2472 LiftType := t;
2474 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2476 if LiftType = 0 then
2477 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2478 else if LiftType = 1 then
2479 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2480 else if LiftType = 2 then
2481 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2482 else if LiftType = 3 then
2483 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2485 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2486 end;
2487 end;
2489 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2490 var
2491 a: Integer;
2492 PointsArray: Array of TRespawnPoint;
2493 begin
2494 Result := False;
2495 SetLength(PointsArray, 0);
2497 if RespawnPoints = nil then
2498 Exit;
2500 for a := 0 to High(RespawnPoints) do
2501 if RespawnPoints[a].PointType = PointType then
2502 begin
2503 SetLength(PointsArray, Length(PointsArray)+1);
2504 PointsArray[High(PointsArray)] := RespawnPoints[a];
2505 end;
2507 if PointsArray = nil then
2508 Exit;
2510 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2511 Result := True;
2512 end;
2514 function g_Map_GetPointCount(PointType: Byte): Word;
2515 var
2516 a: Integer;
2517 begin
2518 Result := 0;
2520 if RespawnPoints = nil then
2521 Exit;
2523 for a := 0 to High(RespawnPoints) do
2524 if RespawnPoints[a].PointType = PointType then
2525 Result := Result + 1;
2526 end;
2528 function g_Map_HaveFlagPoints(): Boolean;
2529 begin
2530 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2531 end;
2533 procedure g_Map_ResetFlag(Flag: Byte);
2534 begin
2535 with gFlags[Flag] do
2536 begin
2537 Obj.X := -1000;
2538 Obj.Y := -1000;
2539 Obj.Vel.X := 0;
2540 Obj.Vel.Y := 0;
2541 Direction := D_LEFT;
2542 State := FLAG_STATE_NONE;
2543 if FlagPoints[Flag] <> nil then
2544 begin
2545 Obj.X := FlagPoints[Flag]^.X;
2546 Obj.Y := FlagPoints[Flag]^.Y;
2547 Direction := FlagPoints[Flag]^.Direction;
2548 State := FLAG_STATE_NORMAL;
2549 end;
2550 Count := -1;
2551 end;
2552 end;
2554 procedure g_Map_DrawFlags();
2555 var
2556 i, dx: Integer;
2557 Mirror: TMirrorType;
2558 begin
2559 if gGameSettings.GameMode <> GM_CTF then
2560 Exit;
2562 for i := FLAG_RED to FLAG_BLUE do
2563 with gFlags[i] do
2564 if State <> FLAG_STATE_CAPTURED then
2565 begin
2566 if State = FLAG_STATE_NONE then
2567 continue;
2569 if Direction = D_LEFT then
2570 begin
2571 Mirror := M_HORIZONTAL;
2572 dx := -1;
2573 end
2574 else
2575 begin
2576 Mirror := M_NONE;
2577 dx := 1;
2578 end;
2580 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2582 if g_debug_Frames then
2583 begin
2584 e_DrawQuad(Obj.X+Obj.Rect.X,
2585 Obj.Y+Obj.Rect.Y,
2586 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2587 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2588 0, 255, 0);
2589 end;
2590 end;
2591 end;
2593 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2594 var
2595 dw: DWORD;
2596 b: Byte;
2597 str: String;
2598 boo: Boolean;
2600 procedure SavePanelArray(var panels: TPanelArray);
2601 var
2602 PAMem: TBinMemoryWriter;
2603 i: Integer;
2604 begin
2605 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2606 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2608 i := 0;
2609 while i < Length(panels) do
2610 begin
2611 if panels[i].SaveIt then
2612 begin
2613 // ID ïàíåëè:
2614 PAMem.WriteInt(i);
2615 // Ñîõðàíÿåì ïàíåëü:
2616 panels[i].SaveState(PAMem);
2617 end;
2618 Inc(i);
2619 end;
2621 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2622 PAMem.SaveToMemory(Mem);
2623 PAMem.Free();
2624 end;
2626 procedure SaveFlag(flag: PFlag);
2627 begin
2628 // Ñèãíàòóðà ôëàãà:
2629 dw := FLAG_SIGNATURE; // 'FLAG'
2630 Mem.WriteDWORD(dw);
2631 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2632 Mem.WriteByte(flag^.RespawnType);
2633 // Ñîñòîÿíèå ôëàãà:
2634 Mem.WriteByte(flag^.State);
2635 // Íàïðàâëåíèå ôëàãà:
2636 if flag^.Direction = D_LEFT then
2637 b := 1
2638 else // D_RIGHT
2639 b := 2;
2640 Mem.WriteByte(b);
2641 // Îáúåêò ôëàãà:
2642 Obj_SaveState(@flag^.Obj, Mem);
2643 end;
2645 begin
2646 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2648 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2649 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2650 SavePanelArray(gWalls);
2651 // Ñîõðàíÿåì ïàíåëè ôîíà:
2652 SavePanelArray(gRenderBackgrounds);
2653 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2654 SavePanelArray(gRenderForegrounds);
2655 // Ñîõðàíÿåì ïàíåëè âîäû:
2656 SavePanelArray(gWater);
2657 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2658 SavePanelArray(gAcid1);
2659 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2660 SavePanelArray(gAcid2);
2661 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2662 SavePanelArray(gSteps);
2663 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2664 SavePanelArray(gLifts);
2665 ///// /////
2667 ///// Ñîõðàíÿåì ìóçûêó: /////
2668 // Ñèãíàòóðà ìóçûêè:
2669 dw := MUSIC_SIGNATURE; // 'MUSI'
2670 Mem.WriteDWORD(dw);
2671 // Íàçâàíèå ìóçûêè:
2672 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2673 if gMusic.NoMusic then
2674 str := ''
2675 else
2676 str := gMusic.Name;
2677 Mem.WriteString(str, 64);
2678 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2679 dw := gMusic.GetPosition();
2680 Mem.WriteDWORD(dw);
2681 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2682 boo := gMusic.SpecPause;
2683 Mem.WriteBoolean(boo);
2684 ///// /////
2686 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2687 Mem.WriteInt(gTotalMonsters);
2688 ///// /////
2690 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2691 if gGameSettings.GameMode = GM_CTF then
2692 begin
2693 // Ôëàã Êðàñíîé êîìàíäû:
2694 SaveFlag(@gFlags[FLAG_RED]);
2695 // Ôëàã Ñèíåé êîìàíäû:
2696 SaveFlag(@gFlags[FLAG_BLUE]);
2697 end;
2698 ///// /////
2700 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2701 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2702 begin
2703 // Î÷êè Êðàñíîé êîìàíäû:
2704 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2705 // Î÷êè Ñèíåé êîìàíäû:
2706 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2707 end;
2708 ///// /////
2709 end;
2711 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2712 var
2713 dw: DWORD;
2714 b: Byte;
2715 str: String;
2716 boo: Boolean;
2718 procedure LoadPanelArray(var panels: TPanelArray);
2719 var
2720 PAMem: TBinMemoryReader;
2721 i, id: Integer;
2722 begin
2723 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
2724 PAMem := TBinMemoryReader.Create();
2725 PAMem.LoadFromMemory(Mem);
2727 for i := 0 to Length(panels)-1 do
2728 if panels[i].SaveIt then
2729 begin
2730 // ID ïàíåëè:
2731 PAMem.ReadInt(id);
2732 if id <> i then
2733 begin
2734 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
2735 end;
2736 // Çàãðóæàåì ïàíåëü:
2737 panels[i].LoadState(PAMem);
2738 end;
2740 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
2741 PAMem.Free();
2742 end;
2744 procedure LoadFlag(flag: PFlag);
2745 begin
2746 // Ñèãíàòóðà ôëàãà:
2747 Mem.ReadDWORD(dw);
2748 if dw <> FLAG_SIGNATURE then // 'FLAG'
2749 begin
2750 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
2751 end;
2752 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2753 Mem.ReadByte(flag^.RespawnType);
2754 // Ñîñòîÿíèå ôëàãà:
2755 Mem.ReadByte(flag^.State);
2756 // Íàïðàâëåíèå ôëàãà:
2757 Mem.ReadByte(b);
2758 if b = 1 then
2759 flag^.Direction := D_LEFT
2760 else // b = 2
2761 flag^.Direction := D_RIGHT;
2762 // Îáúåêò ôëàãà:
2763 Obj_LoadState(@flag^.Obj, Mem);
2764 end;
2766 begin
2767 if Mem = nil then
2768 Exit;
2770 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
2771 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
2772 LoadPanelArray(gWalls);
2773 // Çàãðóæàåì ïàíåëè ôîíà:
2774 LoadPanelArray(gRenderBackgrounds);
2775 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
2776 LoadPanelArray(gRenderForegrounds);
2777 // Çàãðóæàåì ïàíåëè âîäû:
2778 LoadPanelArray(gWater);
2779 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
2780 LoadPanelArray(gAcid1);
2781 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
2782 LoadPanelArray(gAcid2);
2783 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
2784 LoadPanelArray(gSteps);
2785 // Çàãðóæàåì ïàíåëè ëèôòîâ:
2786 LoadPanelArray(gLifts);
2787 ///// /////
2789 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé:
2790 g_GFX_Init();
2792 ///// Çàãðóæàåì ìóçûêó: /////
2793 // Ñèãíàòóðà ìóçûêè:
2794 Mem.ReadDWORD(dw);
2795 if dw <> MUSIC_SIGNATURE then // 'MUSI'
2796 begin
2797 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
2798 end;
2799 // Íàçâàíèå ìóçûêè:
2800 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
2801 Mem.ReadString(str);
2802 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2803 Mem.ReadDWORD(dw);
2804 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2805 Mem.ReadBoolean(boo);
2806 // Çàïóñêàåì ýòó ìóçûêó:
2807 gMusic.SetByName(str);
2808 gMusic.SpecPause := boo;
2809 gMusic.Play();
2810 gMusic.Pause(True);
2811 gMusic.SetPosition(dw);
2812 ///// /////
2814 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
2815 Mem.ReadInt(gTotalMonsters);
2816 ///// /////
2818 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
2819 if gGameSettings.GameMode = GM_CTF then
2820 begin
2821 // Ôëàã Êðàñíîé êîìàíäû:
2822 LoadFlag(@gFlags[FLAG_RED]);
2823 // Ôëàã Ñèíåé êîìàíäû:
2824 LoadFlag(@gFlags[FLAG_BLUE]);
2825 end;
2826 ///// /////
2828 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2829 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2830 begin
2831 // Î÷êè Êðàñíîé êîìàíäû:
2832 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
2833 // Î÷êè Ñèíåé êîìàíäû:
2834 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
2835 end;
2836 ///// /////
2837 end;
2839 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
2840 var
2841 Arr: TPanelArray;
2842 begin
2843 Result := nil;
2844 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
2845 Arr := PanelByID[PanelID].PWhere^;
2846 PanelArrayID := PanelByID[PanelID].PArrID;
2847 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
2848 end;
2850 end.