DEADSOFTWARE

some triggers are working now
[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 {$DEFINE MAP_DEBUG_ENABLED_FLAG}
18 unit g_map;
20 interface
22 uses
23 e_graphics, g_basic, MAPDEF, g_textures, Classes,
24 g_phys, wadreader, BinEditor, g_panel, g_grid, md5, binheap, xprofiler, xparser, xdynrec;
26 type
27 TMapInfo = record
28 Map: String;
29 Name: String;
30 Description: String;
31 Author: String;
32 MusicName: String;
33 SkyName: String;
34 Height: Word;
35 Width: Word;
36 end;
38 PRespawnPoint = ^TRespawnPoint;
39 TRespawnPoint = record
40 X, Y: Integer;
41 Direction: TDirection;
42 PointType: Byte;
43 end;
45 PFlagPoint = ^TFlagPoint;
46 TFlagPoint = TRespawnPoint;
48 PFlag = ^TFlag;
49 TFlag = record
50 Obj: TObj;
51 RespawnType: Byte;
52 State: Byte;
53 Count: Integer;
54 CaptureTime: LongWord;
55 Animation: TAnimation;
56 Direction: TDirection;
57 end;
59 function g_Map_Load(Res: String): Boolean;
60 function g_Map_GetMapInfo(Res: String): TMapInfo;
61 function g_Map_GetMapsList(WADName: String): SArray;
62 function g_Map_Exist(Res: String): Boolean;
63 procedure g_Map_Free();
64 procedure g_Map_Update();
66 procedure g_Map_DrawPanels (PanelType: Word); // unaccelerated
67 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
69 procedure g_Map_DrawBack(dx, dy: Integer);
70 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
71 PanelType: Word; b1x3: Boolean=false): Boolean;
72 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
73 procedure g_Map_EnableWall(ID: DWORD);
74 procedure g_Map_DisableWall(ID: DWORD);
75 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
76 procedure g_Map_SetLift(ID: DWORD; t: Integer);
77 procedure g_Map_ReAdd_DieTriggers();
78 function g_Map_IsSpecialTexture(Texture: String): Boolean;
80 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
81 function g_Map_GetPointCount(PointType: Byte): Word;
83 function g_Map_HaveFlagPoints(): Boolean;
85 procedure g_Map_ResetFlag(Flag: Byte);
86 procedure g_Map_DrawFlags();
88 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
90 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
91 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
93 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
95 // returns panel or nil
96 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
97 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
99 // returns panel or nil
100 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
101 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
103 type
104 TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop
106 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
107 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
109 // trace liquid, stepping by `dx` and `dy`
110 // return last seen liquid coords, and `false` if we're started outside of the liquid
111 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
114 procedure g_Map_ProfilersBegin ();
115 procedure g_Map_ProfilersEnd ();
118 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
120 const
121 NNF_NO_NAME = 0;
122 NNF_NAME_BEFORE = 1;
123 NNF_NAME_EQUALS = 2;
124 NNF_NAME_AFTER = 3;
126 function g_Texture_NumNameFindStart(name: String): Boolean;
127 function g_Texture_NumNameFindNext(var newName: String): Byte;
129 const
130 RESPAWNPOINT_PLAYER1 = 1;
131 RESPAWNPOINT_PLAYER2 = 2;
132 RESPAWNPOINT_DM = 3;
133 RESPAWNPOINT_RED = 4;
134 RESPAWNPOINT_BLUE = 5;
136 FLAG_NONE = 0;
137 FLAG_RED = 1;
138 FLAG_BLUE = 2;
139 FLAG_DOM = 3;
141 FLAG_STATE_NONE = 0;
142 FLAG_STATE_NORMAL = 1;
143 FLAG_STATE_DROPPED = 2;
144 FLAG_STATE_CAPTURED = 3;
145 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
146 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
148 FLAG_TIME = 720; // 20 seconds
150 SKY_STRETCH: Single = 1.5;
152 const
153 GridTagInvalid = 0;
155 (* draw order:
156 PANEL_BACK
157 PANEL_STEP
158 PANEL_WALL
159 PANEL_CLOSEDOOR
160 PANEL_ACID1
161 PANEL_ACID2
162 PANEL_WATER
163 PANEL_FORE
164 *)
165 // sorted by draw priority
166 GridTagBack = 1 shl 0;
167 GridTagStep = 1 shl 1;
168 GridTagWall = 1 shl 2;
169 GridTagDoor = 1 shl 3;
170 GridTagAcid1 = 1 shl 4;
171 GridTagAcid2 = 1 shl 5;
172 GridTagWater = 1 shl 6;
173 GridTagFore = 1 shl 7;
174 // the following are invisible
175 GridTagLift = 1 shl 8;
176 GridTagBlockMon = 1 shl 9;
178 GridDrawableMask = (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore);
181 var
182 gWalls: TPanelArray;
183 gRenderBackgrounds: TPanelArray;
184 gRenderForegrounds: TPanelArray;
185 gWater, gAcid1, gAcid2: TPanelArray;
186 gSteps: TPanelArray;
187 gLifts: TPanelArray;
188 gBlockMon: TPanelArray;
189 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
190 //gDOMFlags: array of TFlag;
191 gMapInfo: TMapInfo;
192 gBackSize: TDFPoint;
193 gDoorMap: array of array of DWORD;
194 gLiftMap: array of array of DWORD;
195 gWADHash: TMD5Digest;
196 BackID: DWORD = DWORD(-1);
197 gExternalResources: TStringList;
199 gdbg_map_use_accel_render: Boolean = true;
200 gdbg_map_use_accel_coldet: Boolean = true;
201 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
202 gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
204 gCurrentMap: TDynRecord = nil;
207 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
210 type
211 TPanelGrid = specialize TBodyGridBase<TPanel>;
213 var
214 mapGrid: TPanelGrid = nil; // DO NOT USE! public for debugging only!
217 implementation
219 uses
220 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
221 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
222 g_options, g_triggers, g_player,
223 Math, g_monsters, g_saveload, g_language, g_netmsg,
224 utils, sfs, xstreams, hashtable,
225 ImagingTypes, Imaging, ImagingUtility,
226 ImagingGif, ImagingNetworkGraphics;
228 const
229 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
230 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
231 FLAG_SIGNATURE = $47414C46; // 'FLAG'
234 var
235 dfmapdef: TDynMapDef = nil;
237 procedure loadMapDefinition ();
238 var
239 pr: TTextParser = nil;
240 st: TStream = nil;
241 WAD: TWADFile = nil;
242 begin
243 if (dfmapdef <> nil) then exit;
244 try
245 e_LogWritefln('parsing "mapdef.txt"...', []);
246 st := openDiskFileRO(DataDir+'mapdef.txt');
247 except
248 st := nil;
249 e_LogWritefln('local "%smapdef.txt" not found', [DataDir]);
250 end;
251 if (st = nil) then
252 begin
253 WAD := TWADFile.Create();
254 if not WAD.ReadFile(GameWAD) then
255 begin
256 //raise Exception.Create('cannot load "game.wad"');
257 st := nil;
258 end
259 else
260 begin
261 st := WAD.openFileStream('mapdef.txt');
262 end;
263 end;
265 if (st = nil) then
266 begin
267 //raise Exception.Create('cannot open "mapdef.txt"');
268 e_LogWritefln('using default "mapdef.txt"...', [], MSG_WARNING);
269 pr := TStrTextParser.Create(defaultMapDef);
270 end
271 else
272 begin
273 pr := TFileTextParser.Create(st);
274 end;
276 try
277 dfmapdef := TDynMapDef.Create(pr);
278 except on e: Exception do
279 raise Exception.Create(Format('ERROR in "mapdef.txt" at (%s,%s): %s', [pr.line, pr.col, e.message]));
280 end;
282 st.Free();
283 WAD.Free();
284 end;
287 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
288 var
289 wst: TSFSMemoryChunkStream = nil;
290 pr: TTextParser = nil;
291 begin
292 result := nil;
293 if (dataLen < 4) then exit;
294 loadMapDefinition();
295 if (dfmapdef = nil) then raise Exception.Create('internal map loader error');
297 wst := TSFSMemoryChunkStream.Create(data, dataLen);
299 if (PAnsiChar(data)[0] = 'M') and (PAnsiChar(data)[1] = 'A') and (PAnsiChar(data)[2] = 'P') and (PByte(data)[3] = 1) then
300 begin
301 // binary map
302 try
303 result := dfmapdef.parseBinMap(wst);
304 except on e: Exception do
305 begin
306 e_LogWritefln('ERROR: %s', [e.message]);
307 wst.Free();
308 result := nil;
309 exit;
310 end;
311 end;
312 wst.Free();
313 end
314 else
315 begin
316 // text map
317 pr := TFileTextParser.Create(wst);
318 try
319 result := dfmapdef.parseMap(pr);
320 except on e: Exception do
321 begin
322 if (pr <> nil) then e_LogWritefln('ERROR at (%s,%s): %s', [pr.line, pr.col, e.message])
323 else e_LogWritefln('ERROR: %s', [e.message]);
324 pr.Free(); // will free `wst`
325 result := nil;
326 exit;
327 end;
328 end;
329 pr.Free(); // will free `wst`
330 end;
331 end;
334 var
335 NNF_PureName: String; // Èìÿ òåêñòóðû áåç öèôð â êîíöå
336 NNF_FirstNum: Integer; // ×èñëî ó íà÷àëüíîé òåêñòóðû
337 NNF_CurrentNum: Integer; // Ñëåäóþùåå ÷èñëî ó òåêñòóðû
340 function g_Texture_NumNameFindStart(name: String): Boolean;
341 var
342 i: Integer;
344 begin
345 Result := False;
346 NNF_PureName := '';
347 NNF_FirstNum := -1;
348 NNF_CurrentNum := -1;
350 for i := Length(name) downto 1 do
351 if (name[i] = '_') then // "_" - ñèìâîë íà÷àëà íîìåðíîãî ïîñòôèêñà
352 begin
353 if i = Length(name) then
354 begin // Íåò öèôð â êîíöå ñòðîêè
355 Exit;
356 end
357 else
358 begin
359 NNF_PureName := Copy(name, 1, i);
360 Delete(name, 1, i);
361 Break;
362 end;
363 end;
365 // Íå ïåðåâåñòè â ÷èñëî:
366 if not TryStrToInt(name, NNF_FirstNum) then
367 Exit;
369 NNF_CurrentNum := 0;
371 Result := True;
372 end;
375 function g_Texture_NumNameFindNext(var newName: String): Byte;
376 begin
377 if (NNF_PureName = '') or (NNF_CurrentNum < 0) then
378 begin
379 newName := '';
380 Result := NNF_NO_NAME;
381 Exit;
382 end;
384 newName := NNF_PureName + IntToStr(NNF_CurrentNum);
386 if NNF_CurrentNum < NNF_FirstNum then
387 Result := NNF_NAME_BEFORE
388 else
389 if NNF_CurrentNum > NNF_FirstNum then
390 Result := NNF_NAME_AFTER
391 else
392 Result := NNF_NAME_EQUALS;
394 Inc(NNF_CurrentNum);
395 end;
398 function panelTypeToTag (panelType: Word): Integer;
399 begin
400 case panelType of
401 PANEL_WALL: result := GridTagWall; // gWalls
402 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
403 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
404 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
405 PANEL_WATER: result := GridTagWater; // gWater
406 PANEL_ACID1: result := GridTagAcid1; // gAcid1
407 PANEL_ACID2: result := GridTagAcid2; // gAcid2
408 PANEL_STEP: result := GridTagStep; // gSteps
409 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
410 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
411 else result := GridTagInvalid;
412 end;
413 end;
416 function dplLess (a, b: TObject): Boolean;
417 var
418 pa, pb: TPanel;
419 begin
420 pa := TPanel(a);
421 pb := TPanel(b);
422 if (pa.tag < pb.tag) then begin result := true; exit; end;
423 if (pa.tag > pb.tag) then begin result := false; exit; end;
424 result := (pa.arrIdx < pb.arrIdx);
425 end;
427 procedure dplClear ();
428 begin
429 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
430 end;
433 type
434 TPanelID = record
435 PWhere: ^TPanelArray;
436 PArrID: Integer;
437 end;
439 var
440 PanelById: array of TPanelID;
441 Textures: TLevelTextureArray;
442 RespawnPoints: Array of TRespawnPoint;
443 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
444 //DOMFlagPoints: Array of TFlagPoint;
447 procedure g_Map_ProfilersBegin ();
448 begin
449 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
450 profMapCollision.mainBegin(g_profile_collision);
451 // create sections
452 if g_profile_collision then
453 begin
454 profMapCollision.sectionBegin('*solids');
455 profMapCollision.sectionEnd();
456 profMapCollision.sectionBegin('liquids');
457 profMapCollision.sectionEnd();
458 end;
459 end;
461 procedure g_Map_ProfilersEnd ();
462 begin
463 if (profMapCollision <> nil) then profMapCollision.mainEnd();
464 end;
467 // wall index in `gWalls` or -1
468 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
469 var
470 ex, ey: Integer;
471 begin
472 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, (GridTagWall or GridTagDoor));
473 if (result <> nil) then
474 begin
475 if (hitx <> nil) then hitx^ := ex;
476 if (hity <> nil) then hity^ := ey;
477 end
478 else
479 begin
480 if (hitx <> nil) then hitx^ := x1;
481 if (hity <> nil) then hity^ := y1;
482 end;
483 end;
485 // returns panel or nil
486 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
487 var
488 ex, ey: Integer;
489 begin
490 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, tag);
491 if (result <> nil) then
492 begin
493 if (hitx <> nil) then hitx^ := ex;
494 if (hity <> nil) then hity^ := ey;
495 end
496 else
497 begin
498 if (hitx <> nil) then hitx^ := x1;
499 if (hity <> nil) then hity^ := y1;
500 end;
501 end;
504 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
506 function checker (pan: TPanel; tag: Integer): Boolean;
507 begin
509 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
510 begin
511 result := pan.Enabled; // stop if wall is enabled
512 exit;
513 end;
516 if ((tag and GridTagLift) <> 0) then
517 begin
518 // stop if the lift of the right type
519 result :=
520 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
521 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
522 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
523 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
524 exit;
525 end;
527 result := true; // otherwise, stop anyway, 'cause `forEachAtPoint()` is guaranteed to call this only for correct panels
528 end;
530 var
531 tagmask: Integer = 0;
532 begin
533 result := false;
535 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
536 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
537 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
538 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
539 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
540 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
541 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
543 if (tagmask = 0) then exit;// just in case
544 if ((tagmask and GridTagLift) <> 0) then
545 begin
546 // slow
547 result := (mapGrid.forEachAtPoint(x, y, checker, tagmask) <> nil);
548 end
549 else
550 begin
551 // fast
552 result := (mapGrid.forEachAtPoint(x, y, nil, tagmask) <> nil);
553 end;
554 end;
557 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
558 begin
559 result := nil;
560 if (tagmask = 0) then exit;
561 result := mapGrid.forEachAtPoint(x, y, nil, tagmask);
562 end;
565 function g_Map_IsSpecialTexture(Texture: String): Boolean;
566 begin
567 Result := (Texture = TEXTURE_NAME_WATER) or
568 (Texture = TEXTURE_NAME_ACID1) or
569 (Texture = TEXTURE_NAME_ACID2);
570 end;
572 procedure CreateDoorMap();
573 var
574 PanelArray: Array of record
575 X, Y: Integer;
576 Width, Height: Word;
577 Active: Boolean;
578 PanelID: DWORD;
579 end;
580 a, b, c, m, i, len: Integer;
581 ok: Boolean;
582 begin
583 if gWalls = nil then
584 Exit;
586 i := 0;
587 len := 128;
588 SetLength(PanelArray, len);
590 for a := 0 to High(gWalls) do
591 if gWalls[a].Door then
592 begin
593 PanelArray[i].X := gWalls[a].X;
594 PanelArray[i].Y := gWalls[a].Y;
595 PanelArray[i].Width := gWalls[a].Width;
596 PanelArray[i].Height := gWalls[a].Height;
597 PanelArray[i].Active := True;
598 PanelArray[i].PanelID := a;
600 i := i + 1;
601 if i = len then
602 begin
603 len := len + 128;
604 SetLength(PanelArray, len);
605 end;
606 end;
608 // Íåò äâåðåé:
609 if i = 0 then
610 begin
611 PanelArray := nil;
612 Exit;
613 end;
615 SetLength(gDoorMap, 0);
617 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
619 for a := 0 to i-1 do
620 if PanelArray[a].Active then
621 begin
622 PanelArray[a].Active := False;
623 m := Length(gDoorMap);
624 SetLength(gDoorMap, m+1);
625 SetLength(gDoorMap[m], 1);
626 gDoorMap[m, 0] := PanelArray[a].PanelID;
627 ok := True;
629 while ok do
630 begin
631 ok := False;
633 for b := 0 to i-1 do
634 if PanelArray[b].Active then
635 for c := 0 to High(gDoorMap[m]) do
636 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
637 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
638 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
639 PanelArray[b].Width, PanelArray[b].Height,
640 gWalls[gDoorMap[m, c]].X,
641 gWalls[gDoorMap[m, c]].Y,
642 gWalls[gDoorMap[m, c]].Width,
643 gWalls[gDoorMap[m, c]].Height) then
644 begin
645 PanelArray[b].Active := False;
646 SetLength(gDoorMap[m],
647 Length(gDoorMap[m])+1);
648 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
649 ok := True;
650 Break;
651 end;
652 end;
654 g_Game_StepLoading();
655 end;
657 PanelArray := nil;
658 end;
660 procedure CreateLiftMap();
661 var
662 PanelArray: Array of record
663 X, Y: Integer;
664 Width, Height: Word;
665 Active: Boolean;
666 end;
667 a, b, c, len, i, j: Integer;
668 ok: Boolean;
669 begin
670 if gLifts = nil then
671 Exit;
673 len := Length(gLifts);
674 SetLength(PanelArray, len);
676 for a := 0 to len-1 do
677 begin
678 PanelArray[a].X := gLifts[a].X;
679 PanelArray[a].Y := gLifts[a].Y;
680 PanelArray[a].Width := gLifts[a].Width;
681 PanelArray[a].Height := gLifts[a].Height;
682 PanelArray[a].Active := True;
683 end;
685 SetLength(gLiftMap, len);
686 i := 0;
688 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
690 for a := 0 to len-1 do
691 if PanelArray[a].Active then
692 begin
693 PanelArray[a].Active := False;
694 SetLength(gLiftMap[i], 32);
695 j := 0;
696 gLiftMap[i, j] := a;
697 ok := True;
699 while ok do
700 begin
701 ok := False;
702 for b := 0 to len-1 do
703 if PanelArray[b].Active then
704 for c := 0 to j do
705 if g_CollideAround(PanelArray[b].X,
706 PanelArray[b].Y,
707 PanelArray[b].Width,
708 PanelArray[b].Height,
709 PanelArray[gLiftMap[i, c]].X,
710 PanelArray[gLiftMap[i, c]].Y,
711 PanelArray[gLiftMap[i, c]].Width,
712 PanelArray[gLiftMap[i, c]].Height) then
713 begin
714 PanelArray[b].Active := False;
715 j := j+1;
716 if j > High(gLiftMap[i]) then
717 SetLength(gLiftMap[i],
718 Length(gLiftMap[i])+32);
720 gLiftMap[i, j] := b;
721 ok := True;
723 Break;
724 end;
725 end;
727 SetLength(gLiftMap[i], j+1);
728 i := i+1;
730 g_Game_StepLoading();
731 end;
733 SetLength(gLiftMap, i);
735 PanelArray := nil;
736 end;
738 function CreatePanel(PanelRec: TDynRecord; AddTextures: TAddTextureArray;
739 CurTex: Integer; sav: Boolean): Integer;
740 var
741 len: Integer;
742 panels: ^TPanelArray;
743 begin
744 Result := -1;
746 case PanelRec.PanelType of
747 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
748 panels := @gWalls;
749 PANEL_BACK:
750 panels := @gRenderBackgrounds;
751 PANEL_FORE:
752 panels := @gRenderForegrounds;
753 PANEL_WATER:
754 panels := @gWater;
755 PANEL_ACID1:
756 panels := @gAcid1;
757 PANEL_ACID2:
758 panels := @gAcid2;
759 PANEL_STEP:
760 panels := @gSteps;
761 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
762 panels := @gLifts;
763 PANEL_BLOCKMON:
764 panels := @gBlockMon;
765 else
766 Exit;
767 end;
769 len := Length(panels^);
770 SetLength(panels^, len + 1);
772 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
773 panels^[len].arrIdx := len;
774 panels^[len].proxyId := -1;
775 panels^[len].tag := panelTypeToTag(PanelRec.PanelType);
776 if sav then
777 panels^[len].SaveIt := True;
779 Result := len;
781 len := Length(PanelByID);
782 SetLength(PanelByID, len + 1);
783 PanelByID[len].PWhere := panels;
784 PanelByID[len].PArrID := Result;
785 end;
787 function CreateNullTexture(RecName: String): Integer;
788 begin
789 SetLength(Textures, Length(Textures)+1);
790 result := High(Textures);
792 with Textures[High(Textures)] do
793 begin
794 TextureName := RecName;
795 Width := 1;
796 Height := 1;
797 Anim := False;
798 TextureID := LongWord(TEXTURE_NONE);
799 end;
800 end;
802 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
803 var
804 WAD: TWADFile;
805 TextureData: Pointer;
806 WADName, txname: String;
807 a, ResLength: Integer;
808 begin
809 Result := -1;
811 if Textures <> nil then
812 for a := 0 to High(Textures) do
813 if Textures[a].TextureName = RecName then
814 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
815 Result := a;
816 Exit;
817 end;
819 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
820 if (RecName = TEXTURE_NAME_WATER) or
821 (RecName = TEXTURE_NAME_ACID1) or
822 (RecName = TEXTURE_NAME_ACID2) then
823 begin
824 SetLength(Textures, Length(Textures)+1);
826 with Textures[High(Textures)] do
827 begin
828 TextureName := RecName;
830 if TextureName = TEXTURE_NAME_WATER then
831 TextureID := LongWord(TEXTURE_SPECIAL_WATER)
832 else
833 if TextureName = TEXTURE_NAME_ACID1 then
834 TextureID := LongWord(TEXTURE_SPECIAL_ACID1)
835 else
836 if TextureName = TEXTURE_NAME_ACID2 then
837 TextureID := LongWord(TEXTURE_SPECIAL_ACID2);
839 Anim := False;
840 end;
842 result := High(Textures);
843 Exit;
844 end;
846 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
847 WADName := g_ExtractWadName(RecName);
849 WAD := TWADFile.Create();
851 if WADName <> '' then
852 WADName := GameDir+'/wads/'+WADName
853 else
854 WADName := Map;
856 WAD.ReadFile(WADName);
858 txname := RecName;
860 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
861 begin
862 FreeMem(TextureData);
863 RecName := 'COMMON\ALIEN';
864 end;
867 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
868 begin
869 SetLength(Textures, Length(Textures)+1);
870 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
871 Exit;
872 e_GetTextureSize(Textures[High(Textures)].TextureID,
873 @Textures[High(Textures)].Width,
874 @Textures[High(Textures)].Height);
875 FreeMem(TextureData);
876 Textures[High(Textures)].TextureName := {RecName}txname;
877 Textures[High(Textures)].Anim := False;
879 result := High(Textures);
880 end
881 else // Íåò òàêîãî ðåóñðñà â WAD'å
882 begin
883 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
884 if log then
885 begin
886 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
887 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
888 end;
889 end;
891 WAD.Free();
892 end;
894 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
895 var
896 WAD: TWADFile;
897 TextureWAD: PChar = nil;
898 TextData: Pointer = nil;
899 TextureData: Pointer = nil;
900 cfg: TConfig = nil;
901 WADName: String;
902 ResLength: Integer;
903 TextureResource: String;
904 _width, _height, _framecount, _speed: Integer;
905 _backanimation: Boolean;
906 //imgfmt: string;
907 ia: TDynImageDataArray = nil;
908 f, c, frdelay, frloop: Integer;
909 begin
910 result := -1;
912 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
914 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
915 WADName := g_ExtractWadName(RecName);
917 WAD := TWADFile.Create();
918 try
919 if WADName <> '' then
920 WADName := GameDir+'/wads/'+WADName
921 else
922 WADName := Map;
924 WAD.ReadFile(WADName);
926 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
927 begin
928 if log then
929 begin
930 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
931 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
932 end;
933 exit;
934 end;
936 {TEST
937 if WADName = Map then
938 begin
939 //FreeMem(TextureWAD);
940 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
941 end;
944 WAD.FreeWAD();
946 if ResLength < 6 then
947 begin
948 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
949 exit;
950 end;
952 // ýòî ïòèöà? ýòî ñàìîë¸ò?
953 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
954 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
955 begin
956 // íåò, ýòî ñóïåðìåí!
957 if not WAD.ReadMemory(TextureWAD, ResLength) then
958 begin
959 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
960 exit;
961 end;
963 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
964 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
965 begin
966 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
967 exit;
968 end;
970 cfg := TConfig.CreateMem(TextData, ResLength);
972 TextureResource := cfg.ReadStr('', 'resource', '');
973 if TextureResource = '' then
974 begin
975 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
976 exit;
977 end;
979 _width := cfg.ReadInt('', 'framewidth', 0);
980 _height := cfg.ReadInt('', 'frameheight', 0);
981 _framecount := cfg.ReadInt('', 'framecount', 0);
982 _speed := cfg.ReadInt('', 'waitcount', 0);
983 _backanimation := cfg.ReadBool('', 'backanimation', False);
985 cfg.Free();
986 cfg := nil;
988 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
989 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
990 begin
991 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
992 exit;
993 end;
995 WAD.Free();
996 WAD := nil;
998 SetLength(Textures, Length(Textures)+1);
999 with Textures[High(Textures)] do
1000 begin
1001 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
1002 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
1003 begin
1004 TextureName := RecName;
1005 Width := _width;
1006 Height := _height;
1007 Anim := True;
1008 FramesCount := _framecount;
1009 Speed := _speed;
1010 result := High(Textures);
1011 end
1012 else
1013 begin
1014 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
1015 end;
1016 end;
1017 end
1018 else
1019 begin
1020 // try animated image
1022 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
1023 if length(imgfmt) = 0 then
1024 begin
1025 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
1026 exit;
1027 end;
1029 GlobalMetadata.ClearMetaItems();
1030 GlobalMetadata.ClearMetaItemsForSaving();
1031 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
1032 begin
1033 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
1034 exit;
1035 end;
1036 if length(ia) = 0 then
1037 begin
1038 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
1039 exit;
1040 end;
1042 WAD.Free();
1043 WAD := nil;
1045 _width := ia[0].width;
1046 _height := ia[0].height;
1047 _framecount := length(ia);
1048 _speed := 1;
1049 _backanimation := false;
1050 frdelay := -1;
1051 frloop := -666;
1052 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
1053 begin
1054 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
1055 try
1056 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
1057 frdelay := f;
1058 if f < 0 then f := 0;
1059 // rounding ;-)
1060 c := f mod 28;
1061 if c < 13 then c := 0 else c := 1;
1062 f := (f div 28)+c;
1063 if f < 1 then f := 1 else if f > 255 then f := 255;
1064 _speed := f;
1065 except
1066 end;
1067 end;
1068 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
1069 begin
1070 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
1071 try
1072 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
1073 frloop := f;
1074 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
1075 except
1076 end;
1077 end;
1078 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
1079 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
1080 f := ord(_backanimation);
1081 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);
1083 SetLength(Textures, Length(Textures)+1);
1084 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
1085 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
1086 begin
1087 Textures[High(Textures)].TextureName := RecName;
1088 Textures[High(Textures)].Width := _width;
1089 Textures[High(Textures)].Height := _height;
1090 Textures[High(Textures)].Anim := True;
1091 Textures[High(Textures)].FramesCount := length(ia);
1092 Textures[High(Textures)].Speed := _speed;
1093 result := High(Textures);
1094 //writeln(' CREATED!');
1095 end
1096 else
1097 begin
1098 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
1099 end;
1100 end;
1101 finally
1102 for f := 0 to High(ia) do FreeImage(ia[f]);
1103 WAD.Free();
1104 cfg.Free();
1105 if TextureWAD <> nil then FreeMem(TextureWAD);
1106 if TextData <> nil then FreeMem(TextData);
1107 if TextureData <> nil then FreeMem(TextureData);
1108 end;
1109 end;
1111 procedure CreateItem(Item: TDynRecord);
1112 begin
1113 if g_Game_IsClient then Exit;
1115 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
1116 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
1117 Exit;
1119 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
1120 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
1121 end;
1123 procedure CreateArea(Area: TDynRecord);
1124 var
1125 a: Integer;
1126 id: DWORD = 0;
1127 begin
1128 case Area.AreaType of
1129 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
1130 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
1131 begin
1132 SetLength(RespawnPoints, Length(RespawnPoints)+1);
1133 with RespawnPoints[High(RespawnPoints)] do
1134 begin
1135 X := Area.X;
1136 Y := Area.Y;
1137 Direction := TDirection(Area.Direction);
1139 case Area.AreaType of
1140 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
1141 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
1142 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
1143 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
1144 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
1145 end;
1146 end;
1147 end;
1149 AREA_REDFLAG, AREA_BLUEFLAG:
1150 begin
1151 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
1153 if FlagPoints[a] <> nil then Exit;
1155 New(FlagPoints[a]);
1157 with FlagPoints[a]^ do
1158 begin
1159 X := Area.X-FLAGRECT.X;
1160 Y := Area.Y-FLAGRECT.Y;
1161 Direction := TDirection(Area.Direction);
1162 end;
1164 with gFlags[a] do
1165 begin
1166 case a of
1167 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
1168 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
1169 end;
1171 Animation := TAnimation.Create(id, True, 8);
1172 Obj.Rect := FLAGRECT;
1174 g_Map_ResetFlag(a);
1175 end;
1176 end;
1178 AREA_DOMFLAG:
1179 begin
1180 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
1181 with DOMFlagPoints[High(DOMFlagPoints)] do
1182 begin
1183 X := Area.X;
1184 Y := Area.Y;
1185 Direction := TDirection(Area.Direction);
1186 end;
1188 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
1189 end;
1190 end;
1191 end;
1193 procedure CreateTrigger(Trigger: TDynRecord; atpanid, atrigpanid: Integer; fTexturePanel1Type, fTexturePanel2Type: Word);
1194 var
1195 _trigger: TTrigger;
1196 begin
1197 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
1199 with _trigger do
1200 begin
1201 X := Trigger.X;
1202 Y := Trigger.Y;
1203 Width := Trigger.Width;
1204 Height := Trigger.Height;
1205 Enabled := ByteBool(Trigger.Enabled);
1206 //TexturePanel := Trigger.TexturePanel;
1207 TexturePanel := atpanid;
1208 TexturePanelType := fTexturePanel1Type;
1209 ShotPanelType := fTexturePanel2Type;
1210 TriggerType := Trigger.TriggerType;
1211 ActivateType := Trigger.ActivateType;
1212 Keys := Trigger.Keys;
1213 trigPanelId := atrigpanid;
1214 //trigShotPanelId := ashotpanid;
1215 //Data.Default := Trigger.DATA;
1216 trigData := Trigger.trigRec.clone();
1217 end;
1219 g_Triggers_Create(_trigger);
1220 end;
1222 procedure CreateMonster(monster: TDynRecord);
1223 var
1224 a: Integer;
1225 mon: TMonster;
1226 begin
1227 if g_Game_IsClient then Exit;
1229 if (gGameSettings.GameType = GT_SINGLE)
1230 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1231 begin
1232 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1234 if gTriggers <> nil then
1235 begin
1236 for a := 0 to High(gTriggers) do
1237 begin
1238 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1239 begin
1240 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1241 if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1242 end;
1243 end;
1244 end;
1246 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1247 end;
1248 end;
1250 procedure g_Map_ReAdd_DieTriggers();
1252 function monsDieTrig (mon: TMonster): Boolean;
1253 var
1254 a: Integer;
1255 begin
1256 result := false; // don't stop
1257 mon.ClearTriggers();
1258 for a := 0 to High(gTriggers) do
1259 begin
1260 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1261 begin
1262 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1263 if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1264 end;
1265 end;
1266 end;
1268 begin
1269 if g_Game_IsClient then Exit;
1271 g_Mons_ForEach(monsDieTrig);
1272 end;
1274 function extractWadName(resourceName: string): string;
1275 var
1276 posN: Integer;
1277 begin
1278 posN := Pos(':', resourceName);
1279 if posN > 0 then
1280 Result:= Copy(resourceName, 0, posN-1)
1281 else
1282 Result := '';
1283 end;
1285 procedure addResToExternalResList(res: string);
1286 begin
1287 res := extractWadName(res);
1288 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1289 gExternalResources.Add(res);
1290 end;
1292 procedure generateExternalResourcesList({mapReader: TMapReader_1}map: TDynRecord);
1293 //var
1294 //textures: TTexturesRec1Array;
1295 //textures: TDynField;
1296 //trec: TDynRecord;
1297 //mapHeader: TMapHeaderRec_1;
1298 //i: integer;
1299 //resFile: String = '';
1300 begin
1301 if gExternalResources = nil then
1302 gExternalResources := TStringList.Create;
1304 gExternalResources.Clear;
1306 (*
1308 textures := GetTextures(map);
1309 for i := 0 to High(textures) do
1310 begin
1311 addResToExternalResList(resFile);
1312 end;
1315 textures := map['texture'];
1316 if (textures <> nil) then
1317 begin
1318 for trec in textures do
1319 begin
1320 addResToExternalResList(resFile);
1321 end;
1322 end;
1324 textures := nil;
1325 *)
1327 //mapHeader := GetMapHeader(map);
1329 addResToExternalResList(map.MusicName);
1330 addResToExternalResList(map.SkyName);
1331 end;
1334 procedure mapCreateGrid ();
1335 var
1336 mapX0: Integer = $3fffffff;
1337 mapY0: Integer = $3fffffff;
1338 mapX1: Integer = -$3fffffff;
1339 mapY1: Integer = -$3fffffff;
1341 procedure calcBoundingBox (constref panels: TPanelArray);
1342 var
1343 idx: Integer;
1344 pan: TPanel;
1345 begin
1346 for idx := 0 to High(panels) do
1347 begin
1348 pan := panels[idx];
1349 if not pan.visvalid then continue;
1350 if (pan.Width < 1) or (pan.Height < 1) then continue;
1351 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1352 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1353 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1354 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1355 end;
1356 end;
1358 procedure addPanelsToGrid (constref panels: TPanelArray);
1359 var
1360 idx: Integer;
1361 pan: TPanel;
1362 newtag: Integer;
1363 begin
1364 //tag := panelTypeToTag(tag);
1365 for idx := 0 to High(panels) do
1366 begin
1367 pan := panels[idx];
1368 if not pan.visvalid then continue;
1369 if (pan.proxyId <> -1) then
1370 begin
1371 {$IF DEFINED(D2F_DEBUG)}
1372 e_WriteLog(Format('DUPLICATE wall #%d(%d) enabled (%d); type:%08x', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.PanelType]), MSG_NOTIFY);
1373 {$ENDIF}
1374 continue;
1375 end;
1376 case pan.PanelType of
1377 PANEL_WALL: newtag := GridTagWall;
1378 PANEL_OPENDOOR, PANEL_CLOSEDOOR: newtag := GridTagDoor;
1379 PANEL_BACK: newtag := GridTagBack;
1380 PANEL_FORE: newtag := GridTagFore;
1381 PANEL_WATER: newtag := GridTagWater;
1382 PANEL_ACID1: newtag := GridTagAcid1;
1383 PANEL_ACID2: newtag := GridTagAcid2;
1384 PANEL_STEP: newtag := GridTagStep;
1385 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: newtag := GridTagLift;
1386 PANEL_BLOCKMON: newtag := GridTagBlockMon;
1387 else continue; // oops
1388 end;
1389 pan.tag := newtag;
1391 pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, newtag);
1392 // "enabled" flag has meaning only for doors and walls (engine assumes it); but meh...
1393 mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
1394 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
1396 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
1397 begin
1398 e_WriteLog(Format('INSERTED wall #%d(%d) enabled (%d)', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY);
1399 end;
1401 {$ENDIF}
1402 end;
1403 end;
1405 begin
1406 mapGrid.Free();
1407 mapGrid := nil;
1409 calcBoundingBox(gWalls);
1410 calcBoundingBox(gRenderBackgrounds);
1411 calcBoundingBox(gRenderForegrounds);
1412 calcBoundingBox(gWater);
1413 calcBoundingBox(gAcid1);
1414 calcBoundingBox(gAcid2);
1415 calcBoundingBox(gSteps);
1416 calcBoundingBox(gLifts);
1417 calcBoundingBox(gBlockMon);
1419 e_LogWritefln('map dimensions: (%d,%d)-(%d,%d); editor size:(0,0)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1, gMapInfo.Width, gMapInfo.Height]);
1421 if (mapX0 > 0) then mapX0 := 0;
1422 if (mapY0 > 0) then mapY0 := 0;
1424 if (mapX1 < gMapInfo.Width-1) then mapX1 := gMapInfo.Width-1;
1425 if (mapY1 < gMapInfo.Height-1) then mapY1 := gMapInfo.Height-1;
1427 mapGrid := TPanelGrid.Create(mapX0-128, mapY0-128, mapX1-mapX0+1+128*2, mapY1-mapY0+1+128*2);
1428 //mapGrid := TPanelGrid.Create(0, 0, gMapInfo.Width, gMapInfo.Height);
1430 addPanelsToGrid(gWalls);
1431 addPanelsToGrid(gRenderBackgrounds);
1432 addPanelsToGrid(gRenderForegrounds);
1433 addPanelsToGrid(gWater);
1434 addPanelsToGrid(gAcid1);
1435 addPanelsToGrid(gAcid2);
1436 addPanelsToGrid(gSteps);
1437 addPanelsToGrid(gLifts); // it doesn't matter which LIFT type is used here
1438 addPanelsToGrid(gBlockMon);
1440 mapGrid.dumpStats();
1442 g_Mons_InitTree(mapGrid.gridX0, mapGrid.gridY0, mapGrid.gridWidth, mapGrid.gridHeight);
1443 end;
1446 function g_Map_Load(Res: String): Boolean;
1447 const
1448 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1449 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1450 type
1451 PTRec = ^TTRec;
1452 TTRec = record
1453 //TexturePanel: Integer;
1454 texPanIdx: Integer;
1455 LiftPanelIdx: Integer;
1456 DoorPanelIdx: Integer;
1457 ShotPanelIdx: Integer;
1458 trigrec: TDynRecord;
1459 texPan: TDynRecord;
1460 liftPan: TDynRecord;
1461 doorPan: TDynRecord;
1462 shotPan: TDynRecord;
1463 end;
1464 var
1465 WAD: TWADFile;
1466 //MapReader: TMapReader_1;
1467 mapReader: TDynRecord = nil;
1468 //Header: TMapHeaderRec_1;
1469 _textures: TDynField = nil; //TTexturesRec1Array; tagInt: texture index
1470 //_texnummap: array of Integer = nil; // `_textures` -> `Textures`
1471 panels: TDynField = nil; //TPanelsRec1Array;
1472 items: TDynField = nil; //TItemsRec1Array;
1473 monsters: TDynField = nil; //TMonsterRec1Array;
1474 areas: TDynField = nil; //TAreasRec1Array;
1475 triggers: TDynField = nil; //TTriggersRec1Array;
1476 b, c, k: Integer;
1477 PanelID: DWORD;
1478 AddTextures: TAddTextureArray;
1479 //texture: TTextureRec_1;
1480 TriggersTable: array of TTRec;
1481 FileName, mapResName, s, TexName: String;
1482 Data: Pointer;
1483 Len: Integer;
1484 ok, isAnim, trigRef: Boolean;
1485 CurTex, ntn: Integer;
1486 rec, texrec: TDynRecord;
1487 pttit: PTRec;
1488 pannum, trignum, cnt, tgpid: Integer;
1489 // key: panel index; value: `TriggersTable` index
1490 hashTextPan: THashIntInt = nil;
1491 hashLiftPan: THashIntInt = nil;
1492 hashDoorPan: THashIntInt = nil;
1493 hashShotPan: THashIntInt = nil;
1494 begin
1495 mapGrid.Free();
1496 mapGrid := nil;
1498 gCurrentMap.Free();
1499 gCurrentMap := nil;
1501 Result := False;
1502 gMapInfo.Map := Res;
1503 TriggersTable := nil;
1504 mapReader := nil;
1505 //FillChar(texture, SizeOf(texture), 0);
1507 sfsGCDisable(); // temporary disable removing of temporary volumes
1508 try
1509 // Çàãðóçêà WAD:
1510 FileName := g_ExtractWadName(Res);
1511 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1512 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1514 WAD := TWADFile.Create();
1515 if not WAD.ReadFile(FileName) then
1516 begin
1517 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1518 WAD.Free();
1519 Exit;
1520 end;
1522 //k8: why loader ignores path here?
1523 mapResName := g_ExtractFileName(Res);
1524 if not WAD.GetMapResource(mapResName, Data, Len) then
1525 begin
1526 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1527 WAD.Free();
1528 Exit;
1529 end;
1531 WAD.Free();
1533 if (Len < 4) then
1534 begin
1535 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1536 FreeMem(Data);
1537 exit;
1538 end;
1540 // Çàãðóçêà êàðòû:
1541 e_LogWritefln('Loading map: %s', [mapResName], MSG_NOTIFY);
1542 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1544 try
1545 mapReader := g_Map_ParseMap(Data, Len);
1546 except
1547 mapReader.Free();
1548 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1549 FreeMem(Data);
1550 Exit;
1551 end;
1553 FreeMem(Data);
1555 hashTextPan := hashNewIntInt();
1556 hashLiftPan := hashNewIntInt();
1557 hashDoorPan := hashNewIntInt();
1558 hashShotPan := hashNewIntInt();
1560 generateExternalResourcesList(MapReader);
1561 //_textures := GetTextures(mapReader);
1562 _textures := mapReader['texture'];
1563 //_texnummap := nil;
1564 // get all other lists here too
1565 //panels := GetPanels(mapReader);
1566 panels := mapReader['panel'];
1567 //triggers := GetTriggers(mapReader);
1568 triggers := mapReader['trigger'];
1569 //items := GetItems(mapReader);
1570 items := mapReader['item'];
1571 //areas := GetAreas(mapReader);
1572 areas := mapReader['area'];
1573 //monsters := GetMonsters(mapReader);
1574 monsters := mapReader['monster'];
1576 // Çàãðóçêà îïèñàíèÿ êàðòû:
1577 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1578 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1579 //Header := GetMapHeader(mapReader);
1581 with gMapInfo do
1582 begin
1583 Name := mapReader.MapName;
1584 Description := mapReader.MapDesc;
1585 Author := mapReader.MapAuthor;
1586 MusicName := mapReader.MusicName;
1587 SkyName := mapReader.SkyName;
1588 Height := mapReader.Height;
1589 Width := mapReader.Width;
1590 end;
1592 // Çàãðóçêà òåêñòóð:
1593 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1594 // Äîáàâëåíèå òåêñòóð â Textures[]:
1595 if (_textures <> nil) and (_textures.count > 0) then
1596 begin
1597 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1598 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], _textures.count-1, False);
1599 //SetLength(_texnummap, _textures.count);
1601 cnt := -1;
1602 for rec in _textures do
1603 begin
1604 Inc(cnt);
1605 s := rec.Resource;
1606 {$IF DEFINED(D2F_DEBUG_TXLOAD)}
1607 e_WriteLog(Format(' Loading texture #%d: %s', [cnt, s]), MSG_NOTIFY);
1608 {$ENDIF}
1609 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1610 if rec.Anim then
1611 begin
1612 // Àíèìèðîâàííàÿ òåêñòóðà
1613 ntn := CreateAnimTexture(rec.Resource, FileName, True);
1614 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1615 end
1616 else
1617 begin
1618 // Îáû÷íàÿ òåêñòóðà
1619 ntn := CreateTexture(rec.Resource, FileName, True);
1620 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1621 end;
1622 if (ntn < 0) then ntn := CreateNullTexture(rec.Resource);
1624 //_texnummap[a] := ntn; // fix texture number
1625 rec.tagInt := ntn;
1626 g_Game_StepLoading();
1627 end;
1629 // set panel tagInt to texture index
1630 if (panels <> nil) then
1631 begin
1632 for rec in panels do
1633 begin
1634 texrec := rec.TextureRec;
1635 if (texrec = nil) then rec.tagInt := -1 else rec.tagInt := texrec.tagInt;
1636 end;
1637 end;
1638 end;
1640 // Çàãðóçêà òðèããåðîâ
1641 gTriggerClientID := 0;
1642 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1643 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1645 // Çàãðóçêà ïàíåëåé
1646 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1647 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1649 // check texture numbers for panels
1650 if (panels <> nil) and (panels.count > 0) then
1651 begin
1652 for rec in panels do
1653 begin
1654 if (rec.tagInt < 0) then
1655 begin
1656 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1657 result := false;
1658 exit;
1659 end;
1660 end;
1661 end;
1663 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì)
1664 if (triggers <> nil) and (triggers.count > 0) then
1665 begin
1666 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1667 //SetLength(TriggersTable, triggers.count);
1668 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], triggers.count-1, False);
1670 for rec in triggers do
1671 begin
1672 SetLength(TriggersTable, Length(TriggersTable)+1);
1673 pttit := @TriggersTable[High(TriggersTable)];
1674 pttit.trigrec := rec;
1675 pttit.texPan := mapReader.panel[rec.TexturePanel];
1676 pttit.liftPan := nil;
1677 pttit.doorPan := nil;
1678 pttit.shotPan := nil;
1679 pttit.texPanIdx := -1;
1680 pttit.LiftPanelIdx := -1;
1681 pttit.DoorPanelIdx := -1;
1682 pttit.ShotPanelIdx := -1;
1683 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè)
1684 //if (rec.TexturePanel >= 0) and (pttit.texPan = nil) then e_WriteLog('error loading map: invalid texture panel index for trigger', MSG_WARNING);
1685 // Ëèôòû
1686 if rec.TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1687 begin
1688 //pttit.LiftPanel := TTriggerData(rec.DATA).PanelID
1689 pttit.liftPan := mapReader.panel[rec.trigRec.tgPanelID];
1690 //if (rec.trigRec.trigPanelID >= 0) and (pttit.liftPan = nil) then e_WriteLog('error loading map: invalid lift panel index for trigger', MSG_WARNING);
1691 end;
1692 // Äâåðè
1693 if rec.TriggerType in [TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1694 begin
1695 //pttit.DoorPanel := TTriggerData(rec.DATA).PanelID
1696 pttit.doorPan := mapReader.panel[rec.trigRec.tgPanelID];
1697 end;
1698 // Òóðåëü
1699 if (rec.TriggerType = TRIGGER_SHOT) then
1700 begin
1701 //pttit.ShotPanel := TTriggerData(rec.DATA).ShotPanelID
1702 pttit.shotPan := mapReader.panel[rec.trigRec.tgShotPanelID];
1703 end;
1705 // update hashes
1706 if (pttit.texPan <> nil) then hashTextPan.put(rec.TexturePanel, High(TriggersTable));
1707 if (pttit.liftPan <> nil) then hashLiftPan.put(rec.trigRec.tgPanelID, High(TriggersTable));
1708 if (pttit.doorPan <> nil) then hashDoorPan.put(rec.trigRec.tgPanelID, High(TriggersTable));
1709 if (pttit.shotPan <> nil) then hashShotPan.put(rec.trigRec.tgShotPanelID, High(TriggersTable));
1711 g_Game_StepLoading();
1712 end;
1713 end;
1715 // Ñîçäàåì ïàíåëè
1716 if (panels <> nil) and (panels.count > 0) then
1717 begin
1718 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1719 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], panels.count-1, False);
1721 pannum := -1;
1722 for rec in panels do
1723 begin
1724 Inc(pannum);
1725 texrec := nil;
1726 SetLength(AddTextures, 0);
1727 trigRef := False;
1728 CurTex := -1;
1729 ok := false;
1731 if (_textures <> nil) then
1732 begin
1733 texrec := rec.TextureRec;
1734 ok := (texrec <> nil);
1735 end;
1737 if ok then
1738 begin
1739 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1740 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð
1741 ok := false;
1742 if (TriggersTable <> nil) and (_textures <> nil) then
1743 begin
1744 for b := 0 to High(TriggersTable) do
1745 begin
1746 if (TriggersTable[b].texPan = rec) or (TriggersTable[b].shotPan = rec) then
1747 begin
1748 trigRef := True;
1749 ok := True;
1750 break;
1751 end;
1752 end;
1753 end;
1754 end;
1756 if ok then
1757 begin
1758 // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1759 s := texrec.Resource;
1761 // Ñïåö-òåêñòóðû çàïðåùåíû
1762 if g_Map_IsSpecialTexture(s) then
1763 begin
1764 ok := false
1765 end
1766 else
1767 begin
1768 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè
1769 ok := g_Texture_NumNameFindStart(s);
1770 end;
1772 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1773 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #
1774 if ok then
1775 begin
1776 k := NNF_NAME_BEFORE;
1777 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû
1778 while ok or (k = NNF_NAME_BEFORE) or (k = NNF_NAME_EQUALS) do
1779 begin
1780 k := g_Texture_NumNameFindNext(TexName);
1782 if (k = NNF_NAME_BEFORE) or (k = NNF_NAME_AFTER) then
1783 begin
1784 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó
1785 if texrec.Anim then
1786 begin
1787 // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1788 isAnim := True;
1789 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1790 if not ok then
1791 begin
1792 // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1793 isAnim := False;
1794 ok := CreateTexture(TexName, FileName, False) >= 0;
1795 end;
1796 end
1797 else
1798 begin
1799 // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1800 isAnim := False;
1801 ok := CreateTexture(TexName, FileName, False) >= 0;
1802 if not ok then
1803 begin
1804 // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1805 isAnim := True;
1806 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1807 end;
1808 end;
1810 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè
1811 if ok then
1812 begin
1813 for c := 0 to High(Textures) do
1814 begin
1815 if (Textures[c].TextureName = TexName) then
1816 begin
1817 SetLength(AddTextures, Length(AddTextures)+1);
1818 AddTextures[High(AddTextures)].Texture := c;
1819 AddTextures[High(AddTextures)].Anim := isAnim;
1820 break;
1821 end;
1822 end;
1823 end;
1824 end
1825 else
1826 begin
1827 if k = NNF_NAME_EQUALS then
1828 begin
1829 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî
1830 SetLength(AddTextures, Length(AddTextures)+1);
1831 //AddTextures[High(AddTextures)].Texture := _texnummap[rec.TextureNum]; //panels[a].TextureNum;
1832 AddTextures[High(AddTextures)].Texture := rec.tagInt; //panels[a].TextureNum;
1833 AddTextures[High(AddTextures)].Anim := texrec.Anim;
1834 CurTex := High(AddTextures);
1835 ok := true;
1836 end
1837 else // NNF_NO_NAME
1838 begin
1839 ok := false;
1840 end;
1841 end;
1842 end; // while ok...
1844 ok := true;
1845 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1846 end; // if ok - ññûëàþòñÿ òðèããåðû
1848 if not ok then
1849 begin
1850 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó
1851 SetLength(AddTextures, 1);
1852 AddTextures[0].Texture := rec.tagInt; //panels[a].TextureNum;
1853 AddTextures[0].Anim := false;
1854 if (texrec <> nil) then AddTextures[0].Anim := texrec.Anim;
1855 CurTex := 0;
1856 end;
1858 //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);
1860 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð
1861 PanelID := CreatePanel(rec, AddTextures, CurTex, trigRef);
1862 //e_LogWritefln('panel #%s of type %s got id #%s', [pannum, rec.PanelType, PanelID]);
1864 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID
1865 if hashTextPan.get(pannum, b) then TriggersTable[b].texPanIdx := PanelID;
1866 if hashLiftPan.get(pannum, b) then TriggersTable[b].LiftPanelIdx := PanelID;
1867 if hashDoorPan.get(pannum, b) then TriggersTable[b].DoorPanelIdx := PanelID;
1868 if hashShotPan.get(pannum, b) then TriggersTable[b].ShotPanelIdx := PanelID;
1870 (*
1871 if (TriggersTable <> nil) then
1872 begin
1873 for b := 0 to High(TriggersTable) do
1874 begin
1875 if (TriggersTable[b].texPan = rec) then TriggersTable[b].texPanIdx := PanelID;
1876 if (TriggersTable[b].liftPan = rec) then TriggersTable[b].LiftPanelIdx := PanelID;
1877 if (TriggersTable[b].doorPan = rec) then TriggersTable[b].DoorPanelIdx := PanelID;
1878 if (TriggersTable[b].shotPan = rec) then TriggersTable[b].ShotPanelIdx := PanelID;
1880 // Òðèããåð äâåðè/ëèôòà
1881 if (TriggersTable[b].LiftPanel = pannum) or
1882 (TriggersTable[b].DoorPanel = pannum) then
1883 //TTriggerData(TriggersTable[b].trigrec.DATA).PanelID := PanelID;
1884 TriggersTable[b].trigrec.trigRec.trigPanelID := PanelID;
1885 // Òðèããåð ñìåíû òåêñòóðû
1886 if TriggersTable[b].texPanIdx = pannum then
1887 TriggersTable[b].trigrec.TexturePanel := PanelID;
1888 // Òðèããåð "Òóðåëü"
1889 if TriggersTable[b].ShotPanel = pannum then
1890 TriggersTable[b].trigrec.trigRec.trigShotPanelID := PanelID;
1892 end;
1893 end;
1894 *)
1896 g_Game_StepLoading();
1897 end;
1898 end;
1900 (*
1901 begin
1902 for b := 0 to High(TriggersTable) do
1903 begin
1904 // Òðèããåð äâåðè/ëèôòà
1905 if (TriggersTable[b].texPan <> nil) then e_LogWritefln('trigger #%s: textPan=%s; panidx=%s', [b, TriggersTable[b].texPanIdx, mapReader.panelIndex[TriggersTable[b].texPan]]);
1906 if (TriggersTable[b].liftPan <> nil) then e_LogWritefln('trigger #%s: liftPan=%s; panidx=%s', [b, TriggersTable[b].LiftPanelIdx, mapReader.panelIndex[TriggersTable[b].liftPan]]);
1907 if (TriggersTable[b].doorPan <> nil) then e_LogWritefln('trigger #%s: doorPan=%s; panidx=%s', [b, TriggersTable[b].DoorPanelIdx, mapReader.panelIndex[TriggersTable[b].doorPan]]);
1908 if (TriggersTable[b].shotPan <> nil) then e_LogWritefln('trigger #%s: shotPan=%s; panidx=%s', [b, TriggersTable[b].ShotPanelIdx, mapReader.panelIndex[TriggersTable[b].shotPan]]);
1909 end;
1910 end;
1911 *)
1913 // create map grid, init other grids (for monsters, for example)
1914 e_WriteLog('Creating map grid', MSG_NOTIFY);
1915 mapCreateGrid();
1917 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû
1918 if (triggers <> nil) and (panels <> nil) and (not gLoadGameMode) then
1919 begin
1920 e_LogWritefln(' Creating triggers (%d)...', [triggers.count]);
1921 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1922 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü
1923 trignum := -1;
1924 for rec in triggers do
1925 begin
1926 Inc(trignum);
1927 if (TriggersTable[trignum].texPan <> nil) then b := TriggersTable[trignum].texPan.PanelType else b := 0;
1928 if (TriggersTable[trignum].shotPan <> nil) then c := TriggersTable[trignum].shotPan.PanelType else c := 0;
1929 tgpid := -1;
1930 if (TriggersTable[trignum].LiftPanelIdx <> -1) then tgpid := TriggersTable[trignum].LiftPanelIdx
1931 else if (TriggersTable[trignum].DoorPanelIdx <> -1) then tgpid := TriggersTable[trignum].DoorPanelIdx
1932 else if (TriggersTable[trignum].ShotPanelIdx <> -1) then tgpid := TriggersTable[trignum].ShotPanelIdx
1933 else tgpid := -1;
1934 (*
1935 if (rec.TexturePanel <> -1) then
1936 begin
1938 if (TriggersTable[trignum].TexturePanel < 0) or (TriggersTable[trignum].TexturePanel >= panels.count) then
1939 begin
1940 e_WriteLog('error loading map: invalid panel index for trigger', MSG_FATALERROR);
1941 result := false;
1942 exit;
1943 end;
1945 //b := panels[TriggersTable[a].TexturePanel].PanelType;
1946 //b := mapReader.panel[TriggersTable[trignum].texPanIdx].PanelType;
1947 assert(TriggersTable[trignum].texPanIdx >= 0);
1948 b := TriggersTable[trignum].texPanIdx;
1949 end
1950 else
1951 begin
1952 b := 0;
1953 end;
1954 e_LogWritefln('trigger #%s: type=%s; texPanIdx=%s; b=%s', [trignum, rec.TriggerType, TriggersTable[trignum].texPanIdx, b]);
1955 if (rec.TriggerType = TRIGGER_SHOT) then e_LogWritefln(' SHOT: shotpanidx=%s', [rec.trigRec.trigShotPanelID]);
1956 if (rec.TriggerType = TRIGGER_SHOT) and {(rec.trigRec.trigShotPanelID <> -1)} (TriggersTable[trignum].shotPan <> nil) then
1957 begin
1958 //c := panels[TriggersTable[a].ShotPanel].PanelType;
1959 //c := mapReader.panel[TriggersTable[trignum].ShotPanel].PanelType;
1960 assert(TriggersTable[trignum].ShotPanelIdx >= 0);
1961 c := TriggersTable[trignum].ShotPanelIdx;
1962 end
1963 else
1964 begin
1965 c := 0;
1966 end;
1967 *)
1968 //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]);
1969 CreateTrigger(rec, TriggersTable[trignum].texPanIdx, tgpid, Word(b), Word(c));
1970 end;
1971 end;
1973 // Çàãðóçêà ïðåäìåòîâ
1974 e_WriteLog(' Loading items...', MSG_NOTIFY);
1975 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1977 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû
1978 if (items <> nil) and not gLoadGameMode then
1979 begin
1980 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1981 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1982 for rec in items do CreateItem(rec);
1983 end;
1985 // Çàãðóçêà îáëàñòåé
1986 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1987 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1989 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè
1990 if areas <> nil then
1991 begin
1992 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1993 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1994 for rec in areas do CreateArea(rec);
1995 end;
1997 // Çàãðóçêà ìîíñòðîâ
1998 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1999 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
2001 gTotalMonsters := 0;
2003 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ
2004 if (monsters <> nil) and not gLoadGameMode then
2005 begin
2006 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
2007 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
2008 for rec in monsters do CreateMonster(rec);
2009 end;
2011 //MapReader.Free();
2012 gCurrentMap := mapReader;
2013 mapReader := nil;
2015 // Çàãðóçêà íåáà
2016 if gMapInfo.SkyName <> '' then
2017 begin
2018 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
2019 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
2020 FileName := g_ExtractWadName(gMapInfo.SkyName);
2022 if FileName <> '' then
2023 FileName := GameDir+'/wads/'+FileName
2024 else
2025 begin
2026 FileName := g_ExtractWadName(Res);
2027 end;
2029 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
2030 if g_Texture_CreateWAD(BackID, s) then
2031 begin
2032 g_Game_SetupScreenSize();
2033 end
2034 else
2035 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
2036 end;
2038 // Çàãðóçêà ìóçûêè
2039 ok := False;
2040 if gMapInfo.MusicName <> '' then
2041 begin
2042 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
2043 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
2044 FileName := g_ExtractWadName(gMapInfo.MusicName);
2046 if FileName <> '' then
2047 FileName := GameDir+'/wads/'+FileName
2048 else
2049 begin
2050 FileName := g_ExtractWadName(Res);
2051 end;
2053 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
2054 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
2055 ok := True
2056 else
2057 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
2058 end;
2060 // Îñòàëüíûå óñòàíâêè
2061 CreateDoorMap();
2062 CreateLiftMap();
2064 g_Items_Init();
2065 g_Weapon_Init();
2066 g_Monsters_Init();
2068 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
2069 if not gLoadGameMode then g_GFX_Init();
2071 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
2072 _textures := nil;
2073 panels := nil;
2074 items := nil;
2075 areas := nil;
2076 triggers := nil;
2077 TriggersTable := nil;
2078 AddTextures := nil;
2080 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
2081 if ok and (not gLoadGameMode) then
2082 begin
2083 gMusic.SetByName(gMapInfo.MusicName);
2084 gMusic.Play();
2085 end
2086 else
2087 begin
2088 gMusic.SetByName('');
2089 end;
2090 finally
2091 sfsGCEnable(); // enable releasing unused volumes
2092 mapReader.Free();
2093 hashTextPan.Free();
2094 hashLiftPan.Free();
2095 hashDoorPan.Free();
2096 hashShotPan.Free();
2097 end;
2099 e_WriteLog('Done loading map.', MSG_NOTIFY);
2100 Result := True;
2101 end;
2103 function g_Map_GetMapInfo(Res: String): TMapInfo;
2104 var
2105 WAD: TWADFile;
2106 MapReader: TDynRecord;
2107 //Header: TMapHeaderRec_1;
2108 FileName: String;
2109 Data: Pointer;
2110 Len: Integer;
2111 begin
2112 FillChar(Result, SizeOf(Result), 0);
2113 FileName := g_ExtractWadName(Res);
2115 WAD := TWADFile.Create();
2116 if not WAD.ReadFile(FileName) then
2117 begin
2118 WAD.Free();
2119 Exit;
2120 end;
2122 //k8: it ignores path again
2123 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
2124 begin
2125 WAD.Free();
2126 Exit;
2127 end;
2129 WAD.Free();
2132 MapReader := TMapReader_1.Create();
2133 if not MapReader.LoadMap(Data) then
2134 begin
2135 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2136 ZeroMemory(@Header, SizeOf(Header));
2137 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2138 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2139 end
2140 else
2141 begin
2142 Header := MapReader.GetMapHeader();
2143 Result.Name := Header.MapName;
2144 Result.Description := Header.MapDescription;
2145 end;
2147 try
2148 mapReader := g_Map_ParseMap(Data, Len);
2149 except
2150 mapReader := nil;
2151 end;
2153 FreeMem(Data);
2154 //MapReader.Free();
2156 //if (mapReader <> nil) then Header := GetMapHeader(mapReader) else FillChar(Header, sizeof(Header), 0);
2157 //MapReader.Free();
2159 if (mapReader.Width > 0) and (mapReader.Height > 0) then
2160 begin
2161 Result.Name := mapReader.MapName;
2162 Result.Description := mapReader.MapDesc;
2163 Result.Map := Res;
2164 Result.Author := mapReader.MapAuthor;
2165 Result.Height := mapReader.Height;
2166 Result.Width := mapReader.Width;
2167 end
2168 else
2169 begin
2170 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2171 //ZeroMemory(@Header, SizeOf(Header));
2172 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2173 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2174 Result.Map := Res;
2175 Result.Author := '';
2176 Result.Height := 0;
2177 Result.Width := 0;
2178 end;
2180 mapReader.Free();
2181 end;
2183 function g_Map_GetMapsList(WADName: string): SArray;
2184 var
2185 WAD: TWADFile;
2186 a: Integer;
2187 ResList: SArray;
2188 begin
2189 Result := nil;
2190 WAD := TWADFile.Create();
2191 if not WAD.ReadFile(WADName) then
2192 begin
2193 WAD.Free();
2194 Exit;
2195 end;
2196 ResList := WAD.GetMapResources();
2197 if ResList <> nil then
2198 begin
2199 for a := 0 to High(ResList) do
2200 begin
2201 SetLength(Result, Length(Result)+1);
2202 Result[High(Result)] := ResList[a];
2203 end;
2204 end;
2205 WAD.Free();
2206 end;
2208 function g_Map_Exist(Res: string): Boolean;
2209 var
2210 WAD: TWADFile;
2211 FileName, mnn: string;
2212 ResList: SArray;
2213 a: Integer;
2214 begin
2215 Result := False;
2217 FileName := addWadExtension(g_ExtractWadName(Res));
2219 WAD := TWADFile.Create;
2220 if not WAD.ReadFile(FileName) then
2221 begin
2222 WAD.Free();
2223 Exit;
2224 end;
2226 ResList := WAD.GetMapResources();
2227 WAD.Free();
2229 mnn := g_ExtractFileName(Res);
2230 if ResList <> nil then
2231 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
2232 begin
2233 Result := True;
2234 Exit;
2235 end;
2236 end;
2238 procedure g_Map_Free();
2239 var
2240 a: Integer;
2242 procedure FreePanelArray(var panels: TPanelArray);
2243 var
2244 i: Integer;
2246 begin
2247 if panels <> nil then
2248 begin
2249 for i := 0 to High(panels) do
2250 panels[i].Free();
2251 panels := nil;
2252 end;
2253 end;
2255 begin
2256 g_GFX_Free();
2257 g_Weapon_Free();
2258 g_Items_Free();
2259 g_Triggers_Free();
2260 g_Monsters_Free();
2262 RespawnPoints := nil;
2263 if FlagPoints[FLAG_RED] <> nil then
2264 begin
2265 Dispose(FlagPoints[FLAG_RED]);
2266 FlagPoints[FLAG_RED] := nil;
2267 end;
2268 if FlagPoints[FLAG_BLUE] <> nil then
2269 begin
2270 Dispose(FlagPoints[FLAG_BLUE]);
2271 FlagPoints[FLAG_BLUE] := nil;
2272 end;
2273 //DOMFlagPoints := nil;
2275 //gDOMFlags := nil;
2277 if Textures <> nil then
2278 begin
2279 for a := 0 to High(Textures) do
2280 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
2281 if Textures[a].Anim then
2282 g_Frames_DeleteByID(Textures[a].FramesID)
2283 else
2284 if Textures[a].TextureID <> LongWord(TEXTURE_NONE) then
2285 e_DeleteTexture(Textures[a].TextureID);
2287 Textures := nil;
2288 end;
2290 FreePanelArray(gWalls);
2291 FreePanelArray(gRenderBackgrounds);
2292 FreePanelArray(gRenderForegrounds);
2293 FreePanelArray(gWater);
2294 FreePanelArray(gAcid1);
2295 FreePanelArray(gAcid2);
2296 FreePanelArray(gSteps);
2297 FreePanelArray(gLifts);
2298 FreePanelArray(gBlockMon);
2300 if BackID <> DWORD(-1) then
2301 begin
2302 gBackSize.X := 0;
2303 gBackSize.Y := 0;
2304 e_DeleteTexture(BackID);
2305 BackID := DWORD(-1);
2306 end;
2308 g_Game_StopAllSounds(False);
2309 gMusic.FreeSound();
2310 g_Sound_Delete(gMapInfo.MusicName);
2312 gMapInfo.Name := '';
2313 gMapInfo.Description := '';
2314 gMapInfo.MusicName := '';
2315 gMapInfo.Height := 0;
2316 gMapInfo.Width := 0;
2318 gDoorMap := nil;
2319 gLiftMap := nil;
2321 PanelByID := nil;
2322 end;
2324 procedure g_Map_Update();
2325 var
2326 a, d, j: Integer;
2327 m: Word;
2328 s: String;
2330 procedure UpdatePanelArray(var panels: TPanelArray);
2331 var
2332 i: Integer;
2334 begin
2335 if panels <> nil then
2336 for i := 0 to High(panels) do
2337 panels[i].Update();
2338 end;
2340 begin
2341 UpdatePanelArray(gWalls);
2342 UpdatePanelArray(gRenderBackgrounds);
2343 UpdatePanelArray(gRenderForegrounds);
2344 UpdatePanelArray(gWater);
2345 UpdatePanelArray(gAcid1);
2346 UpdatePanelArray(gAcid2);
2347 UpdatePanelArray(gSteps);
2349 if gGameSettings.GameMode = GM_CTF then
2350 begin
2351 for a := FLAG_RED to FLAG_BLUE do
2352 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2353 with gFlags[a] do
2354 begin
2355 if gFlags[a].Animation <> nil then
2356 gFlags[a].Animation.Update();
2358 m := g_Obj_Move(@Obj, True, True);
2360 if gTime mod (GAME_TICK*2) <> 0 then
2361 Continue;
2363 // Ñîïðîòèâëåíèå âîçäóõà:
2364 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2366 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
2367 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2368 begin
2369 g_Map_ResetFlag(a);
2370 gFlags[a].CaptureTime := 0;
2371 if a = FLAG_RED then
2372 s := _lc[I_PLAYER_FLAG_RED]
2373 else
2374 s := _lc[I_PLAYER_FLAG_BLUE];
2375 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2377 if g_Game_IsNet then
2378 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2379 Continue;
2380 end;
2382 if Count > 0 then
2383 Count := Count - 1;
2385 // Èãðîê áåðåò ôëàã:
2386 if gPlayers <> nil then
2387 begin
2388 j := Random(Length(gPlayers)) - 1;
2390 for d := 0 to High(gPlayers) do
2391 begin
2392 Inc(j);
2393 if j > High(gPlayers) then
2394 j := 0;
2396 if gPlayers[j] <> nil then
2397 if gPlayers[j].Live and
2398 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2399 begin
2400 if gPlayers[j].GetFlag(a) then
2401 Break;
2402 end;
2403 end;
2404 end;
2405 end;
2406 end;
2407 end;
2410 // old algo
2411 procedure g_Map_DrawPanels (PanelType: Word);
2413 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2414 var
2415 idx: Integer;
2416 begin
2417 if (panels <> nil) then
2418 begin
2419 // alas, no visible set
2420 for idx := 0 to High(panels) do
2421 begin
2422 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
2423 end;
2424 end;
2425 end;
2427 begin
2428 case PanelType of
2429 PANEL_WALL: DrawPanels(gWalls);
2430 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2431 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2432 PANEL_FORE: DrawPanels(gRenderForegrounds);
2433 PANEL_WATER: DrawPanels(gWater);
2434 PANEL_ACID1: DrawPanels(gAcid1);
2435 PANEL_ACID2: DrawPanels(gAcid2);
2436 PANEL_STEP: DrawPanels(gSteps);
2437 end;
2438 end;
2441 // new algo
2442 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2444 function checker (pan: TPanel; tag: Integer): Boolean;
2445 begin
2446 result := false; // don't stop, ever
2447 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2448 gDrawPanelList.insert(pan);
2449 end;
2451 begin
2452 dplClear();
2453 //tagmask := panelTypeToTag(PanelType);
2454 mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask);
2455 // list will be rendered in `g_game.DrawPlayer()`
2456 end;
2459 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2461 function checker (pan: TPanel; tag: Integer): Boolean;
2462 begin
2463 result := false; // don't stop, ever
2464 pan.DrawShadowVolume(lightX, lightY, radius);
2465 end;
2467 begin
2468 mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2469 end;
2472 procedure g_Map_DrawBack(dx, dy: Integer);
2473 begin
2474 if gDrawBackGround and (BackID <> DWORD(-1)) then
2475 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2476 else
2477 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2478 end;
2480 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2481 PanelType: Word; b1x3: Boolean=false): Boolean;
2482 var
2483 a, h: Integer;
2484 begin
2485 Result := False;
2487 if WordBool(PanelType and PANEL_WALL) then
2488 if gWalls <> nil then
2489 begin
2490 h := High(gWalls);
2492 for a := 0 to h do
2493 if gWalls[a].Enabled and
2494 g_Collide(X, Y, Width, Height,
2495 gWalls[a].X, gWalls[a].Y,
2496 gWalls[a].Width, gWalls[a].Height) then
2497 begin
2498 Result := True;
2499 Exit;
2500 end;
2501 end;
2503 if WordBool(PanelType and PANEL_WATER) then
2504 if gWater <> nil then
2505 begin
2506 h := High(gWater);
2508 for a := 0 to h do
2509 if g_Collide(X, Y, Width, Height,
2510 gWater[a].X, gWater[a].Y,
2511 gWater[a].Width, gWater[a].Height) then
2512 begin
2513 Result := True;
2514 Exit;
2515 end;
2516 end;
2518 if WordBool(PanelType and PANEL_ACID1) then
2519 if gAcid1 <> nil then
2520 begin
2521 h := High(gAcid1);
2523 for a := 0 to h do
2524 if g_Collide(X, Y, Width, Height,
2525 gAcid1[a].X, gAcid1[a].Y,
2526 gAcid1[a].Width, gAcid1[a].Height) then
2527 begin
2528 Result := True;
2529 Exit;
2530 end;
2531 end;
2533 if WordBool(PanelType and PANEL_ACID2) then
2534 if gAcid2 <> nil then
2535 begin
2536 h := High(gAcid2);
2538 for a := 0 to h do
2539 if g_Collide(X, Y, Width, Height,
2540 gAcid2[a].X, gAcid2[a].Y,
2541 gAcid2[a].Width, gAcid2[a].Height) then
2542 begin
2543 Result := True;
2544 Exit;
2545 end;
2546 end;
2548 if WordBool(PanelType and PANEL_STEP) then
2549 if gSteps <> nil then
2550 begin
2551 h := High(gSteps);
2553 for a := 0 to h do
2554 if g_Collide(X, Y, Width, Height,
2555 gSteps[a].X, gSteps[a].Y,
2556 gSteps[a].Width, gSteps[a].Height) then
2557 begin
2558 Result := True;
2559 Exit;
2560 end;
2561 end;
2563 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2564 if gLifts <> nil then
2565 begin
2566 h := High(gLifts);
2568 for a := 0 to h do
2569 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2570 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2571 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2572 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2573 g_Collide(X, Y, Width, Height,
2574 gLifts[a].X, gLifts[a].Y,
2575 gLifts[a].Width, gLifts[a].Height) then
2576 begin
2577 Result := True;
2578 Exit;
2579 end;
2580 end;
2582 if WordBool(PanelType and PANEL_BLOCKMON) then
2583 if gBlockMon <> nil then
2584 begin
2585 h := High(gBlockMon);
2587 for a := 0 to h do
2588 if ( (not b1x3) or
2589 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2590 g_Collide(X, Y, Width, Height,
2591 gBlockMon[a].X, gBlockMon[a].Y,
2592 gBlockMon[a].Width, gBlockMon[a].Height) then
2593 begin
2594 Result := True;
2595 Exit;
2596 end;
2597 end;
2598 end;
2600 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2601 var
2602 texid: DWORD;
2604 function checkPanels (constref panels: TPanelArray): Boolean;
2605 var
2606 a: Integer;
2607 begin
2608 result := false;
2609 if panels = nil then exit;
2610 for a := 0 to High(panels) do
2611 begin
2612 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2613 begin
2614 result := true;
2615 texid := panels[a].GetTextureID();
2616 exit;
2617 end;
2618 end;
2619 end;
2621 begin
2622 texid := LongWord(TEXTURE_NONE);
2623 result := texid;
2624 if not checkPanels(gWater) then
2625 if not checkPanels(gAcid1) then
2626 if not checkPanels(gAcid2) then exit;
2627 result := texid;
2628 end;
2631 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2632 const
2633 SlowMask = GridTagLift or GridTagBlockMon;
2634 function checker (pan: TPanel; tag: Integer): Boolean;
2635 begin
2637 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2638 begin
2639 result := pan.Enabled;
2640 exit;
2641 end;
2644 if ((tag and GridTagLift) <> 0) then
2645 begin
2646 result :=
2647 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2648 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2649 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2650 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
2651 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
2652 exit;
2653 end;
2655 if ((tag and GridTagBlockMon) <> 0) then
2656 begin
2657 result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2658 exit;
2659 end;
2661 // other shit
2662 //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2663 result := true; // i found her!
2664 end;
2666 var
2667 tagmask: Integer = 0;
2668 begin
2669 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2670 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2671 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2672 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2673 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2674 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2675 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2677 if (tagmask = 0) then begin result := false; exit; end; // just in case
2679 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2680 if gdbg_map_use_accel_coldet then
2681 begin
2682 if (Width = 1) and (Height = 1) then
2683 begin
2684 if ((tagmask and SlowMask) <> 0) then
2685 begin
2686 // slow
2687 result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil);
2688 end
2689 else
2690 begin
2691 // fast
2692 result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil);
2693 end;
2694 end
2695 else
2696 begin
2697 if ((tagmask and SlowMask) <> 0) then
2698 begin
2699 // slow
2700 result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil);
2701 end
2702 else
2703 begin
2704 // fast
2705 result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil);
2706 end;
2707 end;
2708 end
2709 else
2710 begin
2711 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2712 end;
2713 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2714 end;
2717 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2718 var
2719 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2720 texid: DWORD;
2722 // slightly different from the old code, but meh...
2723 function checker (pan: TPanel; tag: Integer): Boolean;
2724 begin
2725 result := false; // don't stop, ever
2726 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2727 // check priorities
2728 case cctype of
2729 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2730 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2731 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2732 end;
2733 // collision?
2734 //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2735 // yeah
2736 texid := pan.GetTextureID();
2737 // water? water has the highest priority, so stop right here
2738 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2739 // acid2?
2740 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2741 // acid1?
2742 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2743 end;
2745 begin
2746 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2747 if gdbg_map_use_accel_coldet then
2748 begin
2749 texid := LongWord(TEXTURE_NONE);
2750 if (Width = 1) and (Height = 1) then
2751 begin
2752 mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2753 end
2754 else
2755 begin
2756 mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2757 end;
2758 result := texid;
2759 end
2760 else
2761 begin
2762 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2763 end;
2764 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2765 end;
2768 procedure g_Map_EnableWall(ID: DWORD);
2769 var
2770 pan: TPanel;
2771 begin
2772 pan := gWalls[ID];
2773 pan.Enabled := True;
2774 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, True);
2776 mapGrid.proxyEnabled[pan.proxyId] := true;
2777 //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
2778 //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
2780 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(gWalls[ID].PanelType, ID);
2782 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2783 //e_WriteLog(Format('ENABLE: wall #%d(%d) enabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY);
2784 {$ENDIF}
2785 end;
2787 procedure g_Map_DisableWall(ID: DWORD);
2788 var
2789 pan: TPanel;
2790 begin
2791 pan := gWalls[ID];
2792 pan.Enabled := False;
2793 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, False);
2795 mapGrid.proxyEnabled[pan.proxyId] := false;
2796 //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
2798 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, ID);
2800 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2801 //e_WriteLog(Format('DISABLE: wall #%d(%d) disabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY);
2802 {$ENDIF}
2803 end;
2805 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2806 var
2807 tp: TPanel;
2808 begin
2809 case PanelType of
2810 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2811 tp := gWalls[ID];
2812 PANEL_FORE:
2813 tp := gRenderForegrounds[ID];
2814 PANEL_BACK:
2815 tp := gRenderBackgrounds[ID];
2816 PANEL_WATER:
2817 tp := gWater[ID];
2818 PANEL_ACID1:
2819 tp := gAcid1[ID];
2820 PANEL_ACID2:
2821 tp := gAcid2[ID];
2822 PANEL_STEP:
2823 tp := gSteps[ID];
2824 else
2825 Exit;
2826 end;
2828 tp.NextTexture(AnimLoop);
2829 if g_Game_IsServer and g_Game_IsNet then
2830 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2831 end;
2833 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2834 begin
2835 if gLifts[ID].LiftType = t then
2836 Exit;
2838 with gLifts[ID] do
2839 begin
2840 LiftType := t;
2842 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2843 //TODO: make separate lift tags, and change tag here
2845 if LiftType = 0 then
2846 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2847 else if LiftType = 1 then
2848 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2849 else if LiftType = 2 then
2850 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2851 else if LiftType = 3 then
2852 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2854 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2855 end;
2856 end;
2858 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2859 var
2860 a: Integer;
2861 PointsArray: Array of TRespawnPoint;
2862 begin
2863 Result := False;
2864 SetLength(PointsArray, 0);
2866 if RespawnPoints = nil then
2867 Exit;
2869 for a := 0 to High(RespawnPoints) do
2870 if RespawnPoints[a].PointType = PointType then
2871 begin
2872 SetLength(PointsArray, Length(PointsArray)+1);
2873 PointsArray[High(PointsArray)] := RespawnPoints[a];
2874 end;
2876 if PointsArray = nil then
2877 Exit;
2879 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2880 Result := True;
2881 end;
2883 function g_Map_GetPointCount(PointType: Byte): Word;
2884 var
2885 a: Integer;
2886 begin
2887 Result := 0;
2889 if RespawnPoints = nil then
2890 Exit;
2892 for a := 0 to High(RespawnPoints) do
2893 if RespawnPoints[a].PointType = PointType then
2894 Result := Result + 1;
2895 end;
2897 function g_Map_HaveFlagPoints(): Boolean;
2898 begin
2899 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2900 end;
2902 procedure g_Map_ResetFlag(Flag: Byte);
2903 begin
2904 with gFlags[Flag] do
2905 begin
2906 Obj.X := -1000;
2907 Obj.Y := -1000;
2908 Obj.Vel.X := 0;
2909 Obj.Vel.Y := 0;
2910 Direction := D_LEFT;
2911 State := FLAG_STATE_NONE;
2912 if FlagPoints[Flag] <> nil then
2913 begin
2914 Obj.X := FlagPoints[Flag]^.X;
2915 Obj.Y := FlagPoints[Flag]^.Y;
2916 Direction := FlagPoints[Flag]^.Direction;
2917 State := FLAG_STATE_NORMAL;
2918 end;
2919 Count := -1;
2920 end;
2921 end;
2923 procedure g_Map_DrawFlags();
2924 var
2925 i, dx: Integer;
2926 Mirror: TMirrorType;
2927 begin
2928 if gGameSettings.GameMode <> GM_CTF then
2929 Exit;
2931 for i := FLAG_RED to FLAG_BLUE do
2932 with gFlags[i] do
2933 if State <> FLAG_STATE_CAPTURED then
2934 begin
2935 if State = FLAG_STATE_NONE then
2936 continue;
2938 if Direction = D_LEFT then
2939 begin
2940 Mirror := M_HORIZONTAL;
2941 dx := -1;
2942 end
2943 else
2944 begin
2945 Mirror := M_NONE;
2946 dx := 1;
2947 end;
2949 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2951 if g_debug_Frames then
2952 begin
2953 e_DrawQuad(Obj.X+Obj.Rect.X,
2954 Obj.Y+Obj.Rect.Y,
2955 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2956 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2957 0, 255, 0);
2958 end;
2959 end;
2960 end;
2962 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2963 var
2964 dw: DWORD;
2965 b: Byte;
2966 str: String;
2967 boo: Boolean;
2969 procedure SavePanelArray(var panels: TPanelArray);
2970 var
2971 PAMem: TBinMemoryWriter;
2972 i: Integer;
2973 begin
2974 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2975 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2977 i := 0;
2978 while i < Length(panels) do
2979 begin
2980 if panels[i].SaveIt then
2981 begin
2982 // ID ïàíåëè:
2983 PAMem.WriteInt(i);
2984 // Ñîõðàíÿåì ïàíåëü:
2985 panels[i].SaveState(PAMem);
2986 end;
2987 Inc(i);
2988 end;
2990 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2991 PAMem.SaveToMemory(Mem);
2992 PAMem.Free();
2993 end;
2995 procedure SaveFlag(flag: PFlag);
2996 begin
2997 // Ñèãíàòóðà ôëàãà:
2998 dw := FLAG_SIGNATURE; // 'FLAG'
2999 Mem.WriteDWORD(dw);
3000 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
3001 Mem.WriteByte(flag^.RespawnType);
3002 // Ñîñòîÿíèå ôëàãà:
3003 Mem.WriteByte(flag^.State);
3004 // Íàïðàâëåíèå ôëàãà:
3005 if flag^.Direction = D_LEFT then
3006 b := 1
3007 else // D_RIGHT
3008 b := 2;
3009 Mem.WriteByte(b);
3010 // Îáúåêò ôëàãà:
3011 Obj_SaveState(@flag^.Obj, Mem);
3012 end;
3014 begin
3015 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
3017 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
3018 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
3019 SavePanelArray(gWalls);
3020 // Ñîõðàíÿåì ïàíåëè ôîíà:
3021 SavePanelArray(gRenderBackgrounds);
3022 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
3023 SavePanelArray(gRenderForegrounds);
3024 // Ñîõðàíÿåì ïàíåëè âîäû:
3025 SavePanelArray(gWater);
3026 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
3027 SavePanelArray(gAcid1);
3028 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
3029 SavePanelArray(gAcid2);
3030 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
3031 SavePanelArray(gSteps);
3032 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
3033 SavePanelArray(gLifts);
3034 ///// /////
3036 ///// Ñîõðàíÿåì ìóçûêó: /////
3037 // Ñèãíàòóðà ìóçûêè:
3038 dw := MUSIC_SIGNATURE; // 'MUSI'
3039 Mem.WriteDWORD(dw);
3040 // Íàçâàíèå ìóçûêè:
3041 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
3042 if gMusic.NoMusic then
3043 str := ''
3044 else
3045 str := gMusic.Name;
3046 Mem.WriteString(str, 64);
3047 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
3048 dw := gMusic.GetPosition();
3049 Mem.WriteDWORD(dw);
3050 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
3051 boo := gMusic.SpecPause;
3052 Mem.WriteBoolean(boo);
3053 ///// /////
3055 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
3056 Mem.WriteInt(gTotalMonsters);
3057 ///// /////
3059 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
3060 if gGameSettings.GameMode = GM_CTF then
3061 begin
3062 // Ôëàã Êðàñíîé êîìàíäû:
3063 SaveFlag(@gFlags[FLAG_RED]);
3064 // Ôëàã Ñèíåé êîìàíäû:
3065 SaveFlag(@gFlags[FLAG_BLUE]);
3066 end;
3067 ///// /////
3069 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3070 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3071 begin
3072 // Î÷êè Êðàñíîé êîìàíäû:
3073 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
3074 // Î÷êè Ñèíåé êîìàíäû:
3075 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
3076 end;
3077 ///// /////
3078 end;
3080 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
3081 var
3082 dw: DWORD;
3083 b: Byte;
3084 str: String;
3085 boo: Boolean;
3087 procedure LoadPanelArray(var panels: TPanelArray);
3088 var
3089 PAMem: TBinMemoryReader;
3090 i, id: Integer;
3091 begin
3092 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
3093 PAMem := TBinMemoryReader.Create();
3094 PAMem.LoadFromMemory(Mem);
3096 for i := 0 to Length(panels)-1 do
3097 begin
3098 if panels[i].SaveIt then
3099 begin
3100 // ID ïàíåëè:
3101 PAMem.ReadInt(id);
3102 if id <> i then
3103 begin
3104 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
3105 end;
3106 // Çàãðóæàåì ïàíåëü:
3107 panels[i].LoadState(PAMem);
3108 if (panels[i].arrIdx <> i) then raise Exception.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel arrIdx');
3109 if (panels[i].proxyId >= 0) then mapGrid.proxyEnabled[panels[i].proxyId] := panels[i].Enabled;
3110 end;
3111 end;
3113 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
3114 PAMem.Free();
3115 end;
3117 procedure LoadFlag(flag: PFlag);
3118 begin
3119 // Ñèãíàòóðà ôëàãà:
3120 Mem.ReadDWORD(dw);
3121 if dw <> FLAG_SIGNATURE then // 'FLAG'
3122 begin
3123 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
3124 end;
3125 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
3126 Mem.ReadByte(flag^.RespawnType);
3127 // Ñîñòîÿíèå ôëàãà:
3128 Mem.ReadByte(flag^.State);
3129 // Íàïðàâëåíèå ôëàãà:
3130 Mem.ReadByte(b);
3131 if b = 1 then
3132 flag^.Direction := D_LEFT
3133 else // b = 2
3134 flag^.Direction := D_RIGHT;
3135 // Îáúåêò ôëàãà:
3136 Obj_LoadState(@flag^.Obj, Mem);
3137 end;
3139 begin
3140 if Mem = nil then
3141 Exit;
3143 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
3144 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
3145 LoadPanelArray(gWalls);
3146 // Çàãðóæàåì ïàíåëè ôîíà:
3147 LoadPanelArray(gRenderBackgrounds);
3148 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
3149 LoadPanelArray(gRenderForegrounds);
3150 // Çàãðóæàåì ïàíåëè âîäû:
3151 LoadPanelArray(gWater);
3152 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
3153 LoadPanelArray(gAcid1);
3154 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
3155 LoadPanelArray(gAcid2);
3156 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
3157 LoadPanelArray(gSteps);
3158 // Çàãðóæàåì ïàíåëè ëèôòîâ:
3159 LoadPanelArray(gLifts);
3160 ///// /////
3162 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
3163 g_GFX_Init();
3164 //mapCreateGrid();
3166 ///// Çàãðóæàåì ìóçûêó: /////
3167 // Ñèãíàòóðà ìóçûêè:
3168 Mem.ReadDWORD(dw);
3169 if dw <> MUSIC_SIGNATURE then // 'MUSI'
3170 begin
3171 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
3172 end;
3173 // Íàçâàíèå ìóçûêè:
3174 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
3175 Mem.ReadString(str);
3176 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
3177 Mem.ReadDWORD(dw);
3178 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
3179 Mem.ReadBoolean(boo);
3180 // Çàïóñêàåì ýòó ìóçûêó:
3181 gMusic.SetByName(str);
3182 gMusic.SpecPause := boo;
3183 gMusic.Play();
3184 gMusic.Pause(True);
3185 gMusic.SetPosition(dw);
3186 ///// /////
3188 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
3189 Mem.ReadInt(gTotalMonsters);
3190 ///// /////
3192 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
3193 if gGameSettings.GameMode = GM_CTF then
3194 begin
3195 // Ôëàã Êðàñíîé êîìàíäû:
3196 LoadFlag(@gFlags[FLAG_RED]);
3197 // Ôëàã Ñèíåé êîìàíäû:
3198 LoadFlag(@gFlags[FLAG_BLUE]);
3199 end;
3200 ///// /////
3202 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3203 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3204 begin
3205 // Î÷êè Êðàñíîé êîìàíäû:
3206 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
3207 // Î÷êè Ñèíåé êîìàíäû:
3208 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
3209 end;
3210 ///// /////
3211 end;
3213 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
3214 var
3215 Arr: TPanelArray;
3216 begin
3217 Result := nil;
3218 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
3219 Arr := PanelByID[PanelID].PWhere^;
3220 PanelArrayID := PanelByID[PanelID].PArrID;
3221 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
3222 end;
3225 // trace liquid, stepping by `dx` and `dy`
3226 // return last seen liquid coords, and `false` if we're started outside of the liquid
3227 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
3228 const
3229 MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
3230 begin
3231 topx := x;
3232 topy := y;
3233 // started outside of the liquid?
3234 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end;
3235 if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check
3236 result := true;
3237 while true do
3238 begin
3239 Inc(x, dx);
3240 Inc(y, dy);
3241 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit
3242 topx := x;
3243 topy := y;
3244 end;
3245 end;
3248 end.