DEADSOFTWARE

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