DEADSOFTWARE

no more old-styled map structured; sadly, most triggers aren't working; also, save...
[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, ashotpanid: 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 TexturePanelType := fTexturePanel1Type;
1208 ShotPanelType := fTexturePanel2Type;
1209 TriggerType := Trigger.TriggerType;
1210 ActivateType := Trigger.ActivateType;
1211 Keys := Trigger.Keys;
1212 trigPanelId := atpanid;
1213 trigShotPanelId := ashotpanid;
1214 //Data.Default := Trigger.DATA;
1215 trigData := Trigger.trigRec.clone();
1216 end;
1218 g_Triggers_Create(_trigger);
1219 end;
1221 procedure CreateMonster(monster: TDynRecord);
1222 var
1223 a: Integer;
1224 mon: TMonster;
1225 begin
1226 if g_Game_IsClient then Exit;
1228 if (gGameSettings.GameType = GT_SINGLE)
1229 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1230 begin
1231 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1233 if gTriggers <> nil then
1234 begin
1235 for a := 0 to High(gTriggers) do
1236 begin
1237 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1238 begin
1239 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1240 if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1241 end;
1242 end;
1243 end;
1245 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1246 end;
1247 end;
1249 procedure g_Map_ReAdd_DieTriggers();
1251 function monsDieTrig (mon: TMonster): Boolean;
1252 var
1253 a: Integer;
1254 begin
1255 result := false; // don't stop
1256 mon.ClearTriggers();
1257 for a := 0 to High(gTriggers) do
1258 begin
1259 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1260 begin
1261 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1262 if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1263 end;
1264 end;
1265 end;
1267 begin
1268 if g_Game_IsClient then Exit;
1270 g_Mons_ForEach(monsDieTrig);
1271 end;
1273 function extractWadName(resourceName: string): string;
1274 var
1275 posN: Integer;
1276 begin
1277 posN := Pos(':', resourceName);
1278 if posN > 0 then
1279 Result:= Copy(resourceName, 0, posN-1)
1280 else
1281 Result := '';
1282 end;
1284 procedure addResToExternalResList(res: string);
1285 begin
1286 res := extractWadName(res);
1287 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1288 gExternalResources.Add(res);
1289 end;
1291 procedure generateExternalResourcesList({mapReader: TMapReader_1}map: TDynRecord);
1292 //var
1293 //textures: TTexturesRec1Array;
1294 //textures: TDynField;
1295 //trec: TDynRecord;
1296 //mapHeader: TMapHeaderRec_1;
1297 //i: integer;
1298 //resFile: String = '';
1299 begin
1300 if gExternalResources = nil then
1301 gExternalResources := TStringList.Create;
1303 gExternalResources.Clear;
1305 (*
1307 textures := GetTextures(map);
1308 for i := 0 to High(textures) do
1309 begin
1310 addResToExternalResList(resFile);
1311 end;
1314 textures := map['texture'];
1315 if (textures <> nil) then
1316 begin
1317 for trec in textures do
1318 begin
1319 addResToExternalResList(resFile);
1320 end;
1321 end;
1323 textures := nil;
1324 *)
1326 //mapHeader := GetMapHeader(map);
1328 addResToExternalResList(map.MusicName);
1329 addResToExternalResList(map.SkyName);
1330 end;
1333 procedure mapCreateGrid ();
1334 var
1335 mapX0: Integer = $3fffffff;
1336 mapY0: Integer = $3fffffff;
1337 mapX1: Integer = -$3fffffff;
1338 mapY1: Integer = -$3fffffff;
1340 procedure calcBoundingBox (constref panels: TPanelArray);
1341 var
1342 idx: Integer;
1343 pan: TPanel;
1344 begin
1345 for idx := 0 to High(panels) do
1346 begin
1347 pan := panels[idx];
1348 if not pan.visvalid then continue;
1349 if (pan.Width < 1) or (pan.Height < 1) then continue;
1350 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1351 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1352 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1353 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1354 end;
1355 end;
1357 procedure addPanelsToGrid (constref panels: TPanelArray);
1358 var
1359 idx: Integer;
1360 pan: TPanel;
1361 newtag: Integer;
1362 begin
1363 //tag := panelTypeToTag(tag);
1364 for idx := 0 to High(panels) do
1365 begin
1366 pan := panels[idx];
1367 if not pan.visvalid then continue;
1368 if (pan.proxyId <> -1) then
1369 begin
1370 {$IF DEFINED(D2F_DEBUG)}
1371 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);
1372 {$ENDIF}
1373 continue;
1374 end;
1375 case pan.PanelType of
1376 PANEL_WALL: newtag := GridTagWall;
1377 PANEL_OPENDOOR, PANEL_CLOSEDOOR: newtag := GridTagDoor;
1378 PANEL_BACK: newtag := GridTagBack;
1379 PANEL_FORE: newtag := GridTagFore;
1380 PANEL_WATER: newtag := GridTagWater;
1381 PANEL_ACID1: newtag := GridTagAcid1;
1382 PANEL_ACID2: newtag := GridTagAcid2;
1383 PANEL_STEP: newtag := GridTagStep;
1384 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: newtag := GridTagLift;
1385 PANEL_BLOCKMON: newtag := GridTagBlockMon;
1386 else continue; // oops
1387 end;
1388 pan.tag := newtag;
1390 pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, newtag);
1391 // "enabled" flag has meaning only for doors and walls (engine assumes it); but meh...
1392 mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
1393 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
1395 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
1396 begin
1397 e_WriteLog(Format('INSERTED wall #%d(%d) enabled (%d)', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY);
1398 end;
1400 {$ENDIF}
1401 end;
1402 end;
1404 begin
1405 mapGrid.Free();
1406 mapGrid := nil;
1408 calcBoundingBox(gWalls);
1409 calcBoundingBox(gRenderBackgrounds);
1410 calcBoundingBox(gRenderForegrounds);
1411 calcBoundingBox(gWater);
1412 calcBoundingBox(gAcid1);
1413 calcBoundingBox(gAcid2);
1414 calcBoundingBox(gSteps);
1415 calcBoundingBox(gLifts);
1416 calcBoundingBox(gBlockMon);
1418 e_LogWritefln('map dimensions: (%d,%d)-(%d,%d); editor size:(0,0)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1, gMapInfo.Width, gMapInfo.Height]);
1420 if (mapX0 > 0) then mapX0 := 0;
1421 if (mapY0 > 0) then mapY0 := 0;
1423 if (mapX1 < gMapInfo.Width-1) then mapX1 := gMapInfo.Width-1;
1424 if (mapY1 < gMapInfo.Height-1) then mapY1 := gMapInfo.Height-1;
1426 mapGrid := TPanelGrid.Create(mapX0-128, mapY0-128, mapX1-mapX0+1+128*2, mapY1-mapY0+1+128*2);
1427 //mapGrid := TPanelGrid.Create(0, 0, gMapInfo.Width, gMapInfo.Height);
1429 addPanelsToGrid(gWalls);
1430 addPanelsToGrid(gRenderBackgrounds);
1431 addPanelsToGrid(gRenderForegrounds);
1432 addPanelsToGrid(gWater);
1433 addPanelsToGrid(gAcid1);
1434 addPanelsToGrid(gAcid2);
1435 addPanelsToGrid(gSteps);
1436 addPanelsToGrid(gLifts); // it doesn't matter which LIFT type is used here
1437 addPanelsToGrid(gBlockMon);
1439 mapGrid.dumpStats();
1441 g_Mons_InitTree(mapGrid.gridX0, mapGrid.gridY0, mapGrid.gridWidth, mapGrid.gridHeight);
1442 end;
1445 function g_Map_Load(Res: String): Boolean;
1446 const
1447 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1448 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1449 type
1450 PTRec = ^TTRec;
1451 TTRec = record
1452 //TexturePanel: Integer;
1453 texPanIdx: Integer;
1454 LiftPanelIdx: Integer;
1455 DoorPanelIdx: Integer;
1456 ShotPanelIdx: Integer;
1457 trigrec: TDynRecord;
1458 texPan: TDynRecord;
1459 liftPan: TDynRecord;
1460 doorPan: TDynRecord;
1461 shotPan: TDynRecord;
1462 end;
1463 var
1464 WAD: TWADFile;
1465 //MapReader: TMapReader_1;
1466 mapReader: TDynRecord = nil;
1467 //Header: TMapHeaderRec_1;
1468 _textures: TDynField = nil; //TTexturesRec1Array; tagInt: texture index
1469 //_texnummap: array of Integer = nil; // `_textures` -> `Textures`
1470 panels: TDynField = nil; //TPanelsRec1Array;
1471 items: TDynField = nil; //TItemsRec1Array;
1472 monsters: TDynField = nil; //TMonsterRec1Array;
1473 areas: TDynField = nil; //TAreasRec1Array;
1474 triggers: TDynField = nil; //TTriggersRec1Array;
1475 b, c, k: Integer;
1476 PanelID: DWORD;
1477 AddTextures: TAddTextureArray;
1478 //texture: TTextureRec_1;
1479 TriggersTable: array of TTRec;
1480 FileName, mapResName, s, TexName: String;
1481 Data: Pointer;
1482 Len: Integer;
1483 ok, isAnim, trigRef: Boolean;
1484 CurTex, ntn: Integer;
1485 rec, texrec: TDynRecord;
1486 pttit: PTRec;
1487 pannum, trignum, cnt: Integer;
1488 // key: panel index; value: `TriggersTable` index
1489 hashTextPan: THashIntInt = nil;
1490 hashLiftPan: THashIntInt = nil;
1491 hashDoorPan: THashIntInt = nil;
1492 hashShotPan: THashIntInt = nil;
1493 begin
1494 mapGrid.Free();
1495 mapGrid := nil;
1497 gCurrentMap.Free();
1498 gCurrentMap := nil;
1500 Result := False;
1501 gMapInfo.Map := Res;
1502 TriggersTable := nil;
1503 mapReader := nil;
1504 //FillChar(texture, SizeOf(texture), 0);
1506 sfsGCDisable(); // temporary disable removing of temporary volumes
1507 try
1508 // Çàãðóçêà WAD:
1509 FileName := g_ExtractWadName(Res);
1510 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1511 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1513 WAD := TWADFile.Create();
1514 if not WAD.ReadFile(FileName) then
1515 begin
1516 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1517 WAD.Free();
1518 Exit;
1519 end;
1521 //k8: why loader ignores path here?
1522 mapResName := g_ExtractFileName(Res);
1523 if not WAD.GetMapResource(mapResName, Data, Len) then
1524 begin
1525 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1526 WAD.Free();
1527 Exit;
1528 end;
1530 WAD.Free();
1532 if (Len < 4) then
1533 begin
1534 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1535 FreeMem(Data);
1536 exit;
1537 end;
1539 // Çàãðóçêà êàðòû:
1540 e_LogWritefln('Loading map: %s', [mapResName], MSG_NOTIFY);
1541 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1543 try
1544 mapReader := g_Map_ParseMap(Data, Len);
1545 except
1546 mapReader.Free();
1547 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1548 FreeMem(Data);
1549 Exit;
1550 end;
1552 FreeMem(Data);
1554 hashTextPan := hashNewIntInt();
1555 hashLiftPan := hashNewIntInt();
1556 hashDoorPan := hashNewIntInt();
1557 hashShotPan := hashNewIntInt();
1559 generateExternalResourcesList(MapReader);
1560 //_textures := GetTextures(mapReader);
1561 _textures := mapReader['texture'];
1562 //_texnummap := nil;
1563 // get all other lists here too
1564 //panels := GetPanels(mapReader);
1565 panels := mapReader['panel'];
1566 //triggers := GetTriggers(mapReader);
1567 triggers := mapReader['trigger'];
1568 //items := GetItems(mapReader);
1569 items := mapReader['item'];
1570 //areas := GetAreas(mapReader);
1571 areas := mapReader['area'];
1572 //monsters := GetMonsters(mapReader);
1573 monsters := mapReader['monster'];
1575 // Çàãðóçêà îïèñàíèÿ êàðòû:
1576 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1577 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1578 //Header := GetMapHeader(mapReader);
1580 with gMapInfo do
1581 begin
1582 Name := mapReader.MapName;
1583 Description := mapReader.MapDesc;
1584 Author := mapReader.MapAuthor;
1585 MusicName := mapReader.MusicName;
1586 SkyName := mapReader.SkyName;
1587 Height := mapReader.Height;
1588 Width := mapReader.Width;
1589 end;
1591 // Çàãðóçêà òåêñòóð:
1592 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1593 // Äîáàâëåíèå òåêñòóð â Textures[]:
1594 if (_textures <> nil) and (_textures.count > 0) then
1595 begin
1596 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1597 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], _textures.count-1, False);
1598 //SetLength(_texnummap, _textures.count);
1600 cnt := -1;
1601 for rec in _textures do
1602 begin
1603 Inc(cnt);
1604 s := rec.Resource;
1605 {$IF DEFINED(D2F_DEBUG_TXLOAD)}
1606 e_WriteLog(Format(' Loading texture #%d: %s', [cnt, s]), MSG_NOTIFY);
1607 {$ENDIF}
1608 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1609 if rec.Anim then
1610 begin
1611 // Àíèìèðîâàííàÿ òåêñòóðà
1612 ntn := CreateAnimTexture(rec.Resource, FileName, True);
1613 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1614 end
1615 else
1616 begin
1617 // Îáû÷íàÿ òåêñòóðà
1618 ntn := CreateTexture(rec.Resource, FileName, True);
1619 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1620 end;
1621 if (ntn < 0) then ntn := CreateNullTexture(rec.Resource);
1623 //_texnummap[a] := ntn; // fix texture number
1624 rec.tagInt := ntn;
1625 g_Game_StepLoading();
1626 end;
1628 // set panel tagInt to texture index
1629 if (panels <> nil) then
1630 begin
1631 for rec in panels do
1632 begin
1633 texrec := rec.TextureRec;
1634 if (texrec = nil) then rec.tagInt := -1 else rec.tagInt := texrec.tagInt;
1635 end;
1636 end;
1637 end;
1639 // Çàãðóçêà òðèããåðîâ
1640 gTriggerClientID := 0;
1641 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1642 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1644 // Çàãðóçêà ïàíåëåé
1645 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1646 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1648 // check texture numbers for panels
1649 if (panels <> nil) and (panels.count > 0) then
1650 begin
1651 for rec in panels do
1652 begin
1653 if (rec.tagInt < 0) then
1654 begin
1655 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1656 result := false;
1657 exit;
1658 end;
1659 end;
1660 end;
1662 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì)
1663 if (triggers <> nil) and (triggers.count > 0) then
1664 begin
1665 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1666 //SetLength(TriggersTable, triggers.count);
1667 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], triggers.count-1, False);
1669 for rec in triggers do
1670 begin
1671 SetLength(TriggersTable, Length(TriggersTable)+1);
1672 pttit := @TriggersTable[High(TriggersTable)];
1673 pttit.trigrec := rec;
1674 pttit.texPan := mapReader.panel[rec.TexturePanel];
1675 pttit.liftPan := nil;
1676 pttit.doorPan := nil;
1677 pttit.shotPan := nil;
1678 pttit.texPanIdx := -1;
1679 pttit.LiftPanelIdx := -1;
1680 pttit.DoorPanelIdx := -1;
1681 pttit.ShotPanelIdx := -1;
1682 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè)
1683 //if (rec.TexturePanel >= 0) and (pttit.texPan = nil) then e_WriteLog('error loading map: invalid texture panel index for trigger', MSG_WARNING);
1684 // Ëèôòû
1685 if rec.TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1686 begin
1687 //pttit.LiftPanel := TTriggerData(rec.DATA).PanelID
1688 pttit.liftPan := mapReader.panel[rec.trigRec.tgPanelID];
1689 //if (rec.trigRec.trigPanelID >= 0) and (pttit.liftPan = nil) then e_WriteLog('error loading map: invalid lift panel index for trigger', MSG_WARNING);
1690 end;
1691 // Äâåðè
1692 if rec.TriggerType in [TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1693 begin
1694 //pttit.DoorPanel := TTriggerData(rec.DATA).PanelID
1695 pttit.doorPan := mapReader.panel[rec.trigRec.tgPanelID];
1696 end;
1697 // Òóðåëü
1698 if (rec.TriggerType = TRIGGER_SHOT) then
1699 begin
1700 //pttit.ShotPanel := TTriggerData(rec.DATA).ShotPanelID
1701 pttit.shotPan := mapReader.panel[rec.trigRec.tgShotPanelID];
1702 end;
1704 // update hashes
1705 if (pttit.texPan <> nil) then hashTextPan.put(rec.TexturePanel, High(TriggersTable));
1706 if (pttit.liftPan <> nil) then hashLiftPan.put(rec.trigRec.tgPanelID, High(TriggersTable));
1707 if (pttit.doorPan <> nil) then hashDoorPan.put(rec.trigRec.tgPanelID, High(TriggersTable));
1708 if (pttit.shotPan <> nil) then hashShotPan.put(rec.trigRec.tgShotPanelID, High(TriggersTable));
1710 g_Game_StepLoading();
1711 end;
1712 end;
1714 // Ñîçäàåì ïàíåëè
1715 if (panels <> nil) and (panels.count > 0) then
1716 begin
1717 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1718 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], panels.count-1, False);
1720 pannum := -1;
1721 for rec in panels do
1722 begin
1723 Inc(pannum);
1724 texrec := nil;
1725 SetLength(AddTextures, 0);
1726 trigRef := False;
1727 CurTex := -1;
1728 ok := false;
1730 if (_textures <> nil) then
1731 begin
1732 texrec := rec.TextureRec;
1733 ok := (texrec <> nil);
1734 end;
1736 if ok then
1737 begin
1738 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1739 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð
1740 ok := false;
1741 if (TriggersTable <> nil) and (_textures <> nil) then
1742 begin
1743 for b := 0 to High(TriggersTable) do
1744 begin
1745 if (TriggersTable[b].texPan = rec) or (TriggersTable[b].shotPan = rec) then
1746 begin
1747 trigRef := True;
1748 ok := True;
1749 break;
1750 end;
1751 end;
1752 end;
1753 end;
1755 if ok then
1756 begin
1757 // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1758 s := texrec.Resource;
1760 // Ñïåö-òåêñòóðû çàïðåùåíû
1761 if g_Map_IsSpecialTexture(s) then
1762 begin
1763 ok := false
1764 end
1765 else
1766 begin
1767 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè
1768 ok := g_Texture_NumNameFindStart(s);
1769 end;
1771 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1772 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #
1773 if ok then
1774 begin
1775 k := NNF_NAME_BEFORE;
1776 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû
1777 while ok or (k = NNF_NAME_BEFORE) or (k = NNF_NAME_EQUALS) do
1778 begin
1779 k := g_Texture_NumNameFindNext(TexName);
1781 if (k = NNF_NAME_BEFORE) or (k = NNF_NAME_AFTER) then
1782 begin
1783 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó
1784 if texrec.Anim then
1785 begin
1786 // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1787 isAnim := True;
1788 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1789 if not ok then
1790 begin
1791 // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1792 isAnim := False;
1793 ok := CreateTexture(TexName, FileName, False) >= 0;
1794 end;
1795 end
1796 else
1797 begin
1798 // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1799 isAnim := False;
1800 ok := CreateTexture(TexName, FileName, False) >= 0;
1801 if not ok then
1802 begin
1803 // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1804 isAnim := True;
1805 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1806 end;
1807 end;
1809 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè
1810 if ok then
1811 begin
1812 for c := 0 to High(Textures) do
1813 begin
1814 if (Textures[c].TextureName = TexName) then
1815 begin
1816 SetLength(AddTextures, Length(AddTextures)+1);
1817 AddTextures[High(AddTextures)].Texture := c;
1818 AddTextures[High(AddTextures)].Anim := isAnim;
1819 break;
1820 end;
1821 end;
1822 end;
1823 end
1824 else
1825 begin
1826 if k = NNF_NAME_EQUALS then
1827 begin
1828 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî
1829 SetLength(AddTextures, Length(AddTextures)+1);
1830 //AddTextures[High(AddTextures)].Texture := _texnummap[rec.TextureNum]; //panels[a].TextureNum;
1831 AddTextures[High(AddTextures)].Texture := rec.tagInt; //panels[a].TextureNum;
1832 AddTextures[High(AddTextures)].Anim := texrec.Anim;
1833 CurTex := High(AddTextures);
1834 ok := true;
1835 end
1836 else // NNF_NO_NAME
1837 begin
1838 ok := false;
1839 end;
1840 end;
1841 end; // while ok...
1843 ok := true;
1844 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1845 end; // if ok - ññûëàþòñÿ òðèããåðû
1847 if not ok then
1848 begin
1849 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó
1850 SetLength(AddTextures, 1);
1851 AddTextures[0].Texture := rec.tagInt; //panels[a].TextureNum;
1852 AddTextures[0].Anim := false;
1853 if (texrec <> nil) then AddTextures[0].Anim := texrec.Anim;
1854 CurTex := 0;
1855 end;
1857 //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);
1859 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð
1860 PanelID := CreatePanel(rec, AddTextures, CurTex, trigRef);
1861 //e_LogWritefln('panel #%s of type %s got id #%s', [pannum, rec.PanelType, PanelID]);
1863 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID
1864 if hashTextPan.get(pannum, b) then TriggersTable[b].texPanIdx := PanelID;
1865 if hashLiftPan.get(pannum, b) then TriggersTable[b].LiftPanelIdx := PanelID;
1866 if hashDoorPan.get(pannum, b) then TriggersTable[b].DoorPanelIdx := PanelID;
1867 if hashShotPan.get(pannum, b) then TriggersTable[b].ShotPanelIdx := PanelID;
1869 (*
1870 if (TriggersTable <> nil) then
1871 begin
1872 for b := 0 to High(TriggersTable) do
1873 begin
1874 if (TriggersTable[b].texPan = rec) then TriggersTable[b].texPanIdx := PanelID;
1875 if (TriggersTable[b].liftPan = rec) then TriggersTable[b].LiftPanelIdx := PanelID;
1876 if (TriggersTable[b].doorPan = rec) then TriggersTable[b].DoorPanelIdx := PanelID;
1877 if (TriggersTable[b].shotPan = rec) then TriggersTable[b].ShotPanelIdx := PanelID;
1879 // Òðèããåð äâåðè/ëèôòà
1880 if (TriggersTable[b].LiftPanel = pannum) or
1881 (TriggersTable[b].DoorPanel = pannum) then
1882 //TTriggerData(TriggersTable[b].trigrec.DATA).PanelID := PanelID;
1883 TriggersTable[b].trigrec.trigRec.trigPanelID := PanelID;
1884 // Òðèããåð ñìåíû òåêñòóðû
1885 if TriggersTable[b].texPanIdx = pannum then
1886 TriggersTable[b].trigrec.TexturePanel := PanelID;
1887 // Òðèããåð "Òóðåëü"
1888 if TriggersTable[b].ShotPanel = pannum then
1889 TriggersTable[b].trigrec.trigRec.trigShotPanelID := PanelID;
1891 end;
1892 end;
1893 *)
1895 g_Game_StepLoading();
1896 end;
1897 end;
1899 (*
1900 begin
1901 for b := 0 to High(TriggersTable) do
1902 begin
1903 // Òðèããåð äâåðè/ëèôòà
1904 if (TriggersTable[b].texPan <> nil) then e_LogWritefln('trigger #%s: textPan=%s; panidx=%s', [b, TriggersTable[b].texPanIdx, mapReader.panelIndex[TriggersTable[b].texPan]]);
1905 if (TriggersTable[b].liftPan <> nil) then e_LogWritefln('trigger #%s: liftPan=%s; panidx=%s', [b, TriggersTable[b].LiftPanelIdx, mapReader.panelIndex[TriggersTable[b].liftPan]]);
1906 if (TriggersTable[b].doorPan <> nil) then e_LogWritefln('trigger #%s: doorPan=%s; panidx=%s', [b, TriggersTable[b].DoorPanelIdx, mapReader.panelIndex[TriggersTable[b].doorPan]]);
1907 if (TriggersTable[b].shotPan <> nil) then e_LogWritefln('trigger #%s: shotPan=%s; panidx=%s', [b, TriggersTable[b].ShotPanelIdx, mapReader.panelIndex[TriggersTable[b].shotPan]]);
1908 end;
1909 end;
1910 *)
1912 // create map grid, init other grids (for monsters, for example)
1913 e_WriteLog('Creating map grid', MSG_NOTIFY);
1914 mapCreateGrid();
1916 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû
1917 if (triggers <> nil) and (panels <> nil) and (not gLoadGameMode) then
1918 begin
1919 e_LogWritefln(' Creating triggers (%d)...', [triggers.count]);
1920 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1921 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü
1922 trignum := -1;
1923 for rec in triggers do
1924 begin
1925 Inc(trignum);
1926 if (TriggersTable[trignum].texPan <> nil) then b := TriggersTable[trignum].texPan.PanelType else b := 0;
1927 if (TriggersTable[trignum].shotPan <> nil) then c := TriggersTable[trignum].shotPan.PanelType else c := 0;
1928 (*
1929 if (rec.TexturePanel <> -1) then
1930 begin
1932 if (TriggersTable[trignum].TexturePanel < 0) or (TriggersTable[trignum].TexturePanel >= panels.count) then
1933 begin
1934 e_WriteLog('error loading map: invalid panel index for trigger', MSG_FATALERROR);
1935 result := false;
1936 exit;
1937 end;
1939 //b := panels[TriggersTable[a].TexturePanel].PanelType;
1940 //b := mapReader.panel[TriggersTable[trignum].texPanIdx].PanelType;
1941 assert(TriggersTable[trignum].texPanIdx >= 0);
1942 b := TriggersTable[trignum].texPanIdx;
1943 end
1944 else
1945 begin
1946 b := 0;
1947 end;
1948 e_LogWritefln('trigger #%s: type=%s; texPanIdx=%s; b=%s', [trignum, rec.TriggerType, TriggersTable[trignum].texPanIdx, b]);
1949 if (rec.TriggerType = TRIGGER_SHOT) then e_LogWritefln(' SHOT: shotpanidx=%s', [rec.trigRec.trigShotPanelID]);
1950 if (rec.TriggerType = TRIGGER_SHOT) and {(rec.trigRec.trigShotPanelID <> -1)} (TriggersTable[trignum].shotPan <> nil) then
1951 begin
1952 //c := panels[TriggersTable[a].ShotPanel].PanelType;
1953 //c := mapReader.panel[TriggersTable[trignum].ShotPanel].PanelType;
1954 assert(TriggersTable[trignum].ShotPanelIdx >= 0);
1955 c := TriggersTable[trignum].ShotPanelIdx;
1956 end
1957 else
1958 begin
1959 c := 0;
1960 end;
1961 *)
1962 //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]);
1963 CreateTrigger(rec, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx, Word(b), Word(c));
1964 end;
1965 end;
1967 // Çàãðóçêà ïðåäìåòîâ
1968 e_WriteLog(' Loading items...', MSG_NOTIFY);
1969 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1971 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû
1972 if (items <> nil) and not gLoadGameMode then
1973 begin
1974 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1975 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1976 for rec in items do CreateItem(rec);
1977 end;
1979 // Çàãðóçêà îáëàñòåé
1980 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1981 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1983 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè
1984 if areas <> nil then
1985 begin
1986 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1987 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1988 for rec in areas do CreateArea(rec);
1989 end;
1991 // Çàãðóçêà ìîíñòðîâ
1992 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1993 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1995 gTotalMonsters := 0;
1997 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ
1998 if (monsters <> nil) and not gLoadGameMode then
1999 begin
2000 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
2001 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
2002 for rec in monsters do CreateMonster(rec);
2003 end;
2005 //MapReader.Free();
2006 gCurrentMap := mapReader;
2007 mapReader := nil;
2009 // Çàãðóçêà íåáà
2010 if gMapInfo.SkyName <> '' then
2011 begin
2012 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
2013 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
2014 FileName := g_ExtractWadName(gMapInfo.SkyName);
2016 if FileName <> '' then
2017 FileName := GameDir+'/wads/'+FileName
2018 else
2019 begin
2020 FileName := g_ExtractWadName(Res);
2021 end;
2023 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
2024 if g_Texture_CreateWAD(BackID, s) then
2025 begin
2026 g_Game_SetupScreenSize();
2027 end
2028 else
2029 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
2030 end;
2032 // Çàãðóçêà ìóçûêè
2033 ok := False;
2034 if gMapInfo.MusicName <> '' then
2035 begin
2036 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
2037 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
2038 FileName := g_ExtractWadName(gMapInfo.MusicName);
2040 if FileName <> '' then
2041 FileName := GameDir+'/wads/'+FileName
2042 else
2043 begin
2044 FileName := g_ExtractWadName(Res);
2045 end;
2047 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
2048 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
2049 ok := True
2050 else
2051 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
2052 end;
2054 // Îñòàëüíûå óñòàíâêè
2055 CreateDoorMap();
2056 CreateLiftMap();
2058 g_Items_Init();
2059 g_Weapon_Init();
2060 g_Monsters_Init();
2062 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
2063 if not gLoadGameMode then g_GFX_Init();
2065 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
2066 _textures := nil;
2067 panels := nil;
2068 items := nil;
2069 areas := nil;
2070 triggers := nil;
2071 TriggersTable := nil;
2072 AddTextures := nil;
2074 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
2075 if ok and (not gLoadGameMode) then
2076 begin
2077 gMusic.SetByName(gMapInfo.MusicName);
2078 gMusic.Play();
2079 end
2080 else
2081 begin
2082 gMusic.SetByName('');
2083 end;
2084 finally
2085 sfsGCEnable(); // enable releasing unused volumes
2086 mapReader.Free();
2087 hashTextPan.Free();
2088 hashLiftPan.Free();
2089 hashDoorPan.Free();
2090 hashShotPan.Free();
2091 end;
2093 e_WriteLog('Done loading map.', MSG_NOTIFY);
2094 Result := True;
2095 end;
2097 function g_Map_GetMapInfo(Res: String): TMapInfo;
2098 var
2099 WAD: TWADFile;
2100 MapReader: TDynRecord;
2101 //Header: TMapHeaderRec_1;
2102 FileName: String;
2103 Data: Pointer;
2104 Len: Integer;
2105 begin
2106 FillChar(Result, SizeOf(Result), 0);
2107 FileName := g_ExtractWadName(Res);
2109 WAD := TWADFile.Create();
2110 if not WAD.ReadFile(FileName) then
2111 begin
2112 WAD.Free();
2113 Exit;
2114 end;
2116 //k8: it ignores path again
2117 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
2118 begin
2119 WAD.Free();
2120 Exit;
2121 end;
2123 WAD.Free();
2126 MapReader := TMapReader_1.Create();
2127 if not MapReader.LoadMap(Data) then
2128 begin
2129 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2130 ZeroMemory(@Header, SizeOf(Header));
2131 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2132 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2133 end
2134 else
2135 begin
2136 Header := MapReader.GetMapHeader();
2137 Result.Name := Header.MapName;
2138 Result.Description := Header.MapDescription;
2139 end;
2141 try
2142 mapReader := g_Map_ParseMap(Data, Len);
2143 except
2144 mapReader := nil;
2145 end;
2147 FreeMem(Data);
2148 //MapReader.Free();
2150 //if (mapReader <> nil) then Header := GetMapHeader(mapReader) else FillChar(Header, sizeof(Header), 0);
2151 //MapReader.Free();
2153 if (mapReader.Width > 0) and (mapReader.Height > 0) then
2154 begin
2155 Result.Name := mapReader.MapName;
2156 Result.Description := mapReader.MapDesc;
2157 Result.Map := Res;
2158 Result.Author := mapReader.MapAuthor;
2159 Result.Height := mapReader.Height;
2160 Result.Width := mapReader.Width;
2161 end
2162 else
2163 begin
2164 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2165 //ZeroMemory(@Header, SizeOf(Header));
2166 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2167 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2168 Result.Map := Res;
2169 Result.Author := '';
2170 Result.Height := 0;
2171 Result.Width := 0;
2172 end;
2174 mapReader.Free();
2175 end;
2177 function g_Map_GetMapsList(WADName: string): SArray;
2178 var
2179 WAD: TWADFile;
2180 a: Integer;
2181 ResList: SArray;
2182 begin
2183 Result := nil;
2184 WAD := TWADFile.Create();
2185 if not WAD.ReadFile(WADName) then
2186 begin
2187 WAD.Free();
2188 Exit;
2189 end;
2190 ResList := WAD.GetMapResources();
2191 if ResList <> nil then
2192 begin
2193 for a := 0 to High(ResList) do
2194 begin
2195 SetLength(Result, Length(Result)+1);
2196 Result[High(Result)] := ResList[a];
2197 end;
2198 end;
2199 WAD.Free();
2200 end;
2202 function g_Map_Exist(Res: string): Boolean;
2203 var
2204 WAD: TWADFile;
2205 FileName, mnn: string;
2206 ResList: SArray;
2207 a: Integer;
2208 begin
2209 Result := False;
2211 FileName := addWadExtension(g_ExtractWadName(Res));
2213 WAD := TWADFile.Create;
2214 if not WAD.ReadFile(FileName) then
2215 begin
2216 WAD.Free();
2217 Exit;
2218 end;
2220 ResList := WAD.GetMapResources();
2221 WAD.Free();
2223 mnn := g_ExtractFileName(Res);
2224 if ResList <> nil then
2225 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
2226 begin
2227 Result := True;
2228 Exit;
2229 end;
2230 end;
2232 procedure g_Map_Free();
2233 var
2234 a: Integer;
2236 procedure FreePanelArray(var panels: TPanelArray);
2237 var
2238 i: Integer;
2240 begin
2241 if panels <> nil then
2242 begin
2243 for i := 0 to High(panels) do
2244 panels[i].Free();
2245 panels := nil;
2246 end;
2247 end;
2249 begin
2250 g_GFX_Free();
2251 g_Weapon_Free();
2252 g_Items_Free();
2253 g_Triggers_Free();
2254 g_Monsters_Free();
2256 RespawnPoints := nil;
2257 if FlagPoints[FLAG_RED] <> nil then
2258 begin
2259 Dispose(FlagPoints[FLAG_RED]);
2260 FlagPoints[FLAG_RED] := nil;
2261 end;
2262 if FlagPoints[FLAG_BLUE] <> nil then
2263 begin
2264 Dispose(FlagPoints[FLAG_BLUE]);
2265 FlagPoints[FLAG_BLUE] := nil;
2266 end;
2267 //DOMFlagPoints := nil;
2269 //gDOMFlags := nil;
2271 if Textures <> nil then
2272 begin
2273 for a := 0 to High(Textures) do
2274 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
2275 if Textures[a].Anim then
2276 g_Frames_DeleteByID(Textures[a].FramesID)
2277 else
2278 if Textures[a].TextureID <> LongWord(TEXTURE_NONE) then
2279 e_DeleteTexture(Textures[a].TextureID);
2281 Textures := nil;
2282 end;
2284 FreePanelArray(gWalls);
2285 FreePanelArray(gRenderBackgrounds);
2286 FreePanelArray(gRenderForegrounds);
2287 FreePanelArray(gWater);
2288 FreePanelArray(gAcid1);
2289 FreePanelArray(gAcid2);
2290 FreePanelArray(gSteps);
2291 FreePanelArray(gLifts);
2292 FreePanelArray(gBlockMon);
2294 if BackID <> DWORD(-1) then
2295 begin
2296 gBackSize.X := 0;
2297 gBackSize.Y := 0;
2298 e_DeleteTexture(BackID);
2299 BackID := DWORD(-1);
2300 end;
2302 g_Game_StopAllSounds(False);
2303 gMusic.FreeSound();
2304 g_Sound_Delete(gMapInfo.MusicName);
2306 gMapInfo.Name := '';
2307 gMapInfo.Description := '';
2308 gMapInfo.MusicName := '';
2309 gMapInfo.Height := 0;
2310 gMapInfo.Width := 0;
2312 gDoorMap := nil;
2313 gLiftMap := nil;
2315 PanelByID := nil;
2316 end;
2318 procedure g_Map_Update();
2319 var
2320 a, d, j: Integer;
2321 m: Word;
2322 s: String;
2324 procedure UpdatePanelArray(var panels: TPanelArray);
2325 var
2326 i: Integer;
2328 begin
2329 if panels <> nil then
2330 for i := 0 to High(panels) do
2331 panels[i].Update();
2332 end;
2334 begin
2335 UpdatePanelArray(gWalls);
2336 UpdatePanelArray(gRenderBackgrounds);
2337 UpdatePanelArray(gRenderForegrounds);
2338 UpdatePanelArray(gWater);
2339 UpdatePanelArray(gAcid1);
2340 UpdatePanelArray(gAcid2);
2341 UpdatePanelArray(gSteps);
2343 if gGameSettings.GameMode = GM_CTF then
2344 begin
2345 for a := FLAG_RED to FLAG_BLUE do
2346 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2347 with gFlags[a] do
2348 begin
2349 if gFlags[a].Animation <> nil then
2350 gFlags[a].Animation.Update();
2352 m := g_Obj_Move(@Obj, True, True);
2354 if gTime mod (GAME_TICK*2) <> 0 then
2355 Continue;
2357 // Ñîïðîòèâëåíèå âîçäóõà:
2358 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2360 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
2361 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2362 begin
2363 g_Map_ResetFlag(a);
2364 gFlags[a].CaptureTime := 0;
2365 if a = FLAG_RED then
2366 s := _lc[I_PLAYER_FLAG_RED]
2367 else
2368 s := _lc[I_PLAYER_FLAG_BLUE];
2369 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2371 if g_Game_IsNet then
2372 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2373 Continue;
2374 end;
2376 if Count > 0 then
2377 Count := Count - 1;
2379 // Èãðîê áåðåò ôëàã:
2380 if gPlayers <> nil then
2381 begin
2382 j := Random(Length(gPlayers)) - 1;
2384 for d := 0 to High(gPlayers) do
2385 begin
2386 Inc(j);
2387 if j > High(gPlayers) then
2388 j := 0;
2390 if gPlayers[j] <> nil then
2391 if gPlayers[j].Live and
2392 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2393 begin
2394 if gPlayers[j].GetFlag(a) then
2395 Break;
2396 end;
2397 end;
2398 end;
2399 end;
2400 end;
2401 end;
2404 // old algo
2405 procedure g_Map_DrawPanels (PanelType: Word);
2407 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2408 var
2409 idx: Integer;
2410 begin
2411 if (panels <> nil) then
2412 begin
2413 // alas, no visible set
2414 for idx := 0 to High(panels) do
2415 begin
2416 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
2417 end;
2418 end;
2419 end;
2421 begin
2422 case PanelType of
2423 PANEL_WALL: DrawPanels(gWalls);
2424 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2425 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2426 PANEL_FORE: DrawPanels(gRenderForegrounds);
2427 PANEL_WATER: DrawPanels(gWater);
2428 PANEL_ACID1: DrawPanels(gAcid1);
2429 PANEL_ACID2: DrawPanels(gAcid2);
2430 PANEL_STEP: DrawPanels(gSteps);
2431 end;
2432 end;
2435 // new algo
2436 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2438 function checker (pan: TPanel; tag: Integer): Boolean;
2439 begin
2440 result := false; // don't stop, ever
2441 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2442 gDrawPanelList.insert(pan);
2443 end;
2445 begin
2446 dplClear();
2447 //tagmask := panelTypeToTag(PanelType);
2448 mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask);
2449 // list will be rendered in `g_game.DrawPlayer()`
2450 end;
2453 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2455 function checker (pan: TPanel; tag: Integer): Boolean;
2456 begin
2457 result := false; // don't stop, ever
2458 pan.DrawShadowVolume(lightX, lightY, radius);
2459 end;
2461 begin
2462 mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2463 end;
2466 procedure g_Map_DrawBack(dx, dy: Integer);
2467 begin
2468 if gDrawBackGround and (BackID <> DWORD(-1)) then
2469 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2470 else
2471 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2472 end;
2474 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2475 PanelType: Word; b1x3: Boolean=false): Boolean;
2476 var
2477 a, h: Integer;
2478 begin
2479 Result := False;
2481 if WordBool(PanelType and PANEL_WALL) then
2482 if gWalls <> nil then
2483 begin
2484 h := High(gWalls);
2486 for a := 0 to h do
2487 if gWalls[a].Enabled and
2488 g_Collide(X, Y, Width, Height,
2489 gWalls[a].X, gWalls[a].Y,
2490 gWalls[a].Width, gWalls[a].Height) then
2491 begin
2492 Result := True;
2493 Exit;
2494 end;
2495 end;
2497 if WordBool(PanelType and PANEL_WATER) then
2498 if gWater <> nil then
2499 begin
2500 h := High(gWater);
2502 for a := 0 to h do
2503 if g_Collide(X, Y, Width, Height,
2504 gWater[a].X, gWater[a].Y,
2505 gWater[a].Width, gWater[a].Height) then
2506 begin
2507 Result := True;
2508 Exit;
2509 end;
2510 end;
2512 if WordBool(PanelType and PANEL_ACID1) then
2513 if gAcid1 <> nil then
2514 begin
2515 h := High(gAcid1);
2517 for a := 0 to h do
2518 if g_Collide(X, Y, Width, Height,
2519 gAcid1[a].X, gAcid1[a].Y,
2520 gAcid1[a].Width, gAcid1[a].Height) then
2521 begin
2522 Result := True;
2523 Exit;
2524 end;
2525 end;
2527 if WordBool(PanelType and PANEL_ACID2) then
2528 if gAcid2 <> nil then
2529 begin
2530 h := High(gAcid2);
2532 for a := 0 to h do
2533 if g_Collide(X, Y, Width, Height,
2534 gAcid2[a].X, gAcid2[a].Y,
2535 gAcid2[a].Width, gAcid2[a].Height) then
2536 begin
2537 Result := True;
2538 Exit;
2539 end;
2540 end;
2542 if WordBool(PanelType and PANEL_STEP) then
2543 if gSteps <> nil then
2544 begin
2545 h := High(gSteps);
2547 for a := 0 to h do
2548 if g_Collide(X, Y, Width, Height,
2549 gSteps[a].X, gSteps[a].Y,
2550 gSteps[a].Width, gSteps[a].Height) then
2551 begin
2552 Result := True;
2553 Exit;
2554 end;
2555 end;
2557 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2558 if gLifts <> nil then
2559 begin
2560 h := High(gLifts);
2562 for a := 0 to h do
2563 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2564 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2565 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2566 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2567 g_Collide(X, Y, Width, Height,
2568 gLifts[a].X, gLifts[a].Y,
2569 gLifts[a].Width, gLifts[a].Height) then
2570 begin
2571 Result := True;
2572 Exit;
2573 end;
2574 end;
2576 if WordBool(PanelType and PANEL_BLOCKMON) then
2577 if gBlockMon <> nil then
2578 begin
2579 h := High(gBlockMon);
2581 for a := 0 to h do
2582 if ( (not b1x3) or
2583 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2584 g_Collide(X, Y, Width, Height,
2585 gBlockMon[a].X, gBlockMon[a].Y,
2586 gBlockMon[a].Width, gBlockMon[a].Height) then
2587 begin
2588 Result := True;
2589 Exit;
2590 end;
2591 end;
2592 end;
2594 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2595 var
2596 texid: DWORD;
2598 function checkPanels (constref panels: TPanelArray): Boolean;
2599 var
2600 a: Integer;
2601 begin
2602 result := false;
2603 if panels = nil then exit;
2604 for a := 0 to High(panels) do
2605 begin
2606 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2607 begin
2608 result := true;
2609 texid := panels[a].GetTextureID();
2610 exit;
2611 end;
2612 end;
2613 end;
2615 begin
2616 texid := LongWord(TEXTURE_NONE);
2617 result := texid;
2618 if not checkPanels(gWater) then
2619 if not checkPanels(gAcid1) then
2620 if not checkPanels(gAcid2) then exit;
2621 result := texid;
2622 end;
2625 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2626 const
2627 SlowMask = GridTagLift or GridTagBlockMon;
2628 function checker (pan: TPanel; tag: Integer): Boolean;
2629 begin
2631 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2632 begin
2633 result := pan.Enabled;
2634 exit;
2635 end;
2638 if ((tag and GridTagLift) <> 0) then
2639 begin
2640 result :=
2641 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2642 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2643 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2644 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
2645 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
2646 exit;
2647 end;
2649 if ((tag and GridTagBlockMon) <> 0) then
2650 begin
2651 result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2652 exit;
2653 end;
2655 // other shit
2656 //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2657 result := true; // i found her!
2658 end;
2660 var
2661 tagmask: Integer = 0;
2662 begin
2663 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2664 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2665 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2666 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2667 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2668 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2669 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2671 if (tagmask = 0) then begin result := false; exit; end; // just in case
2673 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2674 if gdbg_map_use_accel_coldet then
2675 begin
2676 if (Width = 1) and (Height = 1) then
2677 begin
2678 if ((tagmask and SlowMask) <> 0) then
2679 begin
2680 // slow
2681 result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil);
2682 end
2683 else
2684 begin
2685 // fast
2686 result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil);
2687 end;
2688 end
2689 else
2690 begin
2691 if ((tagmask and SlowMask) <> 0) then
2692 begin
2693 // slow
2694 result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil);
2695 end
2696 else
2697 begin
2698 // fast
2699 result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil);
2700 end;
2701 end;
2702 end
2703 else
2704 begin
2705 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2706 end;
2707 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2708 end;
2711 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2712 var
2713 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2714 texid: DWORD;
2716 // slightly different from the old code, but meh...
2717 function checker (pan: TPanel; tag: Integer): Boolean;
2718 begin
2719 result := false; // don't stop, ever
2720 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2721 // check priorities
2722 case cctype of
2723 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2724 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2725 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2726 end;
2727 // collision?
2728 //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2729 // yeah
2730 texid := pan.GetTextureID();
2731 // water? water has the highest priority, so stop right here
2732 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2733 // acid2?
2734 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2735 // acid1?
2736 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2737 end;
2739 begin
2740 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2741 if gdbg_map_use_accel_coldet then
2742 begin
2743 texid := LongWord(TEXTURE_NONE);
2744 if (Width = 1) and (Height = 1) then
2745 begin
2746 mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2747 end
2748 else
2749 begin
2750 mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2751 end;
2752 result := texid;
2753 end
2754 else
2755 begin
2756 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2757 end;
2758 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2759 end;
2762 procedure g_Map_EnableWall(ID: DWORD);
2763 var
2764 pan: TPanel;
2765 begin
2766 pan := gWalls[ID];
2767 pan.Enabled := True;
2768 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, True);
2770 mapGrid.proxyEnabled[pan.proxyId] := true;
2771 //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
2772 //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
2774 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(gWalls[ID].PanelType, ID);
2776 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2777 //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);
2778 {$ENDIF}
2779 end;
2781 procedure g_Map_DisableWall(ID: DWORD);
2782 var
2783 pan: TPanel;
2784 begin
2785 pan := gWalls[ID];
2786 pan.Enabled := False;
2787 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, False);
2789 mapGrid.proxyEnabled[pan.proxyId] := false;
2790 //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
2792 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, ID);
2794 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2795 //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);
2796 {$ENDIF}
2797 end;
2799 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2800 var
2801 tp: TPanel;
2802 begin
2803 case PanelType of
2804 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2805 tp := gWalls[ID];
2806 PANEL_FORE:
2807 tp := gRenderForegrounds[ID];
2808 PANEL_BACK:
2809 tp := gRenderBackgrounds[ID];
2810 PANEL_WATER:
2811 tp := gWater[ID];
2812 PANEL_ACID1:
2813 tp := gAcid1[ID];
2814 PANEL_ACID2:
2815 tp := gAcid2[ID];
2816 PANEL_STEP:
2817 tp := gSteps[ID];
2818 else
2819 Exit;
2820 end;
2822 tp.NextTexture(AnimLoop);
2823 if g_Game_IsServer and g_Game_IsNet then
2824 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2825 end;
2827 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2828 begin
2829 if gLifts[ID].LiftType = t then
2830 Exit;
2832 with gLifts[ID] do
2833 begin
2834 LiftType := t;
2836 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2837 //TODO: make separate lift tags, and change tag here
2839 if LiftType = 0 then
2840 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2841 else if LiftType = 1 then
2842 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2843 else if LiftType = 2 then
2844 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2845 else if LiftType = 3 then
2846 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2848 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2849 end;
2850 end;
2852 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2853 var
2854 a: Integer;
2855 PointsArray: Array of TRespawnPoint;
2856 begin
2857 Result := False;
2858 SetLength(PointsArray, 0);
2860 if RespawnPoints = nil then
2861 Exit;
2863 for a := 0 to High(RespawnPoints) do
2864 if RespawnPoints[a].PointType = PointType then
2865 begin
2866 SetLength(PointsArray, Length(PointsArray)+1);
2867 PointsArray[High(PointsArray)] := RespawnPoints[a];
2868 end;
2870 if PointsArray = nil then
2871 Exit;
2873 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2874 Result := True;
2875 end;
2877 function g_Map_GetPointCount(PointType: Byte): Word;
2878 var
2879 a: Integer;
2880 begin
2881 Result := 0;
2883 if RespawnPoints = nil then
2884 Exit;
2886 for a := 0 to High(RespawnPoints) do
2887 if RespawnPoints[a].PointType = PointType then
2888 Result := Result + 1;
2889 end;
2891 function g_Map_HaveFlagPoints(): Boolean;
2892 begin
2893 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2894 end;
2896 procedure g_Map_ResetFlag(Flag: Byte);
2897 begin
2898 with gFlags[Flag] do
2899 begin
2900 Obj.X := -1000;
2901 Obj.Y := -1000;
2902 Obj.Vel.X := 0;
2903 Obj.Vel.Y := 0;
2904 Direction := D_LEFT;
2905 State := FLAG_STATE_NONE;
2906 if FlagPoints[Flag] <> nil then
2907 begin
2908 Obj.X := FlagPoints[Flag]^.X;
2909 Obj.Y := FlagPoints[Flag]^.Y;
2910 Direction := FlagPoints[Flag]^.Direction;
2911 State := FLAG_STATE_NORMAL;
2912 end;
2913 Count := -1;
2914 end;
2915 end;
2917 procedure g_Map_DrawFlags();
2918 var
2919 i, dx: Integer;
2920 Mirror: TMirrorType;
2921 begin
2922 if gGameSettings.GameMode <> GM_CTF then
2923 Exit;
2925 for i := FLAG_RED to FLAG_BLUE do
2926 with gFlags[i] do
2927 if State <> FLAG_STATE_CAPTURED then
2928 begin
2929 if State = FLAG_STATE_NONE then
2930 continue;
2932 if Direction = D_LEFT then
2933 begin
2934 Mirror := M_HORIZONTAL;
2935 dx := -1;
2936 end
2937 else
2938 begin
2939 Mirror := M_NONE;
2940 dx := 1;
2941 end;
2943 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2945 if g_debug_Frames then
2946 begin
2947 e_DrawQuad(Obj.X+Obj.Rect.X,
2948 Obj.Y+Obj.Rect.Y,
2949 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2950 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2951 0, 255, 0);
2952 end;
2953 end;
2954 end;
2956 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2957 var
2958 dw: DWORD;
2959 b: Byte;
2960 str: String;
2961 boo: Boolean;
2963 procedure SavePanelArray(var panels: TPanelArray);
2964 var
2965 PAMem: TBinMemoryWriter;
2966 i: Integer;
2967 begin
2968 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2969 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2971 i := 0;
2972 while i < Length(panels) do
2973 begin
2974 if panels[i].SaveIt then
2975 begin
2976 // ID ïàíåëè:
2977 PAMem.WriteInt(i);
2978 // Ñîõðàíÿåì ïàíåëü:
2979 panels[i].SaveState(PAMem);
2980 end;
2981 Inc(i);
2982 end;
2984 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2985 PAMem.SaveToMemory(Mem);
2986 PAMem.Free();
2987 end;
2989 procedure SaveFlag(flag: PFlag);
2990 begin
2991 // Ñèãíàòóðà ôëàãà:
2992 dw := FLAG_SIGNATURE; // 'FLAG'
2993 Mem.WriteDWORD(dw);
2994 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2995 Mem.WriteByte(flag^.RespawnType);
2996 // Ñîñòîÿíèå ôëàãà:
2997 Mem.WriteByte(flag^.State);
2998 // Íàïðàâëåíèå ôëàãà:
2999 if flag^.Direction = D_LEFT then
3000 b := 1
3001 else // D_RIGHT
3002 b := 2;
3003 Mem.WriteByte(b);
3004 // Îáúåêò ôëàãà:
3005 Obj_SaveState(@flag^.Obj, Mem);
3006 end;
3008 begin
3009 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
3011 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
3012 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
3013 SavePanelArray(gWalls);
3014 // Ñîõðàíÿåì ïàíåëè ôîíà:
3015 SavePanelArray(gRenderBackgrounds);
3016 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
3017 SavePanelArray(gRenderForegrounds);
3018 // Ñîõðàíÿåì ïàíåëè âîäû:
3019 SavePanelArray(gWater);
3020 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
3021 SavePanelArray(gAcid1);
3022 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
3023 SavePanelArray(gAcid2);
3024 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
3025 SavePanelArray(gSteps);
3026 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
3027 SavePanelArray(gLifts);
3028 ///// /////
3030 ///// Ñîõðàíÿåì ìóçûêó: /////
3031 // Ñèãíàòóðà ìóçûêè:
3032 dw := MUSIC_SIGNATURE; // 'MUSI'
3033 Mem.WriteDWORD(dw);
3034 // Íàçâàíèå ìóçûêè:
3035 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
3036 if gMusic.NoMusic then
3037 str := ''
3038 else
3039 str := gMusic.Name;
3040 Mem.WriteString(str, 64);
3041 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
3042 dw := gMusic.GetPosition();
3043 Mem.WriteDWORD(dw);
3044 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
3045 boo := gMusic.SpecPause;
3046 Mem.WriteBoolean(boo);
3047 ///// /////
3049 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
3050 Mem.WriteInt(gTotalMonsters);
3051 ///// /////
3053 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
3054 if gGameSettings.GameMode = GM_CTF then
3055 begin
3056 // Ôëàã Êðàñíîé êîìàíäû:
3057 SaveFlag(@gFlags[FLAG_RED]);
3058 // Ôëàã Ñèíåé êîìàíäû:
3059 SaveFlag(@gFlags[FLAG_BLUE]);
3060 end;
3061 ///// /////
3063 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3064 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3065 begin
3066 // Î÷êè Êðàñíîé êîìàíäû:
3067 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
3068 // Î÷êè Ñèíåé êîìàíäû:
3069 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
3070 end;
3071 ///// /////
3072 end;
3074 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
3075 var
3076 dw: DWORD;
3077 b: Byte;
3078 str: String;
3079 boo: Boolean;
3081 procedure LoadPanelArray(var panels: TPanelArray);
3082 var
3083 PAMem: TBinMemoryReader;
3084 i, id: Integer;
3085 begin
3086 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
3087 PAMem := TBinMemoryReader.Create();
3088 PAMem.LoadFromMemory(Mem);
3090 for i := 0 to Length(panels)-1 do
3091 begin
3092 if panels[i].SaveIt then
3093 begin
3094 // ID ïàíåëè:
3095 PAMem.ReadInt(id);
3096 if id <> i then
3097 begin
3098 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
3099 end;
3100 // Çàãðóæàåì ïàíåëü:
3101 panels[i].LoadState(PAMem);
3102 if (panels[i].arrIdx <> i) then raise Exception.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel arrIdx');
3103 if (panels[i].proxyId >= 0) then mapGrid.proxyEnabled[panels[i].proxyId] := panels[i].Enabled;
3104 end;
3105 end;
3107 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
3108 PAMem.Free();
3109 end;
3111 procedure LoadFlag(flag: PFlag);
3112 begin
3113 // Ñèãíàòóðà ôëàãà:
3114 Mem.ReadDWORD(dw);
3115 if dw <> FLAG_SIGNATURE then // 'FLAG'
3116 begin
3117 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
3118 end;
3119 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
3120 Mem.ReadByte(flag^.RespawnType);
3121 // Ñîñòîÿíèå ôëàãà:
3122 Mem.ReadByte(flag^.State);
3123 // Íàïðàâëåíèå ôëàãà:
3124 Mem.ReadByte(b);
3125 if b = 1 then
3126 flag^.Direction := D_LEFT
3127 else // b = 2
3128 flag^.Direction := D_RIGHT;
3129 // Îáúåêò ôëàãà:
3130 Obj_LoadState(@flag^.Obj, Mem);
3131 end;
3133 begin
3134 if Mem = nil then
3135 Exit;
3137 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
3138 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
3139 LoadPanelArray(gWalls);
3140 // Çàãðóæàåì ïàíåëè ôîíà:
3141 LoadPanelArray(gRenderBackgrounds);
3142 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
3143 LoadPanelArray(gRenderForegrounds);
3144 // Çàãðóæàåì ïàíåëè âîäû:
3145 LoadPanelArray(gWater);
3146 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
3147 LoadPanelArray(gAcid1);
3148 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
3149 LoadPanelArray(gAcid2);
3150 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
3151 LoadPanelArray(gSteps);
3152 // Çàãðóæàåì ïàíåëè ëèôòîâ:
3153 LoadPanelArray(gLifts);
3154 ///// /////
3156 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
3157 g_GFX_Init();
3158 //mapCreateGrid();
3160 ///// Çàãðóæàåì ìóçûêó: /////
3161 // Ñèãíàòóðà ìóçûêè:
3162 Mem.ReadDWORD(dw);
3163 if dw <> MUSIC_SIGNATURE then // 'MUSI'
3164 begin
3165 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
3166 end;
3167 // Íàçâàíèå ìóçûêè:
3168 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
3169 Mem.ReadString(str);
3170 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
3171 Mem.ReadDWORD(dw);
3172 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
3173 Mem.ReadBoolean(boo);
3174 // Çàïóñêàåì ýòó ìóçûêó:
3175 gMusic.SetByName(str);
3176 gMusic.SpecPause := boo;
3177 gMusic.Play();
3178 gMusic.Pause(True);
3179 gMusic.SetPosition(dw);
3180 ///// /////
3182 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
3183 Mem.ReadInt(gTotalMonsters);
3184 ///// /////
3186 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
3187 if gGameSettings.GameMode = GM_CTF then
3188 begin
3189 // Ôëàã Êðàñíîé êîìàíäû:
3190 LoadFlag(@gFlags[FLAG_RED]);
3191 // Ôëàã Ñèíåé êîìàíäû:
3192 LoadFlag(@gFlags[FLAG_BLUE]);
3193 end;
3194 ///// /////
3196 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3197 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3198 begin
3199 // Î÷êè Êðàñíîé êîìàíäû:
3200 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
3201 // Î÷êè Ñèíåé êîìàíäû:
3202 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
3203 end;
3204 ///// /////
3205 end;
3207 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
3208 var
3209 Arr: TPanelArray;
3210 begin
3211 Result := nil;
3212 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
3213 Arr := PanelByID[PanelID].PWhere^;
3214 PanelArrayID := PanelByID[PanelID].PArrID;
3215 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
3216 end;
3219 // trace liquid, stepping by `dx` and `dy`
3220 // return last seen liquid coords, and `false` if we're started outside of the liquid
3221 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
3222 const
3223 MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
3224 begin
3225 topx := x;
3226 topy := y;
3227 // started outside of the liquid?
3228 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end;
3229 if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check
3230 result := true;
3231 while true do
3232 begin
3233 Inc(x, dx);
3234 Inc(y, dy);
3235 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit
3236 topx := x;
3237 topy := y;
3238 end;
3239 end;
3242 end.