DEADSOFTWARE

don't use tree for drawing by default
[d2df-sdl.git] / src / game / g_map.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_map;
19 interface
21 uses
22 e_graphics, g_basic, MAPSTRUCT, g_textures, Classes,
23 g_phys, wadreader, BinEditor, g_panel, g_grid, z_aabbtree, md5, 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_tree_draw: Boolean = false;
140 gdbg_map_use_tree_coldet: Boolean = false;
141 gdbg_map_dump_coldet_tree_queries: Boolean = false;
142 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
144 implementation
146 uses
147 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
148 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
149 g_options, MAPREADER, g_triggers, g_player, MAPDEF,
150 Math, g_monsters, g_saveload, g_language, g_netmsg,
151 utils, sfs, binheap,
152 ImagingTypes, Imaging, ImagingUtility,
153 ImagingGif, ImagingNetworkGraphics;
155 const
156 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
157 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
158 FLAG_SIGNATURE = $47414C46; // 'FLAG'
160 GridTagInvalid = 0;
161 GridTagWallDoor = $0001;
162 GridTagBack = $0002;
163 GridTagFore = $0004;
164 GridTagWater = $0008;
165 GridTagAcid1 = $0010;
166 GridTagAcid2 = $0020;
167 GridTagStep = $0040;
168 GridTagLift = $0080;
169 GridTagBlockMon = $0100;
172 function panelTypeToTag (panelType: Word): Integer;
173 begin
174 case panelType of
175 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagWallDoor; // gWalls
176 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
177 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
178 PANEL_WATER: result := GridTagWater; // gWater
179 PANEL_ACID1: result := GridTagAcid1; // gAcid1
180 PANEL_ACID2: result := GridTagAcid2; // gAcid2
181 PANEL_STEP: result := GridTagStep; // gSteps
182 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
183 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
184 else result := GridTagInvalid;
185 end;
186 end;
189 type
190 TPanelID = record
191 PWhere: ^TPanelArray;
192 PArrID: Integer;
193 end;
195 type
196 TDynAABBTreeMap = class(TDynAABBTree)
197 function getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean; override;
198 end;
200 function TDynAABBTreeMap.getFleshAABB (var aabb: AABB2D; flesh: TTreeFlesh): Boolean;
201 var
202 pan: TPanel;
203 begin
204 result := false;
205 if (flesh = nil) then exit;
206 pan := (flesh as TPanel);
207 if (pan.Width < 1) or (pan.Height < 1) then exit;
208 aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width, pan.Y+pan.Height);
209 //if (pan.Width = 1) then aabb.maxX += 1;
210 //if (pan.Height = 1) then aabb.maxY += 1;
211 if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
212 //e_WriteLog(Format('getFleshAABB(%d;%d) AABB:(%f,%f)-(%f,%f); valid=%d; volume=%f; x=%d; y=%d; w=%d; h=%d', [pan.tag, pan.ArrIdx, aabb.minX, aabb.minY, aabb.maxX, aabb.maxY, Integer(aabb.valid), aabb.volume, pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
213 result := aabb.valid;
214 end;
216 var
217 PanelById: array of TPanelID;
218 Textures: TLevelTextureArray;
219 RespawnPoints: Array of TRespawnPoint;
220 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
221 //DOMFlagPoints: Array of TFlagPoint;
222 gMapGrid: TBodyGrid = nil;
223 mapTree: TDynAABBTree = nil;
226 procedure g_Map_ProfilersBegin ();
227 begin
228 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('MAP COLLISION', g_profile_history_size);
229 profMapCollision.mainBegin(g_profile_collision);
230 // create sections
231 if g_profile_collision then
232 begin
233 profMapCollision.sectionBegin('wall coldet');
234 profMapCollision.sectionEnd();
235 profMapCollision.sectionBegin('liquid coldet');
236 profMapCollision.sectionEnd();
237 end;
238 end;
240 procedure g_Map_ProfilersEnd ();
241 begin
242 if (profMapCollision <> nil) then profMapCollision.mainEnd();
243 end;
246 function g_Map_IsSpecialTexture(Texture: String): Boolean;
247 begin
248 Result := (Texture = TEXTURE_NAME_WATER) or
249 (Texture = TEXTURE_NAME_ACID1) or
250 (Texture = TEXTURE_NAME_ACID2);
251 end;
253 procedure CreateDoorMap();
254 var
255 PanelArray: Array of record
256 X, Y: Integer;
257 Width, Height: Word;
258 Active: Boolean;
259 PanelID: DWORD;
260 end;
261 a, b, c, m, i, len: Integer;
262 ok: Boolean;
263 begin
264 if gWalls = nil then
265 Exit;
267 i := 0;
268 len := 128;
269 SetLength(PanelArray, len);
271 for a := 0 to High(gWalls) do
272 if gWalls[a].Door then
273 begin
274 PanelArray[i].X := gWalls[a].X;
275 PanelArray[i].Y := gWalls[a].Y;
276 PanelArray[i].Width := gWalls[a].Width;
277 PanelArray[i].Height := gWalls[a].Height;
278 PanelArray[i].Active := True;
279 PanelArray[i].PanelID := a;
281 i := i + 1;
282 if i = len then
283 begin
284 len := len + 128;
285 SetLength(PanelArray, len);
286 end;
287 end;
289 // Íåò äâåðåé:
290 if i = 0 then
291 begin
292 PanelArray := nil;
293 Exit;
294 end;
296 SetLength(gDoorMap, 0);
298 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
300 for a := 0 to i-1 do
301 if PanelArray[a].Active then
302 begin
303 PanelArray[a].Active := False;
304 m := Length(gDoorMap);
305 SetLength(gDoorMap, m+1);
306 SetLength(gDoorMap[m], 1);
307 gDoorMap[m, 0] := PanelArray[a].PanelID;
308 ok := True;
310 while ok do
311 begin
312 ok := False;
314 for b := 0 to i-1 do
315 if PanelArray[b].Active then
316 for c := 0 to High(gDoorMap[m]) do
317 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
318 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
319 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
320 PanelArray[b].Width, PanelArray[b].Height,
321 gWalls[gDoorMap[m, c]].X,
322 gWalls[gDoorMap[m, c]].Y,
323 gWalls[gDoorMap[m, c]].Width,
324 gWalls[gDoorMap[m, c]].Height) then
325 begin
326 PanelArray[b].Active := False;
327 SetLength(gDoorMap[m],
328 Length(gDoorMap[m])+1);
329 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
330 ok := True;
331 Break;
332 end;
333 end;
335 g_Game_StepLoading();
336 end;
338 PanelArray := nil;
339 end;
341 procedure CreateLiftMap();
342 var
343 PanelArray: Array of record
344 X, Y: Integer;
345 Width, Height: Word;
346 Active: Boolean;
347 end;
348 a, b, c, len, i, j: Integer;
349 ok: Boolean;
350 begin
351 if gLifts = nil then
352 Exit;
354 len := Length(gLifts);
355 SetLength(PanelArray, len);
357 for a := 0 to len-1 do
358 begin
359 PanelArray[a].X := gLifts[a].X;
360 PanelArray[a].Y := gLifts[a].Y;
361 PanelArray[a].Width := gLifts[a].Width;
362 PanelArray[a].Height := gLifts[a].Height;
363 PanelArray[a].Active := True;
364 end;
366 SetLength(gLiftMap, len);
367 i := 0;
369 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
371 for a := 0 to len-1 do
372 if PanelArray[a].Active then
373 begin
374 PanelArray[a].Active := False;
375 SetLength(gLiftMap[i], 32);
376 j := 0;
377 gLiftMap[i, j] := a;
378 ok := True;
380 while ok do
381 begin
382 ok := False;
383 for b := 0 to len-1 do
384 if PanelArray[b].Active then
385 for c := 0 to j do
386 if g_CollideAround(PanelArray[b].X,
387 PanelArray[b].Y,
388 PanelArray[b].Width,
389 PanelArray[b].Height,
390 PanelArray[gLiftMap[i, c]].X,
391 PanelArray[gLiftMap[i, c]].Y,
392 PanelArray[gLiftMap[i, c]].Width,
393 PanelArray[gLiftMap[i, c]].Height) then
394 begin
395 PanelArray[b].Active := False;
396 j := j+1;
397 if j > High(gLiftMap[i]) then
398 SetLength(gLiftMap[i],
399 Length(gLiftMap[i])+32);
401 gLiftMap[i, j] := b;
402 ok := True;
404 Break;
405 end;
406 end;
408 SetLength(gLiftMap[i], j+1);
409 i := i+1;
411 g_Game_StepLoading();
412 end;
414 SetLength(gLiftMap, i);
416 PanelArray := nil;
417 end;
419 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
420 CurTex: Integer; sav: Boolean): Integer;
421 var
422 len: Integer;
423 panels: ^TPanelArray;
424 begin
425 Result := -1;
427 case PanelRec.PanelType of
428 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
429 panels := @gWalls;
430 PANEL_BACK:
431 panels := @gRenderBackgrounds;
432 PANEL_FORE:
433 panels := @gRenderForegrounds;
434 PANEL_WATER:
435 panels := @gWater;
436 PANEL_ACID1:
437 panels := @gAcid1;
438 PANEL_ACID2:
439 panels := @gAcid2;
440 PANEL_STEP:
441 panels := @gSteps;
442 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
443 panels := @gLifts;
444 PANEL_BLOCKMON:
445 panels := @gBlockMon;
446 else
447 Exit;
448 end;
450 len := Length(panels^);
451 SetLength(panels^, len + 1);
453 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
454 panels^[len].ArrIdx := len;
455 panels^[len].tag := panelTypeToTag(PanelRec.PanelType);
456 if sav then
457 panels^[len].SaveIt := True;
459 Result := len;
461 len := Length(PanelByID);
462 SetLength(PanelByID, len + 1);
463 PanelByID[len].PWhere := panels;
464 PanelByID[len].PArrID := Result;
465 end;
467 function CreateNullTexture(RecName: String): Integer;
468 begin
469 SetLength(Textures, Length(Textures)+1);
470 result := High(Textures);
472 with Textures[High(Textures)] do
473 begin
474 TextureName := RecName;
475 Width := 1;
476 Height := 1;
477 Anim := False;
478 TextureID := TEXTURE_NONE;
479 end;
480 end;
482 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
483 var
484 WAD: TWADFile;
485 TextureData: Pointer;
486 WADName, txname: String;
487 a, ResLength: Integer;
488 begin
489 Result := -1;
491 if Textures <> nil then
492 for a := 0 to High(Textures) do
493 if Textures[a].TextureName = RecName then
494 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
495 Result := a;
496 Exit;
497 end;
499 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
500 if (RecName = TEXTURE_NAME_WATER) or
501 (RecName = TEXTURE_NAME_ACID1) or
502 (RecName = TEXTURE_NAME_ACID2) then
503 begin
504 SetLength(Textures, Length(Textures)+1);
506 with Textures[High(Textures)] do
507 begin
508 TextureName := RecName;
510 if TextureName = TEXTURE_NAME_WATER then
511 TextureID := TEXTURE_SPECIAL_WATER
512 else
513 if TextureName = TEXTURE_NAME_ACID1 then
514 TextureID := TEXTURE_SPECIAL_ACID1
515 else
516 if TextureName = TEXTURE_NAME_ACID2 then
517 TextureID := TEXTURE_SPECIAL_ACID2;
519 Anim := False;
520 end;
522 result := High(Textures);
523 Exit;
524 end;
526 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
527 WADName := g_ExtractWadName(RecName);
529 WAD := TWADFile.Create();
531 if WADName <> '' then
532 WADName := GameDir+'/wads/'+WADName
533 else
534 WADName := Map;
536 WAD.ReadFile(WADName);
538 txname := RecName;
540 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
541 begin
542 FreeMem(TextureData);
543 RecName := 'COMMON\ALIEN';
544 end;
547 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
548 begin
549 SetLength(Textures, Length(Textures)+1);
550 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
551 Exit;
552 e_GetTextureSize(Textures[High(Textures)].TextureID,
553 @Textures[High(Textures)].Width,
554 @Textures[High(Textures)].Height);
555 FreeMem(TextureData);
556 Textures[High(Textures)].TextureName := {RecName}txname;
557 Textures[High(Textures)].Anim := False;
559 result := High(Textures);
560 end
561 else // Íåò òàêîãî ðåóñðñà â WAD'å
562 begin
563 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
564 if log then
565 begin
566 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
567 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
568 end;
569 end;
571 WAD.Free();
572 end;
574 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
575 var
576 WAD: TWADFile;
577 TextureWAD: PChar = nil;
578 TextData: Pointer = nil;
579 TextureData: Pointer = nil;
580 cfg: TConfig = nil;
581 WADName: String;
582 ResLength: Integer;
583 TextureResource: String;
584 _width, _height, _framecount, _speed: Integer;
585 _backanimation: Boolean;
586 //imgfmt: string;
587 ia: TDynImageDataArray = nil;
588 f, c, frdelay, frloop: Integer;
589 begin
590 result := -1;
592 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
594 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
595 WADName := g_ExtractWadName(RecName);
597 WAD := TWADFile.Create();
598 try
599 if WADName <> '' then
600 WADName := GameDir+'/wads/'+WADName
601 else
602 WADName := Map;
604 WAD.ReadFile(WADName);
606 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
607 begin
608 if log then
609 begin
610 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
611 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
612 end;
613 exit;
614 end;
616 {TEST
617 if WADName = Map then
618 begin
619 //FreeMem(TextureWAD);
620 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
621 end;
624 WAD.FreeWAD();
626 if ResLength < 6 then
627 begin
628 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
629 exit;
630 end;
632 // ýòî ïòèöà? ýòî ñàìîë¸ò?
633 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
634 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
635 begin
636 // íåò, ýòî ñóïåðìåí!
637 if not WAD.ReadMemory(TextureWAD, ResLength) then
638 begin
639 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
640 exit;
641 end;
643 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
644 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
645 begin
646 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
647 exit;
648 end;
650 cfg := TConfig.CreateMem(TextData, ResLength);
652 TextureResource := cfg.ReadStr('', 'resource', '');
653 if TextureResource = '' then
654 begin
655 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
656 exit;
657 end;
659 _width := cfg.ReadInt('', 'framewidth', 0);
660 _height := cfg.ReadInt('', 'frameheight', 0);
661 _framecount := cfg.ReadInt('', 'framecount', 0);
662 _speed := cfg.ReadInt('', 'waitcount', 0);
663 _backanimation := cfg.ReadBool('', 'backanimation', False);
665 cfg.Free();
666 cfg := nil;
668 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
669 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
670 begin
671 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
672 exit;
673 end;
675 WAD.Free();
676 WAD := nil;
678 SetLength(Textures, Length(Textures)+1);
679 with Textures[High(Textures)] do
680 begin
681 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
682 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
683 begin
684 TextureName := RecName;
685 Width := _width;
686 Height := _height;
687 Anim := True;
688 FramesCount := _framecount;
689 Speed := _speed;
690 result := High(Textures);
691 end
692 else
693 begin
694 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
695 end;
696 end;
697 end
698 else
699 begin
700 // try animated image
702 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
703 if length(imgfmt) = 0 then
704 begin
705 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
706 exit;
707 end;
709 GlobalMetadata.ClearMetaItems();
710 GlobalMetadata.ClearMetaItemsForSaving();
711 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
712 begin
713 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
714 exit;
715 end;
716 if length(ia) = 0 then
717 begin
718 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
719 exit;
720 end;
722 WAD.Free();
723 WAD := nil;
725 _width := ia[0].width;
726 _height := ia[0].height;
727 _framecount := length(ia);
728 _speed := 1;
729 _backanimation := false;
730 frdelay := -1;
731 frloop := -666;
732 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
733 begin
734 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
735 try
736 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
737 frdelay := f;
738 if f < 0 then f := 0;
739 // rounding ;-)
740 c := f mod 28;
741 if c < 13 then c := 0 else c := 1;
742 f := (f div 28)+c;
743 if f < 1 then f := 1 else if f > 255 then f := 255;
744 _speed := f;
745 except
746 end;
747 end;
748 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
749 begin
750 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
751 try
752 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
753 frloop := f;
754 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
755 except
756 end;
757 end;
758 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
759 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
760 f := ord(_backanimation);
761 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);
763 SetLength(Textures, Length(Textures)+1);
764 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
765 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
766 begin
767 Textures[High(Textures)].TextureName := RecName;
768 Textures[High(Textures)].Width := _width;
769 Textures[High(Textures)].Height := _height;
770 Textures[High(Textures)].Anim := True;
771 Textures[High(Textures)].FramesCount := length(ia);
772 Textures[High(Textures)].Speed := _speed;
773 result := High(Textures);
774 //writeln(' CREATED!');
775 end
776 else
777 begin
778 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
779 end;
780 end;
781 finally
782 for f := 0 to High(ia) do FreeImage(ia[f]);
783 WAD.Free();
784 cfg.Free();
785 if TextureWAD <> nil then FreeMem(TextureWAD);
786 if TextData <> nil then FreeMem(TextData);
787 if TextureData <> nil then FreeMem(TextureData);
788 end;
789 end;
791 procedure CreateItem(Item: TItemRec_1);
792 begin
793 if g_Game_IsClient then Exit;
795 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
796 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
797 Exit;
799 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
800 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
801 end;
803 procedure CreateArea(Area: TAreaRec_1);
804 var
805 a: Integer;
806 id: DWORD;
807 begin
808 case Area.AreaType of
809 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
810 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
811 begin
812 SetLength(RespawnPoints, Length(RespawnPoints)+1);
813 with RespawnPoints[High(RespawnPoints)] do
814 begin
815 X := Area.X;
816 Y := Area.Y;
817 Direction := TDirection(Area.Direction);
819 case Area.AreaType of
820 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
821 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
822 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
823 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
824 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
825 end;
826 end;
827 end;
829 AREA_REDFLAG, AREA_BLUEFLAG:
830 begin
831 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
833 if FlagPoints[a] <> nil then Exit;
835 New(FlagPoints[a]);
837 with FlagPoints[a]^ do
838 begin
839 X := Area.X-FLAGRECT.X;
840 Y := Area.Y-FLAGRECT.Y;
841 Direction := TDirection(Area.Direction);
842 end;
844 with gFlags[a] do
845 begin
846 case a of
847 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
848 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
849 end;
851 Animation := TAnimation.Create(id, True, 8);
852 Obj.Rect := FLAGRECT;
854 g_Map_ResetFlag(a);
855 end;
856 end;
858 AREA_DOMFLAG:
859 begin
860 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
861 with DOMFlagPoints[High(DOMFlagPoints)] do
862 begin
863 X := Area.X;
864 Y := Area.Y;
865 Direction := TDirection(Area.Direction);
866 end;
868 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
869 end;
870 end;
871 end;
873 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
874 var
875 _trigger: TTrigger;
876 begin
877 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
879 with _trigger do
880 begin
881 X := Trigger.X;
882 Y := Trigger.Y;
883 Width := Trigger.Width;
884 Height := Trigger.Height;
885 Enabled := ByteBool(Trigger.Enabled);
886 TexturePanel := Trigger.TexturePanel;
887 TexturePanelType := fTexturePanel1Type;
888 ShotPanelType := fTexturePanel2Type;
889 TriggerType := Trigger.TriggerType;
890 ActivateType := Trigger.ActivateType;
891 Keys := Trigger.Keys;
892 Data.Default := Trigger.DATA;
893 end;
895 g_Triggers_Create(_trigger);
896 end;
898 procedure CreateMonster(monster: TMonsterRec_1);
899 var
900 a, i: Integer;
901 begin
902 if g_Game_IsClient then Exit;
904 if (gGameSettings.GameType = GT_SINGLE)
905 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
906 begin
907 i := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y,
908 TDirection(monster.Direction));
910 if gTriggers <> nil then
911 for a := 0 to High(gTriggers) do
912 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
913 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
914 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
915 gMonsters[i].AddTrigger(a);
917 if monster.MonsterType <> MONSTER_BARREL then
918 Inc(gTotalMonsters);
919 end;
920 end;
922 procedure g_Map_ReAdd_DieTriggers();
923 var
924 i, a: Integer;
925 begin
926 if g_Game_IsClient then Exit;
928 for i := 0 to High(gMonsters) do
929 if gMonsters[i] <> nil then
930 begin
931 gMonsters[i].ClearTriggers();
933 for a := 0 to High(gTriggers) do
934 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
935 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
936 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
937 gMonsters[i].AddTrigger(a);
938 end;
939 end;
941 function extractWadName(resourceName: string): string;
942 var
943 posN: Integer;
944 begin
945 posN := Pos(':', resourceName);
946 if posN > 0 then
947 Result:= Copy(resourceName, 0, posN-1)
948 else
949 Result := '';
950 end;
952 procedure addResToExternalResList(res: string);
953 begin
954 res := extractWadName(res);
955 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
956 gExternalResources.Add(res);
957 end;
959 procedure generateExternalResourcesList(mapReader: TMapReader_1);
960 var
961 textures: TTexturesRec1Array;
962 mapHeader: TMapHeaderRec_1;
963 i: integer;
964 resFile: String = '';
965 begin
966 if gExternalResources = nil then
967 gExternalResources := TStringList.Create;
969 gExternalResources.Clear;
970 textures := mapReader.GetTextures();
971 for i := 0 to High(textures) do
972 begin
973 addResToExternalResList(resFile);
974 end;
976 textures := nil;
978 mapHeader := mapReader.GetMapHeader;
980 addResToExternalResList(mapHeader.MusicName);
981 addResToExternalResList(mapHeader.SkyName);
982 end;
984 procedure mapCreateGrid ();
985 var
986 mapX0: Integer = $3fffffff;
987 mapY0: Integer = $3fffffff;
988 mapX1: Integer = -$3fffffff;
989 mapY1: Integer = -$3fffffff;
991 procedure fixMinMax (var panels: TPanelArray);
992 var
993 idx: Integer;
994 begin
995 for idx := 0 to High(panels) do
996 begin
997 if (panels[idx].Width < 1) or (panels[idx].Height < 1) then continue;
998 if mapX0 > panels[idx].X then mapX0 := panels[idx].X;
999 if mapY0 > panels[idx].Y then mapY0 := panels[idx].Y;
1000 if mapX1 < panels[idx].X+panels[idx].Width-1 then mapX1 := panels[idx].X+panels[idx].Width-1;
1001 if mapY1 < panels[idx].Y+panels[idx].Height-1 then mapY1 := panels[idx].Y+panels[idx].Height-1;
1002 end;
1003 end;
1005 procedure addPanelsToGrid (var panels: TPanelArray; tag: Integer);
1006 var
1007 idx: Integer;
1008 begin
1009 tag := panelTypeToTag(tag);
1010 for idx := High(panels) downto 0 do
1011 begin
1012 panels[idx].tag := tag;
1013 gMapGrid.insertBody(panels[idx], panels[idx].X, panels[idx].Y, panels[idx].Width, panels[idx].Height, tag);
1014 mapTree.insertObject(panels[idx], tag, true); // as static object
1015 end;
1016 end;
1018 begin
1019 gMapGrid.Free();
1020 gMapGrid := nil;
1021 mapTree.Free();
1022 mapTree := nil;
1024 fixMinMax(gWalls);
1025 fixMinMax(gRenderBackgrounds);
1026 fixMinMax(gRenderForegrounds);
1027 fixMinMax(gWater);
1028 fixMinMax(gAcid1);
1029 fixMinMax(gAcid2);
1030 fixMinMax(gSteps);
1031 fixMinMax(gLifts);
1032 fixMinMax(gBlockMon);
1034 if (mapX0 < 0) or (mapY0 < 0) then
1035 begin
1036 e_WriteLog(Format('funny map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING);
1037 //raise Exception.Create('we are fucked');
1038 end;
1040 gMapGrid := TBodyGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
1041 mapTree := TDynAABBTreeMap.Create();
1043 addPanelsToGrid(gWalls, PANEL_WALL); // and PANEL_CLOSEDOOR
1044 addPanelsToGrid(gRenderBackgrounds, PANEL_BACK);
1045 addPanelsToGrid(gRenderForegrounds, PANEL_FORE);
1046 addPanelsToGrid(gWater, PANEL_WATER);
1047 addPanelsToGrid(gAcid1, PANEL_ACID1);
1048 addPanelsToGrid(gAcid2, PANEL_ACID2);
1049 addPanelsToGrid(gSteps, PANEL_STEP);
1050 addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here
1051 addPanelsToGrid(gBlockMon, PANEL_BLOCKMON);
1053 gMapGrid.dumpStats();
1054 e_WriteLog(Format('tree depth: %d; %d nodes used, %d nodes allocated', [mapTree.computeTreeHeight, mapTree.nodeCount, mapTree.nodeAlloced]), MSG_NOTIFY);
1055 mapTree.forEachLeaf(nil);
1056 end;
1058 function g_Map_Load(Res: String): Boolean;
1059 const
1060 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1061 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1062 var
1063 WAD: TWADFile;
1064 MapReader: TMapReader_1;
1065 Header: TMapHeaderRec_1;
1066 _textures: TTexturesRec1Array;
1067 _texnummap: array of Integer; // `_textures` -> `Textures`
1068 panels: TPanelsRec1Array;
1069 items: TItemsRec1Array;
1070 monsters: TMonsterRec1Array;
1071 areas: TAreasRec1Array;
1072 triggers: TTriggersRec1Array;
1073 a, b, c, k: Integer;
1074 PanelID: DWORD;
1075 AddTextures: TAddTextureArray;
1076 texture: TTextureRec_1;
1077 TriggersTable: Array of record
1078 TexturePanel: Integer;
1079 LiftPanel: Integer;
1080 DoorPanel: Integer;
1081 ShotPanel: Integer;
1082 end;
1083 FileName, mapResName, s, TexName: String;
1084 Data: Pointer;
1085 Len: Integer;
1086 ok, isAnim, trigRef: Boolean;
1087 CurTex, ntn: Integer;
1089 begin
1090 gMapGrid.Free();
1091 gMapGrid := nil;
1092 mapTree.Free();
1093 mapTree := nil;
1095 Result := False;
1096 gMapInfo.Map := Res;
1097 TriggersTable := nil;
1098 FillChar(texture, SizeOf(texture), 0);
1100 sfsGCDisable(); // temporary disable removing of temporary volumes
1101 try
1102 // Çàãðóçêà WAD:
1103 FileName := g_ExtractWadName(Res);
1104 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1105 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1107 WAD := TWADFile.Create();
1108 if not WAD.ReadFile(FileName) then
1109 begin
1110 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1111 WAD.Free();
1112 Exit;
1113 end;
1114 //k8: why loader ignores path here?
1115 mapResName := g_ExtractFileName(Res);
1116 if not WAD.GetMapResource(mapResName, Data, Len) then
1117 begin
1118 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1119 WAD.Free();
1120 Exit;
1121 end;
1123 WAD.Free();
1125 // Çàãðóçêà êàðòû:
1126 e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
1127 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1128 MapReader := TMapReader_1.Create();
1130 if not MapReader.LoadMap(Data) then
1131 begin
1132 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1133 FreeMem(Data);
1134 MapReader.Free();
1135 Exit;
1136 end;
1138 FreeMem(Data);
1139 generateExternalResourcesList(MapReader);
1140 // Çàãðóçêà òåêñòóð:
1141 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1142 _textures := MapReader.GetTextures();
1143 _texnummap := nil;
1145 // Äîáàâëåíèå òåêñòóð â Textures[]:
1146 if _textures <> nil then
1147 begin
1148 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1149 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
1150 SetLength(_texnummap, length(_textures));
1152 for a := 0 to High(_textures) do
1153 begin
1154 SetLength(s, 64);
1155 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
1156 for b := 1 to Length(s) do
1157 if s[b] = #0 then
1158 begin
1159 SetLength(s, b-1);
1160 Break;
1161 end;
1162 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1163 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1164 // Àíèìèðîâàííàÿ òåêñòóðà:
1165 if ByteBool(_textures[a].Anim) then
1166 begin
1167 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1168 if ntn < 0 then
1169 begin
1170 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1171 ntn := CreateNullTexture(_textures[a].Resource);
1172 end;
1173 end
1174 else // Îáû÷íàÿ òåêñòóðà:
1175 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1176 if ntn < 0 then
1177 begin
1178 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1179 ntn := CreateNullTexture(_textures[a].Resource);
1180 end;
1182 _texnummap[a] := ntn; // fix texture number
1183 g_Game_StepLoading();
1184 end;
1185 end;
1187 // Çàãðóçêà òðèããåðîâ:
1188 gTriggerClientID := 0;
1189 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1190 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1191 triggers := MapReader.GetTriggers();
1193 // Çàãðóçêà ïàíåëåé:
1194 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1195 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1196 panels := MapReader.GetPanels();
1198 // check texture numbers for panels
1199 for a := 0 to High(panels) do
1200 begin
1201 if panels[a].TextureNum > High(_textures) then
1202 begin
1203 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1204 result := false;
1205 exit;
1206 end;
1207 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1208 end;
1210 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1211 if triggers <> nil then
1212 begin
1213 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1214 SetLength(TriggersTable, Length(triggers));
1215 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1217 for a := 0 to High(TriggersTable) do
1218 begin
1219 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1220 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1221 // Ëèôòû:
1222 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1223 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1224 else
1225 TriggersTable[a].LiftPanel := -1;
1226 // Äâåðè:
1227 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1228 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1229 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1230 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1231 else
1232 TriggersTable[a].DoorPanel := -1;
1233 // Òóðåëü:
1234 if triggers[a].TriggerType = TRIGGER_SHOT then
1235 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1236 else
1237 TriggersTable[a].ShotPanel := -1;
1239 g_Game_StepLoading();
1240 end;
1241 end;
1243 // Ñîçäàåì ïàíåëè:
1244 if panels <> nil then
1245 begin
1246 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1247 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1249 for a := 0 to High(panels) do
1250 begin
1251 SetLength(AddTextures, 0);
1252 trigRef := False;
1253 CurTex := -1;
1254 if _textures <> nil then
1255 begin
1256 texture := _textures[panels[a].TextureNum];
1257 ok := True;
1258 end
1259 else
1260 ok := False;
1262 if ok then
1263 begin
1264 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1265 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1266 ok := False;
1267 if (TriggersTable <> nil) and (_textures <> nil) then
1268 for b := 0 to High(TriggersTable) do
1269 if (TriggersTable[b].TexturePanel = a)
1270 or (TriggersTable[b].ShotPanel = a) then
1271 begin
1272 trigRef := True;
1273 ok := True;
1274 Break;
1275 end;
1276 end;
1278 if ok then
1279 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1280 SetLength(s, 64);
1281 CopyMemory(@s[1], @texture.Resource[0], 64);
1282 // Èçìåðÿåì äëèíó:
1283 Len := Length(s);
1284 for c := Len downto 1 do
1285 if s[c] <> #0 then
1286 begin
1287 Len := c;
1288 Break;
1289 end;
1290 SetLength(s, Len);
1292 // Ñïåö-òåêñòóðû çàïðåùåíû:
1293 if g_Map_IsSpecialTexture(s) then
1294 ok := False
1295 else
1296 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1297 ok := g_Texture_NumNameFindStart(s);
1299 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1300 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1301 if ok then
1302 begin
1303 k := NNF_NAME_BEFORE;
1304 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1305 while ok or (k = NNF_NAME_BEFORE) or
1306 (k = NNF_NAME_EQUALS) do
1307 begin
1308 k := g_Texture_NumNameFindNext(TexName);
1310 if (k = NNF_NAME_BEFORE) or
1311 (k = NNF_NAME_AFTER) then
1312 begin
1313 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1314 if ByteBool(texture.Anim) then
1315 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1316 isAnim := True;
1317 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1318 if not ok then
1319 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1320 isAnim := False;
1321 ok := CreateTexture(TexName, FileName, False) >= 0;
1322 end;
1323 end
1324 else
1325 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1326 isAnim := False;
1327 ok := CreateTexture(TexName, FileName, False) >= 0;
1328 if not ok then
1329 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1330 isAnim := True;
1331 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1332 end;
1333 end;
1335 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1336 if ok then
1337 begin
1338 for c := 0 to High(Textures) do
1339 if Textures[c].TextureName = TexName then
1340 begin
1341 SetLength(AddTextures, Length(AddTextures)+1);
1342 AddTextures[High(AddTextures)].Texture := c;
1343 AddTextures[High(AddTextures)].Anim := isAnim;
1344 Break;
1345 end;
1346 end;
1347 end
1348 else
1349 if k = NNF_NAME_EQUALS then
1350 begin
1351 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1352 SetLength(AddTextures, Length(AddTextures)+1);
1353 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1354 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1355 CurTex := High(AddTextures);
1356 ok := True;
1357 end
1358 else // NNF_NO_NAME
1359 ok := False;
1360 end; // while ok...
1362 ok := True;
1363 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1364 end; // if ok - ññûëàþòñÿ òðèããåðû
1366 if not ok then
1367 begin
1368 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1369 SetLength(AddTextures, 1);
1370 AddTextures[0].Texture := panels[a].TextureNum;
1371 AddTextures[0].Anim := ByteBool(texture.Anim);
1372 CurTex := 0;
1373 end;
1375 //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);
1377 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1378 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1380 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1381 if TriggersTable <> nil then
1382 for b := 0 to High(TriggersTable) do
1383 begin
1384 // Òðèããåð äâåðè/ëèôòà:
1385 if (TriggersTable[b].LiftPanel = a) or
1386 (TriggersTable[b].DoorPanel = a) then
1387 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1388 // Òðèããåð ñìåíû òåêñòóðû:
1389 if TriggersTable[b].TexturePanel = a then
1390 triggers[b].TexturePanel := PanelID;
1391 // Òðèããåð "Òóðåëü":
1392 if TriggersTable[b].ShotPanel = a then
1393 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1394 end;
1396 g_Game_StepLoading();
1397 end;
1398 end;
1400 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1401 if (triggers <> nil) and not gLoadGameMode then
1402 begin
1403 e_WriteLog(' Creating triggers...', MSG_NOTIFY);
1404 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1405 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1406 for a := 0 to High(triggers) do
1407 begin
1408 if triggers[a].TexturePanel <> -1 then
1409 b := panels[TriggersTable[a].TexturePanel].PanelType
1410 else
1411 b := 0;
1412 if (triggers[a].TriggerType = TRIGGER_SHOT) and
1413 (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1414 c := panels[TriggersTable[a].ShotPanel].PanelType
1415 else
1416 c := 0;
1417 CreateTrigger(triggers[a], b, c);
1418 end;
1419 end;
1421 // Çàãðóçêà ïðåäìåòîâ:
1422 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1423 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1424 items := MapReader.GetItems();
1426 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1427 if (items <> nil) and not gLoadGameMode then
1428 begin
1429 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1430 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1431 for a := 0 to High(items) do
1432 CreateItem(Items[a]);
1433 end;
1435 // Çàãðóçêà îáëàñòåé:
1436 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1437 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1438 areas := MapReader.GetAreas();
1440 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1441 if areas <> nil then
1442 begin
1443 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1444 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1445 for a := 0 to High(areas) do
1446 CreateArea(areas[a]);
1447 end;
1449 // Çàãðóçêà ìîíñòðîâ:
1450 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1451 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1452 monsters := MapReader.GetMonsters();
1454 gTotalMonsters := 0;
1456 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1457 if (monsters <> nil) and not gLoadGameMode then
1458 begin
1459 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1460 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1461 for a := 0 to High(monsters) do
1462 CreateMonster(monsters[a]);
1463 end;
1465 // Çàãðóçêà îïèñàíèÿ êàðòû:
1466 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1467 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1468 Header := MapReader.GetMapHeader();
1470 MapReader.Free();
1472 with gMapInfo do
1473 begin
1474 Name := Header.MapName;
1475 Description := Header.MapDescription;
1476 Author := Header.MapAuthor;
1477 MusicName := Header.MusicName;
1478 SkyName := Header.SkyName;
1479 Height := Header.Height;
1480 Width := Header.Width;
1481 end;
1483 // Çàãðóçêà íåáà:
1484 if gMapInfo.SkyName <> '' then
1485 begin
1486 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1487 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1488 FileName := g_ExtractWadName(gMapInfo.SkyName);
1490 if FileName <> '' then
1491 FileName := GameDir+'/wads/'+FileName
1492 else
1493 begin
1494 FileName := g_ExtractWadName(Res);
1495 end;
1497 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1498 if g_Texture_CreateWAD(BackID, s) then
1499 begin
1500 g_Game_SetupScreenSize();
1501 end
1502 else
1503 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1504 end;
1506 // Çàãðóçêà ìóçûêè:
1507 ok := False;
1508 if gMapInfo.MusicName <> '' then
1509 begin
1510 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1511 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1512 FileName := g_ExtractWadName(gMapInfo.MusicName);
1514 if FileName <> '' then
1515 FileName := GameDir+'/wads/'+FileName
1516 else
1517 begin
1518 FileName := g_ExtractWadName(Res);
1519 end;
1521 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1522 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1523 ok := True
1524 else
1525 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1526 end;
1528 // Îñòàëüíûå óñòàíâêè:
1529 CreateDoorMap();
1530 CreateLiftMap();
1532 g_Items_Init();
1533 g_Weapon_Init();
1534 g_Monsters_Init();
1536 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1537 if not gLoadGameMode then
1538 g_GFX_Init();
1540 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1541 _textures := nil;
1542 panels := nil;
1543 items := nil;
1544 areas := nil;
1545 triggers := nil;
1546 TriggersTable := nil;
1547 AddTextures := nil;
1549 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
1550 if ok and (not gLoadGameMode) then
1551 begin
1552 gMusic.SetByName(gMapInfo.MusicName);
1553 gMusic.Play();
1554 end
1555 else
1556 gMusic.SetByName('');
1557 finally
1558 sfsGCEnable(); // enable releasing unused volumes
1559 end;
1561 e_WriteLog('Creating map grid', MSG_NOTIFY);
1562 mapCreateGrid();
1564 e_WriteLog('Done loading map.', MSG_NOTIFY);
1565 Result := True;
1566 end;
1568 function g_Map_GetMapInfo(Res: String): TMapInfo;
1569 var
1570 WAD: TWADFile;
1571 MapReader: TMapReader_1;
1572 Header: TMapHeaderRec_1;
1573 FileName: String;
1574 Data: Pointer;
1575 Len: Integer;
1576 begin
1577 FillChar(Result, SizeOf(Result), 0);
1578 FileName := g_ExtractWadName(Res);
1580 WAD := TWADFile.Create();
1581 if not WAD.ReadFile(FileName) then
1582 begin
1583 WAD.Free();
1584 Exit;
1585 end;
1587 //k8: it ignores path again
1588 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
1589 begin
1590 WAD.Free();
1591 Exit;
1592 end;
1594 WAD.Free();
1596 MapReader := TMapReader_1.Create();
1598 if not MapReader.LoadMap(Data) then
1599 begin
1600 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
1601 ZeroMemory(@Header, SizeOf(Header));
1602 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
1603 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
1604 end
1605 else
1606 begin
1607 Header := MapReader.GetMapHeader();
1608 Result.Name := Header.MapName;
1609 Result.Description := Header.MapDescription;
1610 end;
1612 FreeMem(Data);
1613 MapReader.Free();
1615 Result.Map := Res;
1616 Result.Author := Header.MapAuthor;
1617 Result.Height := Header.Height;
1618 Result.Width := Header.Width;
1619 end;
1621 function g_Map_GetMapsList(WADName: string): SArray;
1622 var
1623 WAD: TWADFile;
1624 a: Integer;
1625 ResList: SArray;
1626 begin
1627 Result := nil;
1628 WAD := TWADFile.Create();
1629 if not WAD.ReadFile(WADName) then
1630 begin
1631 WAD.Free();
1632 Exit;
1633 end;
1634 ResList := WAD.GetMapResources();
1635 if ResList <> nil then
1636 begin
1637 for a := 0 to High(ResList) do
1638 begin
1639 SetLength(Result, Length(Result)+1);
1640 Result[High(Result)] := ResList[a];
1641 end;
1642 end;
1643 WAD.Free();
1644 end;
1646 function g_Map_Exist(Res: string): Boolean;
1647 var
1648 WAD: TWADFile;
1649 FileName, mnn: string;
1650 ResList: SArray;
1651 a: Integer;
1652 begin
1653 Result := False;
1655 FileName := addWadExtension(g_ExtractWadName(Res));
1657 WAD := TWADFile.Create;
1658 if not WAD.ReadFile(FileName) then
1659 begin
1660 WAD.Free();
1661 Exit;
1662 end;
1664 ResList := WAD.GetMapResources();
1665 WAD.Free();
1667 mnn := g_ExtractFileName(Res);
1668 if ResList <> nil then
1669 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
1670 begin
1671 Result := True;
1672 Exit;
1673 end;
1674 end;
1676 procedure g_Map_Free();
1677 var
1678 a: Integer;
1680 procedure FreePanelArray(var panels: TPanelArray);
1681 var
1682 i: Integer;
1684 begin
1685 if panels <> nil then
1686 begin
1687 for i := 0 to High(panels) do
1688 panels[i].Free();
1689 panels := nil;
1690 end;
1691 end;
1693 begin
1694 g_GFX_Free();
1695 g_Weapon_Free();
1696 g_Items_Free();
1697 g_Triggers_Free();
1698 g_Monsters_Free();
1700 RespawnPoints := nil;
1701 if FlagPoints[FLAG_RED] <> nil then
1702 begin
1703 Dispose(FlagPoints[FLAG_RED]);
1704 FlagPoints[FLAG_RED] := nil;
1705 end;
1706 if FlagPoints[FLAG_BLUE] <> nil then
1707 begin
1708 Dispose(FlagPoints[FLAG_BLUE]);
1709 FlagPoints[FLAG_BLUE] := nil;
1710 end;
1711 //DOMFlagPoints := nil;
1713 //gDOMFlags := nil;
1715 if Textures <> nil then
1716 begin
1717 for a := 0 to High(Textures) do
1718 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
1719 if Textures[a].Anim then
1720 g_Frames_DeleteByID(Textures[a].FramesID)
1721 else
1722 if Textures[a].TextureID <> TEXTURE_NONE then
1723 e_DeleteTexture(Textures[a].TextureID);
1725 Textures := nil;
1726 end;
1728 FreePanelArray(gWalls);
1729 FreePanelArray(gRenderBackgrounds);
1730 FreePanelArray(gRenderForegrounds);
1731 FreePanelArray(gWater);
1732 FreePanelArray(gAcid1);
1733 FreePanelArray(gAcid2);
1734 FreePanelArray(gSteps);
1735 FreePanelArray(gLifts);
1736 FreePanelArray(gBlockMon);
1738 if BackID <> DWORD(-1) then
1739 begin
1740 gBackSize.X := 0;
1741 gBackSize.Y := 0;
1742 e_DeleteTexture(BackID);
1743 BackID := DWORD(-1);
1744 end;
1746 g_Game_StopAllSounds(False);
1747 gMusic.FreeSound();
1748 g_Sound_Delete(gMapInfo.MusicName);
1750 gMapInfo.Name := '';
1751 gMapInfo.Description := '';
1752 gMapInfo.MusicName := '';
1753 gMapInfo.Height := 0;
1754 gMapInfo.Width := 0;
1756 gDoorMap := nil;
1757 gLiftMap := nil;
1759 PanelByID := nil;
1760 end;
1762 procedure g_Map_Update();
1763 var
1764 a, d, j: Integer;
1765 m: Word;
1766 s: String;
1768 procedure UpdatePanelArray(var panels: TPanelArray);
1769 var
1770 i: Integer;
1772 begin
1773 if panels <> nil then
1774 for i := 0 to High(panels) do
1775 panels[i].Update();
1776 end;
1778 begin
1779 UpdatePanelArray(gWalls);
1780 UpdatePanelArray(gRenderBackgrounds);
1781 UpdatePanelArray(gRenderForegrounds);
1782 UpdatePanelArray(gWater);
1783 UpdatePanelArray(gAcid1);
1784 UpdatePanelArray(gAcid2);
1785 UpdatePanelArray(gSteps);
1787 if gGameSettings.GameMode = GM_CTF then
1788 begin
1789 for a := FLAG_RED to FLAG_BLUE do
1790 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
1791 with gFlags[a] do
1792 begin
1793 if gFlags[a].Animation <> nil then
1794 gFlags[a].Animation.Update();
1796 m := g_Obj_Move(@Obj, True, True);
1798 if gTime mod (GAME_TICK*2) <> 0 then
1799 Continue;
1801 // Ñîïðîòèâëåíèå âîçäóõà:
1802 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
1804 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
1805 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
1806 begin
1807 g_Map_ResetFlag(a);
1808 gFlags[a].CaptureTime := 0;
1809 if a = FLAG_RED then
1810 s := _lc[I_PLAYER_FLAG_RED]
1811 else
1812 s := _lc[I_PLAYER_FLAG_BLUE];
1813 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1815 if g_Game_IsNet then
1816 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
1817 Continue;
1818 end;
1820 if Count > 0 then
1821 Count := Count - 1;
1823 // Èãðîê áåðåò ôëàã:
1824 if gPlayers <> nil then
1825 begin
1826 j := Random(Length(gPlayers)) - 1;
1828 for d := 0 to High(gPlayers) do
1829 begin
1830 Inc(j);
1831 if j > High(gPlayers) then
1832 j := 0;
1834 if gPlayers[j] <> nil then
1835 if gPlayers[j].Live and
1836 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
1837 begin
1838 if gPlayers[j].GetFlag(a) then
1839 Break;
1840 end;
1841 end;
1842 end;
1843 end;
1844 end;
1845 end;
1848 procedure g_Map_DrawPanelsOld(PanelType: Word);
1850 procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
1851 var
1852 idx: Integer;
1853 begin
1854 if (panels <> nil) and (stp >= 0) and (stp <= 6) then
1855 begin
1856 // alas, no visible set
1857 for idx := 0 to High(panels) do
1858 begin
1859 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
1860 end;
1861 end;
1862 end;
1864 begin
1865 case PanelType of
1866 PANEL_WALL: DrawPanels(0, gWalls);
1867 PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True);
1868 PANEL_BACK: DrawPanels(1, gRenderBackgrounds);
1869 PANEL_FORE: DrawPanels(2, gRenderForegrounds);
1870 PANEL_WATER: DrawPanels(3, gWater);
1871 PANEL_ACID1: DrawPanels(4, gAcid1);
1872 PANEL_ACID2: DrawPanels(5, gAcid2);
1873 PANEL_STEP: DrawPanels(6, gSteps);
1874 end;
1875 end;
1878 var
1879 gDrawPanelList: TBinaryHeapObj = nil;
1881 function dplLess (a, b: TObject): Boolean;
1882 begin
1883 result := ((a as TPanel).ArrIdx < (b as TPanel).ArrIdx);
1884 end;
1886 procedure dplClear ();
1887 begin
1888 if gDrawPanelList = nil then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
1889 end;
1891 procedure dplAddPanel (pan: TPanel);
1892 begin
1893 if pan = nil then exit;
1894 gDrawPanelList.insert(pan);
1895 end;
1898 procedure g_Map_DrawPanels(x0, y0, wdt, hgt: Integer; PanelType: Word);
1899 var
1900 ptag: Integer;
1902 function checker (obj: TObject; tag: Integer): Boolean;
1903 var
1904 pan: TPanel;
1905 begin
1906 result := false; // don't stop, ever
1907 //e_WriteLog(Format(' *body: tag:%d; ptag:%d; pantype=%d', [tag, ptag, PanelType]), MSG_NOTIFY);
1908 if (tag <> ptag) then exit;
1910 if obj = nil then begin e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end;
1911 if not (obj is TPanel) then begin e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end;
1912 //pan := (obj as TPanel);
1913 //e_WriteLog(Format(' !body: (%d,%d)-(%dx%d) tag:%d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, tag, PanelType]), MSG_NOTIFY);
1915 pan := (obj as TPanel);
1916 if (PanelType = PANEL_CLOSEDOOR) then begin if not pan.Door then exit; end else begin if pan.Door then exit; end;
1917 //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);
1918 dplAddPanel(pan);
1919 end;
1921 procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
1922 var
1923 idx: Integer;
1924 pan: TPanel;
1925 begin
1926 if (panels <> nil) and (stp >= 0) and (stp <= 6) then
1927 begin
1928 // alas, no visible set
1929 for idx := 0 to High(panels) do
1930 begin
1931 if not (drawDoors xor panels[idx].Door) then
1932 begin
1933 pan := panels[idx];
1935 if (pan.Width < 1) or (pan.Height < 1) then continue;
1936 if (pan.X+pan.Width <= x0) or (pan.Y+pan.Height <= y0) then continue;
1937 if (pan.X >= x0+wdt) or (pan.Y >= y0+hgt) then continue;
1939 pan.Draw();
1940 //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);
1941 end;
1942 end;
1943 end;
1944 end;
1946 begin
1947 //g_Map_DrawPanelsOld(PanelType); exit;
1948 //e_WriteLog('==================', MSG_NOTIFY);
1949 //e_WriteLog(Format('***QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
1950 dplClear();
1951 ptag := panelTypeToTag(PanelType);
1953 if gdbg_map_use_grid_render then
1954 begin
1955 if gdbg_map_use_tree_draw then
1956 begin
1957 mapTree.aabbQuery(x0, y0, wdt, hgt, checker, ptag);
1958 end
1959 else
1960 begin
1961 gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker);
1962 end;
1963 // sort and draw the list (we need to sort it, or rendering is fucked)
1964 while gDrawPanelList.count > 0 do
1965 begin
1966 (gDrawPanelList.front() as TPanel).Draw();
1967 gDrawPanelList.popFront();
1968 end;
1969 end
1970 else
1971 begin
1972 //e_WriteLog(Format('+++QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
1973 case PanelType of
1974 PANEL_WALL: DrawPanels(0, gWalls);
1975 PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True);
1976 PANEL_BACK: DrawPanels(1, gRenderBackgrounds);
1977 PANEL_FORE: DrawPanels(2, gRenderForegrounds);
1978 PANEL_WATER: DrawPanels(3, gWater);
1979 PANEL_ACID1: DrawPanels(4, gAcid1);
1980 PANEL_ACID2: DrawPanels(5, gAcid2);
1981 PANEL_STEP: DrawPanels(6, gSteps);
1982 end;
1983 end;
1985 //e_WriteLog('==================', MSG_NOTIFY);
1986 end;
1989 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
1990 function checker (obj: TObject; tag: Integer): Boolean;
1991 var
1992 pan: TPanel;
1993 begin
1994 result := false; // don't stop, ever
1995 if (tag <> GridTagWallDoor) then exit; // only walls
1996 pan := (obj as TPanel);
1997 pan.DrawShadowVolume(lightX, lightY, radius);
1998 end;
2000 begin
2001 if gdbg_map_use_tree_draw then
2002 begin
2003 mapTree.aabbQuery(lightX-radius, lightY-radius, radius*2, radius*2, checker, GridTagWallDoor);
2004 end
2005 else
2006 begin
2007 gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker);
2008 end;
2009 end;
2012 procedure g_Map_DrawBack(dx, dy: Integer);
2013 begin
2014 if gDrawBackGround and (BackID <> DWORD(-1)) then
2015 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2016 else
2017 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2018 end;
2020 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2021 PanelType: Word; b1x3: Boolean): Boolean;
2022 var
2023 a, h: Integer;
2024 begin
2025 Result := False;
2027 if WordBool(PanelType and PANEL_WALL) then
2028 if gWalls <> nil then
2029 begin
2030 h := High(gWalls);
2032 for a := 0 to h do
2033 if gWalls[a].Enabled and
2034 g_Collide(X, Y, Width, Height,
2035 gWalls[a].X, gWalls[a].Y,
2036 gWalls[a].Width, gWalls[a].Height) then
2037 begin
2038 Result := True;
2039 Exit;
2040 end;
2041 end;
2043 if WordBool(PanelType and PANEL_WATER) then
2044 if gWater <> nil then
2045 begin
2046 h := High(gWater);
2048 for a := 0 to h do
2049 if g_Collide(X, Y, Width, Height,
2050 gWater[a].X, gWater[a].Y,
2051 gWater[a].Width, gWater[a].Height) then
2052 begin
2053 Result := True;
2054 Exit;
2055 end;
2056 end;
2058 if WordBool(PanelType and PANEL_ACID1) then
2059 if gAcid1 <> nil then
2060 begin
2061 h := High(gAcid1);
2063 for a := 0 to h do
2064 if g_Collide(X, Y, Width, Height,
2065 gAcid1[a].X, gAcid1[a].Y,
2066 gAcid1[a].Width, gAcid1[a].Height) then
2067 begin
2068 Result := True;
2069 Exit;
2070 end;
2071 end;
2073 if WordBool(PanelType and PANEL_ACID2) then
2074 if gAcid2 <> nil then
2075 begin
2076 h := High(gAcid2);
2078 for a := 0 to h do
2079 if g_Collide(X, Y, Width, Height,
2080 gAcid2[a].X, gAcid2[a].Y,
2081 gAcid2[a].Width, gAcid2[a].Height) then
2082 begin
2083 Result := True;
2084 Exit;
2085 end;
2086 end;
2088 if WordBool(PanelType and PANEL_STEP) then
2089 if gSteps <> nil then
2090 begin
2091 h := High(gSteps);
2093 for a := 0 to h do
2094 if g_Collide(X, Y, Width, Height,
2095 gSteps[a].X, gSteps[a].Y,
2096 gSteps[a].Width, gSteps[a].Height) then
2097 begin
2098 Result := True;
2099 Exit;
2100 end;
2101 end;
2103 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2104 if gLifts <> nil then
2105 begin
2106 h := High(gLifts);
2108 for a := 0 to h do
2109 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2110 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2111 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2112 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2113 g_Collide(X, Y, Width, Height,
2114 gLifts[a].X, gLifts[a].Y,
2115 gLifts[a].Width, gLifts[a].Height) then
2116 begin
2117 Result := True;
2118 Exit;
2119 end;
2120 end;
2122 if WordBool(PanelType and PANEL_BLOCKMON) then
2123 if gBlockMon <> nil then
2124 begin
2125 h := High(gBlockMon);
2127 for a := 0 to h do
2128 if ( (not b1x3) or
2129 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2130 g_Collide(X, Y, Width, Height,
2131 gBlockMon[a].X, gBlockMon[a].Y,
2132 gBlockMon[a].Width, gBlockMon[a].Height) then
2133 begin
2134 Result := True;
2135 Exit;
2136 end;
2137 end;
2138 end;
2140 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2141 var
2142 texid: DWORD;
2144 function checkPanels (var panels: TPanelArray): Boolean;
2145 var
2146 a: Integer;
2147 begin
2148 result := false;
2149 if panels = nil then exit;
2150 for a := 0 to High(panels) do
2151 begin
2152 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2153 begin
2154 result := true;
2155 texid := panels[a].GetTextureID();
2156 exit;
2157 end;
2158 end;
2159 end;
2161 begin
2162 texid := TEXTURE_NONE;
2163 result := texid;
2164 if not checkPanels(gWater) then
2165 if not checkPanels(gAcid1) then
2166 if not checkPanels(gAcid2) then exit;
2167 result := texid;
2168 end;
2171 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2173 function checker (obj: TObject; tag: Integer): Boolean;
2174 var
2175 pan: TPanel;
2176 a: Integer;
2177 begin
2178 result := false; // don't stop, ever
2180 //e_WriteLog(Format(' *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2182 if obj = nil then
2183 begin
2184 e_WriteLog(Format(' !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2185 end;
2186 if not (obj is TPanel) then
2187 begin
2188 e_WriteLog(Format(' !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
2189 exit;
2190 end;
2192 pan := (obj as TPanel);
2193 a := pan.ArrIdx;
2195 if WordBool(PanelType and PANEL_WALL) and (tag = GridTagWallDoor) then
2196 begin
2197 if gWalls[a].Enabled and g_Collide(X, Y, Width, Height, gWalls[a].X, gWalls[a].Y, gWalls[a].Width, gWalls[a].Height) then
2198 begin
2199 result := true;
2200 exit;
2201 end;
2202 end;
2204 if WordBool(PanelType and PANEL_WATER) and (tag = GridTagWater) then
2205 begin
2206 if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then
2207 begin
2208 result := True;
2209 exit;
2210 end;
2211 end;
2213 if WordBool(PanelType and PANEL_ACID1) and (tag = GridTagAcid1) then
2214 begin
2215 if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then
2216 begin
2217 result := True;
2218 exit;
2219 end;
2220 end;
2222 if WordBool(PanelType and PANEL_ACID2) and (tag = GridTagAcid2) then
2223 begin
2224 if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then
2225 begin
2226 result := True;
2227 exit;
2228 end;
2229 end;
2231 if WordBool(PanelType and PANEL_STEP) and (tag = GridTagStep) then
2232 begin
2233 if g_Collide(X, Y, Width, Height, gSteps[a].X, gSteps[a].Y, gSteps[a].Width, gSteps[a].Height) then
2234 begin
2235 result := True;
2236 exit;
2237 end;
2238 end;
2240 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) and (tag = GridTagLift) then
2241 begin
2242 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2243 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2244 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2245 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2246 g_Collide(X, Y, Width, Height, gLifts[a].X, gLifts[a].Y, gLifts[a].Width, gLifts[a].Height) then
2247 begin
2248 result := true;
2249 exit;
2250 end;
2251 end;
2253 if WordBool(PanelType and PANEL_BLOCKMON) and (tag = GridTagBlockMon) then
2254 begin
2255 if ((not b1x3) or ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64)) and
2256 g_Collide(X, Y, Width, Height, gBlockMon[a].X, gBlockMon[a].Y, gBlockMon[a].Width, gBlockMon[a].Height) then
2257 begin
2258 result := True;
2259 exit;
2260 end;
2261 end;
2262 end;
2264 begin
2265 //TODO: detailed profile
2266 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('wall coldet');
2267 try
2268 if gdbg_map_use_grid_coldet then
2269 begin
2270 if gdbg_map_use_tree_coldet then
2271 begin
2272 result := (mapTree.aabbQuery(X, Y, Width, Height, checker, (GridTagWallDoor or GridTagWater or GridTagAcid1 or GridTagAcid2 or GridTagStep or GridTagLift or GridTagBlockMon)) <> nil);
2273 if (gdbg_map_dump_coldet_tree_queries) and (mapTree.nodesVisited <> 0) then
2274 begin
2275 //e_WriteLog(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]), MSG_NOTIFY);
2276 g_Console_Add(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]));
2277 end;
2278 end
2279 else
2280 begin
2281 result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker);
2282 end;
2283 end
2284 else
2285 begin
2286 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2287 end;
2288 finally
2289 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2290 end;
2291 end;
2294 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2295 var
2296 cctype: Integer = 3; // priority: 0: water, 1: acid1, 2: acid2; 3: others (nothing)
2297 texid: DWORD;
2299 // slightly different from the old code, but meh...
2300 function checker (obj: TObject; tag: Integer): Boolean;
2301 var
2302 pan: TPanel;
2303 a: Integer;
2304 begin
2305 result := false; // don't stop, ever
2306 if (tag <> GridTagWater) and (tag <> GridTagAcid1) and (tag <> GridTagAcid2) then exit;
2307 pan := (obj as TPanel);
2308 a := pan.ArrIdx;
2309 // water
2310 if (tag = GridTagWater) then
2311 begin
2312 if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then
2313 begin
2314 result := true; // water has highest priority, so stop right here
2315 texid := gWater[a].GetTextureID();
2316 exit;
2317 end;
2318 end;
2319 // acid1
2320 if (cctype > 1) and (tag = GridTagAcid1) then
2321 begin
2322 if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then
2323 begin
2324 cctype := 1;
2325 texid := gAcid1[a].GetTextureID();
2326 exit;
2327 end;
2328 end;
2329 // acid2
2330 if (cctype > 2) and (tag = GridTagAcid2) then
2331 begin
2332 if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then
2333 begin
2334 cctype := 2;
2335 texid := gAcid2[a].GetTextureID();
2336 exit;
2337 end;
2338 end;
2339 end;
2341 {var
2342 cctype1: Integer = 3; // priority: 0: water, 1: acid1, 2: acid2; 3: others (nothing)
2343 texid1: DWORD;}
2344 begin
2345 //TODO: detailed profile?
2346 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquid coldet');
2347 try
2348 if gdbg_map_use_grid_coldet then
2349 begin
2350 texid := TEXTURE_NONE;
2351 if gdbg_map_use_tree_coldet then
2352 begin
2353 mapTree.aabbQuery(X, Y, Width, Height, checker);
2355 cctype1 := cctype;
2356 texid1 := texid;
2357 cctype := 3;
2358 texid := TEXTURE_NONE;
2359 gMapGrid.forEachInAABB(X, Y, Width, Height, checker);
2360 if (cctype1 <> cctype) or (texid1 <> texid) then
2361 begin
2362 e_WriteLog(Format('g_Map_CollideLiquid_Texture(%d, %d, %u, %u): tree(cctype:%d;texid:%u); grid(cctype:%d;texid:%u)', [X, Y, Width, Height, cctype1, texid1, cctype, texid]), MSG_WARNING);
2363 end;}
2364 end
2365 else
2366 begin
2367 gMapGrid.forEachInAABB(X, Y, Width, Height, checker);
2368 end;
2369 result := texid;
2370 end
2371 else
2372 begin
2373 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2374 end;
2375 finally
2376 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2377 end;
2378 end;
2380 procedure g_Map_EnableWall(ID: DWORD);
2381 begin
2382 with gWalls[ID] do
2383 begin
2384 Enabled := True;
2385 g_Mark(X, Y, Width, Height, MARK_DOOR, True);
2387 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2388 end;
2389 end;
2391 procedure g_Map_DisableWall(ID: DWORD);
2392 begin
2393 with gWalls[ID] do
2394 begin
2395 Enabled := False;
2396 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
2398 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2399 end;
2400 end;
2402 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2403 var
2404 tp: TPanel;
2405 begin
2406 case PanelType of
2407 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2408 tp := gWalls[ID];
2409 PANEL_FORE:
2410 tp := gRenderForegrounds[ID];
2411 PANEL_BACK:
2412 tp := gRenderBackgrounds[ID];
2413 PANEL_WATER:
2414 tp := gWater[ID];
2415 PANEL_ACID1:
2416 tp := gAcid1[ID];
2417 PANEL_ACID2:
2418 tp := gAcid2[ID];
2419 PANEL_STEP:
2420 tp := gSteps[ID];
2421 else
2422 Exit;
2423 end;
2425 tp.NextTexture(AnimLoop);
2426 if g_Game_IsServer and g_Game_IsNet then
2427 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2428 end;
2430 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2431 begin
2432 if gLifts[ID].LiftType = t then
2433 Exit;
2435 with gLifts[ID] do
2436 begin
2437 LiftType := t;
2439 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2441 if LiftType = 0 then
2442 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2443 else if LiftType = 1 then
2444 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2445 else if LiftType = 2 then
2446 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2447 else if LiftType = 3 then
2448 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2450 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2451 end;
2452 end;
2454 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2455 var
2456 a: Integer;
2457 PointsArray: Array of TRespawnPoint;
2458 begin
2459 Result := False;
2460 SetLength(PointsArray, 0);
2462 if RespawnPoints = nil then
2463 Exit;
2465 for a := 0 to High(RespawnPoints) do
2466 if RespawnPoints[a].PointType = PointType then
2467 begin
2468 SetLength(PointsArray, Length(PointsArray)+1);
2469 PointsArray[High(PointsArray)] := RespawnPoints[a];
2470 end;
2472 if PointsArray = nil then
2473 Exit;
2475 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2476 Result := True;
2477 end;
2479 function g_Map_GetPointCount(PointType: Byte): Word;
2480 var
2481 a: Integer;
2482 begin
2483 Result := 0;
2485 if RespawnPoints = nil then
2486 Exit;
2488 for a := 0 to High(RespawnPoints) do
2489 if RespawnPoints[a].PointType = PointType then
2490 Result := Result + 1;
2491 end;
2493 function g_Map_HaveFlagPoints(): Boolean;
2494 begin
2495 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2496 end;
2498 procedure g_Map_ResetFlag(Flag: Byte);
2499 begin
2500 with gFlags[Flag] do
2501 begin
2502 Obj.X := -1000;
2503 Obj.Y := -1000;
2504 Obj.Vel.X := 0;
2505 Obj.Vel.Y := 0;
2506 Direction := D_LEFT;
2507 State := FLAG_STATE_NONE;
2508 if FlagPoints[Flag] <> nil then
2509 begin
2510 Obj.X := FlagPoints[Flag]^.X;
2511 Obj.Y := FlagPoints[Flag]^.Y;
2512 Direction := FlagPoints[Flag]^.Direction;
2513 State := FLAG_STATE_NORMAL;
2514 end;
2515 Count := -1;
2516 end;
2517 end;
2519 procedure g_Map_DrawFlags();
2520 var
2521 i, dx: Integer;
2522 Mirror: TMirrorType;
2523 begin
2524 if gGameSettings.GameMode <> GM_CTF then
2525 Exit;
2527 for i := FLAG_RED to FLAG_BLUE do
2528 with gFlags[i] do
2529 if State <> FLAG_STATE_CAPTURED then
2530 begin
2531 if State = FLAG_STATE_NONE then
2532 continue;
2534 if Direction = D_LEFT then
2535 begin
2536 Mirror := M_HORIZONTAL;
2537 dx := -1;
2538 end
2539 else
2540 begin
2541 Mirror := M_NONE;
2542 dx := 1;
2543 end;
2545 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2547 if g_debug_Frames then
2548 begin
2549 e_DrawQuad(Obj.X+Obj.Rect.X,
2550 Obj.Y+Obj.Rect.Y,
2551 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2552 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2553 0, 255, 0);
2554 end;
2555 end;
2556 end;
2558 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2559 var
2560 dw: DWORD;
2561 b: Byte;
2562 str: String;
2563 boo: Boolean;
2565 procedure SavePanelArray(var panels: TPanelArray);
2566 var
2567 PAMem: TBinMemoryWriter;
2568 i: Integer;
2569 begin
2570 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2571 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2573 i := 0;
2574 while i < Length(panels) do
2575 begin
2576 if panels[i].SaveIt then
2577 begin
2578 // ID ïàíåëè:
2579 PAMem.WriteInt(i);
2580 // Ñîõðàíÿåì ïàíåëü:
2581 panels[i].SaveState(PAMem);
2582 end;
2583 Inc(i);
2584 end;
2586 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2587 PAMem.SaveToMemory(Mem);
2588 PAMem.Free();
2589 end;
2591 procedure SaveFlag(flag: PFlag);
2592 begin
2593 // Ñèãíàòóðà ôëàãà:
2594 dw := FLAG_SIGNATURE; // 'FLAG'
2595 Mem.WriteDWORD(dw);
2596 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2597 Mem.WriteByte(flag^.RespawnType);
2598 // Ñîñòîÿíèå ôëàãà:
2599 Mem.WriteByte(flag^.State);
2600 // Íàïðàâëåíèå ôëàãà:
2601 if flag^.Direction = D_LEFT then
2602 b := 1
2603 else // D_RIGHT
2604 b := 2;
2605 Mem.WriteByte(b);
2606 // Îáúåêò ôëàãà:
2607 Obj_SaveState(@flag^.Obj, Mem);
2608 end;
2610 begin
2611 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2613 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2614 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2615 SavePanelArray(gWalls);
2616 // Ñîõðàíÿåì ïàíåëè ôîíà:
2617 SavePanelArray(gRenderBackgrounds);
2618 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2619 SavePanelArray(gRenderForegrounds);
2620 // Ñîõðàíÿåì ïàíåëè âîäû:
2621 SavePanelArray(gWater);
2622 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2623 SavePanelArray(gAcid1);
2624 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2625 SavePanelArray(gAcid2);
2626 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2627 SavePanelArray(gSteps);
2628 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2629 SavePanelArray(gLifts);
2630 ///// /////
2632 ///// Ñîõðàíÿåì ìóçûêó: /////
2633 // Ñèãíàòóðà ìóçûêè:
2634 dw := MUSIC_SIGNATURE; // 'MUSI'
2635 Mem.WriteDWORD(dw);
2636 // Íàçâàíèå ìóçûêè:
2637 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2638 if gMusic.NoMusic then
2639 str := ''
2640 else
2641 str := gMusic.Name;
2642 Mem.WriteString(str, 64);
2643 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2644 dw := gMusic.GetPosition();
2645 Mem.WriteDWORD(dw);
2646 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2647 boo := gMusic.SpecPause;
2648 Mem.WriteBoolean(boo);
2649 ///// /////
2651 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2652 Mem.WriteInt(gTotalMonsters);
2653 ///// /////
2655 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2656 if gGameSettings.GameMode = GM_CTF then
2657 begin
2658 // Ôëàã Êðàñíîé êîìàíäû:
2659 SaveFlag(@gFlags[FLAG_RED]);
2660 // Ôëàã Ñèíåé êîìàíäû:
2661 SaveFlag(@gFlags[FLAG_BLUE]);
2662 end;
2663 ///// /////
2665 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2666 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2667 begin
2668 // Î÷êè Êðàñíîé êîìàíäû:
2669 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2670 // Î÷êè Ñèíåé êîìàíäû:
2671 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2672 end;
2673 ///// /////
2674 end;
2676 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2677 var
2678 dw: DWORD;
2679 b: Byte;
2680 str: String;
2681 boo: Boolean;
2683 procedure LoadPanelArray(var panels: TPanelArray);
2684 var
2685 PAMem: TBinMemoryReader;
2686 i, id: Integer;
2687 begin
2688 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
2689 PAMem := TBinMemoryReader.Create();
2690 PAMem.LoadFromMemory(Mem);
2692 for i := 0 to Length(panels)-1 do
2693 if panels[i].SaveIt then
2694 begin
2695 // ID ïàíåëè:
2696 PAMem.ReadInt(id);
2697 if id <> i then
2698 begin
2699 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
2700 end;
2701 // Çàãðóæàåì ïàíåëü:
2702 panels[i].LoadState(PAMem);
2703 end;
2705 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
2706 PAMem.Free();
2707 end;
2709 procedure LoadFlag(flag: PFlag);
2710 begin
2711 // Ñèãíàòóðà ôëàãà:
2712 Mem.ReadDWORD(dw);
2713 if dw <> FLAG_SIGNATURE then // 'FLAG'
2714 begin
2715 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
2716 end;
2717 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2718 Mem.ReadByte(flag^.RespawnType);
2719 // Ñîñòîÿíèå ôëàãà:
2720 Mem.ReadByte(flag^.State);
2721 // Íàïðàâëåíèå ôëàãà:
2722 Mem.ReadByte(b);
2723 if b = 1 then
2724 flag^.Direction := D_LEFT
2725 else // b = 2
2726 flag^.Direction := D_RIGHT;
2727 // Îáúåêò ôëàãà:
2728 Obj_LoadState(@flag^.Obj, Mem);
2729 end;
2731 begin
2732 if Mem = nil then
2733 Exit;
2735 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
2736 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
2737 LoadPanelArray(gWalls);
2738 // Çàãðóæàåì ïàíåëè ôîíà:
2739 LoadPanelArray(gRenderBackgrounds);
2740 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
2741 LoadPanelArray(gRenderForegrounds);
2742 // Çàãðóæàåì ïàíåëè âîäû:
2743 LoadPanelArray(gWater);
2744 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
2745 LoadPanelArray(gAcid1);
2746 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
2747 LoadPanelArray(gAcid2);
2748 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
2749 LoadPanelArray(gSteps);
2750 // Çàãðóæàåì ïàíåëè ëèôòîâ:
2751 LoadPanelArray(gLifts);
2752 ///// /////
2754 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
2755 g_GFX_Init();
2756 mapCreateGrid();
2758 ///// Çàãðóæàåì ìóçûêó: /////
2759 // Ñèãíàòóðà ìóçûêè:
2760 Mem.ReadDWORD(dw);
2761 if dw <> MUSIC_SIGNATURE then // 'MUSI'
2762 begin
2763 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
2764 end;
2765 // Íàçâàíèå ìóçûêè:
2766 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
2767 Mem.ReadString(str);
2768 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2769 Mem.ReadDWORD(dw);
2770 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2771 Mem.ReadBoolean(boo);
2772 // Çàïóñêàåì ýòó ìóçûêó:
2773 gMusic.SetByName(str);
2774 gMusic.SpecPause := boo;
2775 gMusic.Play();
2776 gMusic.Pause(True);
2777 gMusic.SetPosition(dw);
2778 ///// /////
2780 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
2781 Mem.ReadInt(gTotalMonsters);
2782 ///// /////
2784 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
2785 if gGameSettings.GameMode = GM_CTF then
2786 begin
2787 // Ôëàã Êðàñíîé êîìàíäû:
2788 LoadFlag(@gFlags[FLAG_RED]);
2789 // Ôëàã Ñèíåé êîìàíäû:
2790 LoadFlag(@gFlags[FLAG_BLUE]);
2791 end;
2792 ///// /////
2794 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2795 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2796 begin
2797 // Î÷êè Êðàñíîé êîìàíäû:
2798 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
2799 // Î÷êè Ñèíåé êîìàíäû:
2800 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
2801 end;
2802 ///// /////
2803 end;
2805 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
2806 var
2807 Arr: TPanelArray;
2808 begin
2809 Result := nil;
2810 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
2811 Arr := PanelByID[PanelID].PWhere^;
2812 PanelArrayID := PanelByID[PanelID].PArrID;
2813 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
2814 end;
2816 end.