DEADSOFTWARE

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