DEADSOFTWARE

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