DEADSOFTWARE

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