DEADSOFTWARE

fixed segfault on loading invalid maps
[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(freeTextures: Boolean=true);
64 procedure g_Map_Update();
66 function g_Map_PanelByGUID (aguid: Integer): TPanel; inline;
68 procedure g_Map_DrawPanels (PanelType: Word); // unaccelerated
69 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
71 procedure g_Map_DrawBack(dx, dy: Integer);
72 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
73 PanelType: Word; b1x3: Boolean=false): Boolean;
74 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
76 procedure g_Map_EnableWallGUID (pguid: Integer);
77 procedure g_Map_DisableWallGUID (pguid: Integer);
78 procedure g_Map_SetLiftGUID (pguid: Integer; t: Integer);
80 // HACK!!!
81 procedure g_Map_EnableWall_XXX (ID: DWORD);
82 procedure g_Map_DisableWall_XXX (ID: DWORD);
83 procedure g_Map_SetLift_XXX (ID: DWORD; t: Integer);
85 procedure g_Map_SwitchTextureGUID (PanelType: Word; pguid: Integer; AnimLoop: Byte = 0);
87 procedure g_Map_ReAdd_DieTriggers();
88 function g_Map_IsSpecialTexture(Texture: String): Boolean;
90 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
91 function g_Map_GetPointCount(PointType: Byte): Word;
93 function g_Map_HaveFlagPoints(): Boolean;
95 procedure g_Map_ResetFlag(Flag: Byte);
96 procedure g_Map_DrawFlags();
98 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
99 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
101 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
103 // returns panel or nil
104 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
105 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
107 // returns panel or nil
108 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
109 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
111 type
112 TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop
114 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
115 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
117 // trace liquid, stepping by `dx` and `dy`
118 // return last seen liquid coords, and `false` if we're started outside of the liquid
119 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
122 procedure g_Map_ProfilersBegin ();
123 procedure g_Map_ProfilersEnd ();
126 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
129 function g_Map_MinX (): Integer; inline;
130 function g_Map_MinY (): Integer; inline;
131 function g_Map_MaxX (): Integer; inline;
132 function g_Map_MaxY (): Integer; inline;
134 const
135 NNF_NO_NAME = 0;
136 NNF_NAME_BEFORE = 1;
137 NNF_NAME_EQUALS = 2;
138 NNF_NAME_AFTER = 3;
140 function g_Texture_NumNameFindStart(name: String): Boolean;
141 function g_Texture_NumNameFindNext(var newName: String): Byte;
143 const
144 RESPAWNPOINT_PLAYER1 = 1;
145 RESPAWNPOINT_PLAYER2 = 2;
146 RESPAWNPOINT_DM = 3;
147 RESPAWNPOINT_RED = 4;
148 RESPAWNPOINT_BLUE = 5;
150 FLAG_NONE = 0;
151 FLAG_RED = 1;
152 FLAG_BLUE = 2;
153 FLAG_DOM = 3;
155 FLAG_STATE_NONE = 0;
156 FLAG_STATE_NORMAL = 1;
157 FLAG_STATE_DROPPED = 2;
158 FLAG_STATE_CAPTURED = 3;
159 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
160 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
162 FLAG_TIME = 720; // 20 seconds
164 SKY_STRETCH: Single = 1.5;
166 const
167 GridTagInvalid = 0;
169 (* draw order:
170 PANEL_BACK
171 PANEL_STEP
172 PANEL_WALL
173 PANEL_CLOSEDOOR
174 PANEL_ACID1
175 PANEL_ACID2
176 PANEL_WATER
177 PANEL_FORE
178 *)
179 // sorted by draw priority
180 GridTagBack = 1 shl 0; // gRenderBackgrounds
181 GridTagStep = 1 shl 1; // gSteps
182 GridTagWall = 1 shl 2; // gWalls
183 GridTagDoor = 1 shl 3; // gWalls
184 GridTagAcid1 = 1 shl 4; // gAcid1
185 GridTagAcid2 = 1 shl 5; // gAcid2
186 GridTagWater = 1 shl 6; // gWater
187 GridTagFore = 1 shl 7; // gRenderForegrounds
188 // the following are invisible
189 GridTagLift = 1 shl 8; // gLifts
190 GridTagBlockMon = 1 shl 9; // gBlockMon
192 GridTagObstacle = (GridTagStep or GridTagWall or GridTagDoor);
193 GridTagLiquid = (GridTagAcid1 or GridTagAcid2 or GridTagWater);
195 GridDrawableMask = (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore);
198 var
199 gWalls: TPanelArray;
200 gRenderBackgrounds: TPanelArray;
201 gRenderForegrounds: TPanelArray;
202 gWater, gAcid1, gAcid2: TPanelArray;
203 gSteps: TPanelArray;
204 gLifts: TPanelArray;
205 gBlockMon: TPanelArray;
206 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
207 //gDOMFlags: array of TFlag;
208 gMapInfo: TMapInfo;
209 gBackSize: TDFPoint;
210 gDoorMap: array of array of DWORD;
211 gLiftMap: array of array of DWORD;
212 gWADHash: TMD5Digest;
213 BackID: DWORD = DWORD(-1);
214 gExternalResources: TStringList;
215 gMovingWallIds: array of Integer = nil;
217 gdbg_map_use_accel_render: Boolean = true;
218 gdbg_map_use_accel_coldet: Boolean = true;
219 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
220 gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
222 gCurrentMap: TDynRecord = nil;
223 gCurrentMapFileName: AnsiString = ''; // so we can skip texture reloading
226 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
229 type
230 TPanelGrid = specialize TBodyGridBase<TPanel>;
232 var
233 mapGrid: TPanelGrid = nil; // DO NOT USE! public for debugging only!
236 implementation
238 uses
239 e_input, g_main, e_log, SysUtils, g_items, g_gfx, g_console,
240 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
241 g_options, g_triggers, g_player,
242 Math, g_monsters, g_saveload, g_language, g_netmsg,
243 utils, sfs, xstreams, hashtable,
244 ImagingTypes, Imaging, ImagingUtility,
245 ImagingGif, ImagingNetworkGraphics;
247 const
248 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
249 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
250 FLAG_SIGNATURE = $47414C46; // 'FLAG'
253 var
254 panByGUID: array of TPanel = nil;
257 // ////////////////////////////////////////////////////////////////////////// //
258 function g_Map_PanelByGUID (aguid: Integer): TPanel; inline;
259 begin
260 //if (panByGUID = nil) or (not panByGUID.get(aguid, result)) then result := nil;
261 if (aguid >= 0) and (aguid < Length(panByGUID)) then result := panByGUID[aguid] else result := nil;
262 end;
265 // ////////////////////////////////////////////////////////////////////////// //
266 function g_Map_MinX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0 else result := 0; end;
267 function g_Map_MinY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0 else result := 0; end;
268 function g_Map_MaxX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0+mapGrid.gridWidth-1 else result := 0; end;
269 function g_Map_MaxY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0+mapGrid.gridHeight-1 else result := 0; end;
272 // ////////////////////////////////////////////////////////////////////////// //
273 var
274 dfmapdef: TDynMapDef = nil;
277 procedure loadMapDefinition ();
278 var
279 pr: TTextParser = nil;
280 st: TStream = nil;
281 WAD: TWADFile = nil;
282 begin
283 if (dfmapdef <> nil) then exit;
285 try
286 e_LogWritefln('parsing "mapdef.txt"...', []);
287 st := openDiskFileRO(DataDir+'mapdef.txt');
288 e_LogWritefln('found local "%smapdef.txt"', [DataDir]);
289 except
290 st := nil;
291 end;
293 if (st = nil) then
294 begin
295 WAD := TWADFile.Create();
296 if not WAD.ReadFile(GameWAD) then
297 begin
298 //raise Exception.Create('cannot load "game.wad"');
299 st := nil;
300 end
301 else
302 begin
303 st := WAD.openFileStream('mapdef.txt');
304 end;
305 end;
307 try
308 if (st = nil) then
309 begin
310 //raise Exception.Create('cannot open "mapdef.txt"');
311 e_LogWriteln('using default "mapdef.txt"...');
312 pr := TStrTextParser.Create(defaultMapDef);
313 end
314 else
315 begin
316 pr := TFileTextParser.Create(st);
317 end;
318 except on e: Exception do
319 begin
320 e_LogWritefln('something is VERY wrong here! -- ', [e.message]);
321 raise;
322 end;
323 end;
325 try
326 dfmapdef := TDynMapDef.Create(pr);
327 except on e: Exception do
328 raise Exception.Create(Format('ERROR in "mapdef.txt" at (%s,%s): %s', [pr.line, pr.col, e.message]));
329 end;
331 st.Free();
332 WAD.Free();
333 end;
336 // ////////////////////////////////////////////////////////////////////////// //
337 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
338 var
339 wst: TSFSMemoryChunkStream = nil;
340 pr: TTextParser = nil;
341 begin
342 result := nil;
343 if (dataLen < 4) then exit;
345 if (dfmapdef = nil) then writeln('need to load mapdef');
346 loadMapDefinition();
347 if (dfmapdef = nil) then raise Exception.Create('internal map loader error');
349 wst := TSFSMemoryChunkStream.Create(data, dataLen);
351 if (PAnsiChar(data)[0] = 'M') and (PAnsiChar(data)[1] = 'A') and (PAnsiChar(data)[2] = 'P') and (PByte(data)[3] = 1) then
352 begin
353 // binary map
354 try
355 //e_LogWriteln('parsing binary map...');
356 result := dfmapdef.parseBinMap(wst);
357 except on e: Exception do
358 begin
359 e_LogWritefln('ERROR: %s', [e.message]);
360 wst.Free();
361 result := nil;
362 exit;
363 end;
364 end;
365 wst.Free();
366 end
367 else
368 begin
369 // text map
370 pr := TFileTextParser.Create(wst);
371 try
372 //e_LogWriteln('parsing text map...');
373 result := dfmapdef.parseMap(pr);
374 except on e: Exception do
375 begin
376 if (pr <> nil) then e_LogWritefln('ERROR at (%s,%s): %s', [pr.tokLine, pr.tokCol, e.message])
377 else e_LogWritefln('ERROR: %s', [e.message]);
378 pr.Free(); // will free `wst`
379 result := nil;
380 exit;
381 end;
382 end;
383 pr.Free(); // will free `wst`
384 end;
385 //e_LogWriteln('map parsed.');
386 end;
389 // ////////////////////////////////////////////////////////////////////////// //
390 var
391 NNF_PureName: String; // Èìÿ òåêñòóðû áåç öèôð â êîíöå
392 NNF_FirstNum: Integer; // ×èñëî ó íà÷àëüíîé òåêñòóðû
393 NNF_CurrentNum: Integer; // Ñëåäóþùåå ÷èñëî ó òåêñòóðû
396 function g_Texture_NumNameFindStart(name: String): Boolean;
397 var
398 i: Integer;
400 begin
401 Result := False;
402 NNF_PureName := '';
403 NNF_FirstNum := -1;
404 NNF_CurrentNum := -1;
406 for i := Length(name) downto 1 do
407 if (name[i] = '_') then // "_" - ñèìâîë íà÷àëà íîìåðíîãî ïîñòôèêñà
408 begin
409 if i = Length(name) then
410 begin // Íåò öèôð â êîíöå ñòðîêè
411 Exit;
412 end
413 else
414 begin
415 NNF_PureName := Copy(name, 1, i);
416 Delete(name, 1, i);
417 Break;
418 end;
419 end;
421 // Íå ïåðåâåñòè â ÷èñëî:
422 if not TryStrToInt(name, NNF_FirstNum) then
423 Exit;
425 NNF_CurrentNum := 0;
427 Result := True;
428 end;
431 function g_Texture_NumNameFindNext(var newName: String): Byte;
432 begin
433 if (NNF_PureName = '') or (NNF_CurrentNum < 0) then
434 begin
435 newName := '';
436 Result := NNF_NO_NAME;
437 Exit;
438 end;
440 newName := NNF_PureName + IntToStr(NNF_CurrentNum);
442 if NNF_CurrentNum < NNF_FirstNum then
443 Result := NNF_NAME_BEFORE
444 else
445 if NNF_CurrentNum > NNF_FirstNum then
446 Result := NNF_NAME_AFTER
447 else
448 Result := NNF_NAME_EQUALS;
450 Inc(NNF_CurrentNum);
451 end;
454 // ////////////////////////////////////////////////////////////////////////// //
455 function panelTypeToTag (panelType: Word): Integer;
456 begin
457 case panelType of
458 PANEL_WALL: result := GridTagWall; // gWalls
459 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
460 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
461 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
462 PANEL_WATER: result := GridTagWater; // gWater
463 PANEL_ACID1: result := GridTagAcid1; // gAcid1
464 PANEL_ACID2: result := GridTagAcid2; // gAcid2
465 PANEL_STEP: result := GridTagStep; // gSteps
466 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
467 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
468 else result := GridTagInvalid;
469 end;
470 end;
473 function dplLess (a, b: TObject): Boolean;
474 var
475 pa, pb: TPanel;
476 begin
477 pa := TPanel(a);
478 pb := TPanel(b);
479 if (pa.tag < pb.tag) then begin result := true; exit; end;
480 if (pa.tag > pb.tag) then begin result := false; exit; end;
481 result := (pa.arrIdx < pb.arrIdx);
482 end;
484 procedure dplClear ();
485 begin
486 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
487 end;
490 var
491 Textures: TLevelTextureArray = nil;
492 TextNameHash: THashStrInt = nil; // key: texture name; value: index in `Textures`
493 BadTextNameHash: THashStrInt = nil; // set; so we won't spam with non-existing texture messages
494 RespawnPoints: array of TRespawnPoint;
495 FlagPoints: array[FLAG_RED..FLAG_BLUE] of PFlagPoint;
496 //DOMFlagPoints: Array of TFlagPoint;
499 procedure g_Map_ProfilersBegin ();
500 begin
501 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
502 profMapCollision.mainBegin(g_profile_collision);
503 // create sections
504 if g_profile_collision then
505 begin
506 profMapCollision.sectionBegin('*solids');
507 profMapCollision.sectionEnd();
508 profMapCollision.sectionBegin('liquids');
509 profMapCollision.sectionEnd();
510 end;
511 end;
513 procedure g_Map_ProfilersEnd ();
514 begin
515 if (profMapCollision <> nil) then profMapCollision.mainEnd();
516 end;
519 // wall index in `gWalls` or -1
520 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
521 var
522 ex, ey: Integer;
523 begin
524 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, (GridTagWall or GridTagDoor));
525 if (result <> nil) then
526 begin
527 if (hitx <> nil) then hitx^ := ex;
528 if (hity <> nil) then hity^ := ey;
529 end
530 else
531 begin
532 if (hitx <> nil) then hitx^ := x1;
533 if (hity <> nil) then hity^ := y1;
534 end;
535 end;
537 // returns panel or nil
538 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
539 var
540 ex, ey: Integer;
541 begin
542 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, tag);
543 if (result <> nil) then
544 begin
545 if (hitx <> nil) then hitx^ := ex;
546 if (hity <> nil) then hity^ := ey;
547 end
548 else
549 begin
550 if (hitx <> nil) then hitx^ := x1;
551 if (hity <> nil) then hity^ := y1;
552 end;
553 end;
556 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
558 function checker (pan: TPanel; tag: Integer): Boolean;
559 begin
561 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
562 begin
563 result := pan.Enabled; // stop if wall is enabled
564 exit;
565 end;
568 if ((tag and GridTagLift) <> 0) then
569 begin
570 // stop if the lift of the right type
571 result :=
572 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
573 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
574 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
575 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
576 exit;
577 end;
579 result := true; // otherwise, stop anyway, 'cause `forEachAtPoint()` is guaranteed to call this only for correct panels
580 end;
582 var
583 tagmask: Integer = 0;
584 begin
585 result := false;
587 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
588 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
589 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
590 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
591 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
592 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
593 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
595 if (tagmask = 0) then exit;// just in case
596 if ((tagmask and GridTagLift) <> 0) then
597 begin
598 // slow
599 result := (mapGrid.forEachAtPoint(x, y, checker, tagmask) <> nil);
600 end
601 else
602 begin
603 // fast
604 result := (mapGrid.forEachAtPoint(x, y, nil, tagmask) <> nil);
605 end;
606 end;
609 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
610 begin
611 result := nil;
612 if (tagmask = 0) then exit;
613 result := mapGrid.forEachAtPoint(x, y, nil, tagmask);
614 end;
617 function g_Map_IsSpecialTexture(Texture: String): Boolean;
618 begin
619 Result := (Texture = TEXTURE_NAME_WATER) or
620 (Texture = TEXTURE_NAME_ACID1) or
621 (Texture = TEXTURE_NAME_ACID2);
622 end;
624 procedure CreateDoorMap();
625 var
626 PanelArray: Array of record
627 X, Y: Integer;
628 Width, Height: Word;
629 Active: Boolean;
630 PanelID: DWORD;
631 end;
632 a, b, c, m, i, len: Integer;
633 ok: Boolean;
634 begin
635 if gWalls = nil then
636 Exit;
638 i := 0;
639 len := 128;
640 SetLength(PanelArray, len);
642 for a := 0 to High(gWalls) do
643 if gWalls[a].Door then
644 begin
645 PanelArray[i].X := gWalls[a].X;
646 PanelArray[i].Y := gWalls[a].Y;
647 PanelArray[i].Width := gWalls[a].Width;
648 PanelArray[i].Height := gWalls[a].Height;
649 PanelArray[i].Active := True;
650 PanelArray[i].PanelID := a;
652 i := i + 1;
653 if i = len then
654 begin
655 len := len + 128;
656 SetLength(PanelArray, len);
657 end;
658 end;
660 // Íåò äâåðåé:
661 if i = 0 then
662 begin
663 PanelArray := nil;
664 Exit;
665 end;
667 SetLength(gDoorMap, 0);
669 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
671 for a := 0 to i-1 do
672 if PanelArray[a].Active then
673 begin
674 PanelArray[a].Active := False;
675 m := Length(gDoorMap);
676 SetLength(gDoorMap, m+1);
677 SetLength(gDoorMap[m], 1);
678 gDoorMap[m, 0] := PanelArray[a].PanelID;
679 ok := True;
681 while ok do
682 begin
683 ok := False;
685 for b := 0 to i-1 do
686 if PanelArray[b].Active then
687 for c := 0 to High(gDoorMap[m]) do
688 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
689 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
690 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
691 PanelArray[b].Width, PanelArray[b].Height,
692 gWalls[gDoorMap[m, c]].X,
693 gWalls[gDoorMap[m, c]].Y,
694 gWalls[gDoorMap[m, c]].Width,
695 gWalls[gDoorMap[m, c]].Height) then
696 begin
697 PanelArray[b].Active := False;
698 SetLength(gDoorMap[m],
699 Length(gDoorMap[m])+1);
700 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
701 ok := True;
702 Break;
703 end;
704 end;
706 g_Game_StepLoading();
707 end;
709 PanelArray := nil;
710 end;
712 procedure CreateLiftMap();
713 var
714 PanelArray: Array of record
715 X, Y: Integer;
716 Width, Height: Word;
717 Active: Boolean;
718 end;
719 a, b, c, len, i, j: Integer;
720 ok: Boolean;
721 begin
722 if gLifts = nil then
723 Exit;
725 len := Length(gLifts);
726 SetLength(PanelArray, len);
728 for a := 0 to len-1 do
729 begin
730 PanelArray[a].X := gLifts[a].X;
731 PanelArray[a].Y := gLifts[a].Y;
732 PanelArray[a].Width := gLifts[a].Width;
733 PanelArray[a].Height := gLifts[a].Height;
734 PanelArray[a].Active := True;
735 end;
737 SetLength(gLiftMap, len);
738 i := 0;
740 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
742 for a := 0 to len-1 do
743 if PanelArray[a].Active then
744 begin
745 PanelArray[a].Active := False;
746 SetLength(gLiftMap[i], 32);
747 j := 0;
748 gLiftMap[i, j] := a;
749 ok := True;
751 while ok do
752 begin
753 ok := False;
754 for b := 0 to len-1 do
755 if PanelArray[b].Active then
756 for c := 0 to j do
757 if g_CollideAround(PanelArray[b].X,
758 PanelArray[b].Y,
759 PanelArray[b].Width,
760 PanelArray[b].Height,
761 PanelArray[gLiftMap[i, c]].X,
762 PanelArray[gLiftMap[i, c]].Y,
763 PanelArray[gLiftMap[i, c]].Width,
764 PanelArray[gLiftMap[i, c]].Height) then
765 begin
766 PanelArray[b].Active := False;
767 j := j+1;
768 if j > High(gLiftMap[i]) then
769 SetLength(gLiftMap[i],
770 Length(gLiftMap[i])+32);
772 gLiftMap[i, j] := b;
773 ok := True;
775 Break;
776 end;
777 end;
779 SetLength(gLiftMap[i], j+1);
780 i := i+1;
782 g_Game_StepLoading();
783 end;
785 SetLength(gLiftMap, i);
787 PanelArray := nil;
788 end;
790 function CreatePanel (PanelRec: TDynRecord; AddTextures: TAddTextureArray; CurTex: Integer; sav: Boolean): Integer;
791 var
792 len: Integer;
793 panels: ^TPanelArray;
794 pan: TPanel;
795 pguid: Integer;
796 begin
797 Result := -1;
799 case PanelRec.PanelType of
800 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: panels := @gWalls;
801 PANEL_BACK: panels := @gRenderBackgrounds;
802 PANEL_FORE: panels := @gRenderForegrounds;
803 PANEL_WATER: panels := @gWater;
804 PANEL_ACID1: panels := @gAcid1;
805 PANEL_ACID2: panels := @gAcid2;
806 PANEL_STEP: panels := @gSteps;
807 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: panels := @gLifts;
808 PANEL_BLOCKMON: panels := @gBlockMon;
809 else exit;
810 end;
812 len := Length(panels^);
813 SetLength(panels^, len+1);
815 pguid := Length(panByGUID);
816 SetLength(panByGUID, pguid+1); //FIXME!
817 pan := TPanel.Create(PanelRec, AddTextures, CurTex, Textures, pguid);
818 assert(pguid >= 0);
819 assert(pguid < Length(panByGUID));
820 panByGUID[pguid] := pan;
821 panels^[len] := pan;
822 pan.arrIdx := len;
823 pan.proxyId := -1;
824 pan.tag := panelTypeToTag(PanelRec.PanelType);
825 if sav then pan.SaveIt := True;
827 PanelRec.user['panel_guid'] := pguid;
829 //result := len;
830 result := pguid;
831 end;
834 function CreateNullTexture(RecName: String): Integer;
835 begin
836 RecName := toLowerCase1251(RecName);
837 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
838 if TextNameHash.get(RecName, result) then exit; // i found her!
840 SetLength(Textures, Length(Textures)+1);
841 result := High(Textures);
843 with Textures[High(Textures)] do
844 begin
845 TextureName := RecName;
846 Width := 1;
847 Height := 1;
848 Anim := False;
849 TextureID := LongWord(TEXTURE_NONE);
850 end;
852 TextNameHash.put(RecName, result);
853 end;
856 function CreateTexture(RecName: AnsiString; Map: string; log: Boolean): Integer;
857 var
858 WAD: TWADFile;
859 TextureData: Pointer;
860 WADName: String;
861 a, ResLength: Integer;
862 begin
863 RecName := toLowerCase1251(RecName);
864 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
865 if TextNameHash.get(RecName, result) then
866 begin
867 // i found her!
868 //e_LogWritefln('texture ''%s'' already loaded', [RecName]);
869 exit;
870 end;
872 Result := -1;
874 if (BadTextNameHash <> nil) and BadTextNameHash.has(RecName) then exit; // don't do it again and again
877 if Textures <> nil then
878 begin
879 for a := 0 to High(Textures) do
880 begin
881 if (Textures[a].TextureName = RecName) then
882 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
883 e_LogWritefln('texture ''%s'' already loaded', [RecName]);
884 Result := a;
885 Exit;
886 end;
887 end;
888 end;
891 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
892 if (RecName = TEXTURE_NAME_WATER) or
893 (RecName = TEXTURE_NAME_ACID1) or
894 (RecName = TEXTURE_NAME_ACID2) then
895 begin
896 SetLength(Textures, Length(Textures)+1);
898 with Textures[High(Textures)] do
899 begin
900 TextureName := RecName;
901 if (TextureName = TEXTURE_NAME_WATER) then TextureID := LongWord(TEXTURE_SPECIAL_WATER)
902 else if (TextureName = TEXTURE_NAME_ACID1) then TextureID := LongWord(TEXTURE_SPECIAL_ACID1)
903 else if (TextureName = TEXTURE_NAME_ACID2) then TextureID := LongWord(TEXTURE_SPECIAL_ACID2);
905 Anim := False;
906 end;
908 result := High(Textures);
909 TextNameHash.put(RecName, result);
910 Exit;
911 end;
913 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
914 WADName := g_ExtractWadName(RecName);
916 WAD := TWADFile.Create();
918 if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
920 WAD.ReadFile(WADName);
922 //txname := RecName;
924 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
925 begin
926 FreeMem(TextureData);
927 RecName := 'COMMON\ALIEN';
928 end;
931 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength, log) then
932 begin
933 SetLength(Textures, Length(Textures)+1);
934 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
935 begin
936 SetLength(Textures, Length(Textures)-1);
937 Exit;
938 end;
939 e_GetTextureSize(Textures[High(Textures)].TextureID, @Textures[High(Textures)].Width, @Textures[High(Textures)].Height);
940 FreeMem(TextureData);
941 Textures[High(Textures)].TextureName := RecName;
942 Textures[High(Textures)].Anim := False;
944 result := High(Textures);
945 TextNameHash.put(RecName, result);
946 end
947 else // Íåò òàêîãî ðåóñðñà â WAD'å
948 begin
949 //e_WriteLog(Format('SHIT! Error loading texture %s : %s', [RecName, g_ExtractFilePathName(RecName)]), MSG_WARNING);
950 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
951 if log and (not BadTextNameHash.get(RecName, a)) then
952 begin
953 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
954 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
955 end;
956 BadTextNameHash.put(RecName, -1);
957 end;
959 WAD.Free();
960 end;
963 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
964 var
965 WAD: TWADFile;
966 TextureWAD: PChar = nil;
967 TextData: Pointer = nil;
968 TextureData: Pointer = nil;
969 cfg: TConfig = nil;
970 WADName: String;
971 ResLength: Integer;
972 TextureResource: String;
973 _width, _height, _framecount, _speed: Integer;
974 _backanimation: Boolean;
975 //imgfmt: string;
976 ia: TDynImageDataArray = nil;
977 f, c, frdelay, frloop: Integer;
978 begin
979 RecName := toLowerCase1251(RecName);
980 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
981 if TextNameHash.get(RecName, result) then
982 begin
983 // i found her!
984 //e_LogWritefln('animated texture ''%s'' already loaded', [RecName]);
985 exit;
986 end;
988 result := -1;
990 //e_LogWritefln('*** Loading animated texture "%s"', [RecName]);
992 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
993 if BadTextNameHash.get(RecName, f) then
994 begin
995 //e_WriteLog(Format('no animation texture %s (don''t worry)', [RecName]), MSG_NOTIFY);
996 exit;
997 end;
999 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
1000 WADName := g_ExtractWadName(RecName);
1002 WAD := TWADFile.Create();
1003 try
1004 if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
1006 WAD.ReadFile(WADName);
1008 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength, log) then
1009 begin
1010 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1011 if log and (not BadTextNameHash.get(RecName, f)) then
1012 begin
1013 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
1014 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
1015 end;
1016 BadTextNameHash.put(RecName, -1);
1017 exit;
1018 end;
1020 {TEST
1021 if WADName = Map then
1022 begin
1023 //FreeMem(TextureWAD);
1024 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
1025 end;
1028 WAD.FreeWAD();
1030 if ResLength < 6 then
1031 begin
1032 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
1033 BadTextNameHash.put(RecName, -1);
1034 exit;
1035 end;
1037 // ýòî ïòèöà? ýòî ñàìîë¸ò?
1038 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
1039 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
1040 begin
1041 // íåò, ýòî ñóïåðìåí!
1042 if not WAD.ReadMemory(TextureWAD, ResLength) then
1043 begin
1044 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
1045 BadTextNameHash.put(RecName, -1);
1046 exit;
1047 end;
1049 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
1050 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
1051 begin
1052 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
1053 BadTextNameHash.put(RecName, -1);
1054 exit;
1055 end;
1057 cfg := TConfig.CreateMem(TextData, ResLength);
1059 TextureResource := cfg.ReadStr('', 'resource', '');
1060 if TextureResource = '' then
1061 begin
1062 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
1063 BadTextNameHash.put(RecName, -1);
1064 exit;
1065 end;
1067 _width := cfg.ReadInt('', 'framewidth', 0);
1068 _height := cfg.ReadInt('', 'frameheight', 0);
1069 _framecount := cfg.ReadInt('', 'framecount', 0);
1070 _speed := cfg.ReadInt('', 'waitcount', 0);
1071 _backanimation := cfg.ReadBool('', 'backanimation', False);
1073 cfg.Free();
1074 cfg := nil;
1076 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
1077 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
1078 begin
1079 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
1080 BadTextNameHash.put(RecName, -1);
1081 exit;
1082 end;
1084 WAD.Free();
1085 WAD := nil;
1087 SetLength(Textures, Length(Textures)+1);
1088 with Textures[High(Textures)] do
1089 begin
1090 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
1091 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
1092 begin
1093 TextureName := RecName;
1094 Width := _width;
1095 Height := _height;
1096 Anim := True;
1097 FramesCount := _framecount;
1098 Speed := _speed;
1099 result := High(Textures);
1100 TextNameHash.put(RecName, result);
1101 end
1102 else
1103 begin
1104 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1105 if log and (not BadTextNameHash.get(RecName, f)) then
1106 begin
1107 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
1108 end;
1109 BadTextNameHash.put(RecName, -1);
1110 end;
1111 end;
1112 end
1113 else
1114 begin
1115 // try animated image
1117 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
1118 if length(imgfmt) = 0 then
1119 begin
1120 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
1121 exit;
1122 end;
1124 GlobalMetadata.ClearMetaItems();
1125 GlobalMetadata.ClearMetaItemsForSaving();
1126 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
1127 begin
1128 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
1129 BadTextNameHash.put(RecName, -1);
1130 exit;
1131 end;
1132 if length(ia) = 0 then
1133 begin
1134 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
1135 BadTextNameHash.put(RecName, -1);
1136 exit;
1137 end;
1139 WAD.Free();
1140 WAD := nil;
1142 _width := ia[0].width;
1143 _height := ia[0].height;
1144 _framecount := length(ia);
1145 _speed := 1;
1146 _backanimation := false;
1147 frdelay := -1;
1148 frloop := -666;
1149 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
1150 begin
1151 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
1152 try
1153 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
1154 frdelay := f;
1155 if f < 0 then f := 0;
1156 // rounding ;-)
1157 c := f mod 28;
1158 if c < 13 then c := 0 else c := 1;
1159 f := (f div 28)+c;
1160 if f < 1 then f := 1 else if f > 255 then f := 255;
1161 _speed := f;
1162 except
1163 end;
1164 end;
1165 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
1166 begin
1167 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
1168 try
1169 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
1170 frloop := f;
1171 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
1172 except
1173 end;
1174 end;
1175 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
1176 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
1177 f := ord(_backanimation);
1178 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);
1180 SetLength(Textures, Length(Textures)+1);
1181 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
1182 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
1183 begin
1184 Textures[High(Textures)].TextureName := RecName;
1185 Textures[High(Textures)].Width := _width;
1186 Textures[High(Textures)].Height := _height;
1187 Textures[High(Textures)].Anim := True;
1188 Textures[High(Textures)].FramesCount := length(ia);
1189 Textures[High(Textures)].Speed := _speed;
1190 result := High(Textures);
1191 TextNameHash.put(RecName, result);
1192 //writeln(' CREATED!');
1193 end
1194 else
1195 begin
1196 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1197 if log and (not BadTextNameHash.get(RecName, f)) then
1198 begin
1199 e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
1200 end;
1201 BadTextNameHash.put(RecName, -1);
1202 end;
1203 end;
1204 finally
1205 for f := 0 to High(ia) do FreeImage(ia[f]);
1206 WAD.Free();
1207 cfg.Free();
1208 if (TextureWAD <> nil) then FreeMem(TextureWAD);
1209 if (TextData <> nil) then FreeMem(TextData);
1210 if (TextureData <> nil) then FreeMem(TextureData);
1211 end;
1212 end;
1214 procedure CreateItem(Item: TDynRecord);
1215 begin
1216 if g_Game_IsClient then Exit;
1218 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
1219 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
1220 Exit;
1222 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
1223 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
1224 end;
1226 procedure CreateArea(Area: TDynRecord);
1227 var
1228 a: Integer;
1229 id: DWORD = 0;
1230 begin
1231 case Area.AreaType of
1232 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
1233 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
1234 begin
1235 SetLength(RespawnPoints, Length(RespawnPoints)+1);
1236 with RespawnPoints[High(RespawnPoints)] do
1237 begin
1238 X := Area.X;
1239 Y := Area.Y;
1240 Direction := TDirection(Area.Direction);
1242 case Area.AreaType of
1243 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
1244 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
1245 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
1246 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
1247 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
1248 end;
1249 end;
1250 end;
1252 AREA_REDFLAG, AREA_BLUEFLAG:
1253 begin
1254 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
1256 if FlagPoints[a] <> nil then Exit;
1258 New(FlagPoints[a]);
1260 with FlagPoints[a]^ do
1261 begin
1262 X := Area.X-FLAGRECT.X;
1263 Y := Area.Y-FLAGRECT.Y;
1264 Direction := TDirection(Area.Direction);
1265 end;
1267 with gFlags[a] do
1268 begin
1269 case a of
1270 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
1271 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
1272 end;
1274 Animation := TAnimation.Create(id, True, 8);
1275 Obj.Rect := FLAGRECT;
1277 g_Map_ResetFlag(a);
1278 end;
1279 end;
1281 AREA_DOMFLAG:
1282 begin
1283 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
1284 with DOMFlagPoints[High(DOMFlagPoints)] do
1285 begin
1286 X := Area.X;
1287 Y := Area.Y;
1288 Direction := TDirection(Area.Direction);
1289 end;
1291 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
1292 end;
1293 end;
1294 end;
1296 procedure CreateTrigger (amapIdx: Integer; Trigger: TDynRecord; atpanid, atrigpanid: Integer; fTexturePanel1Type, fTexturePanel2Type: Word);
1297 var
1298 _trigger: TTrigger;
1299 begin
1300 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
1302 with _trigger do
1303 begin
1304 mapId := Trigger.id;
1305 mapIndex := amapIdx;
1306 X := Trigger.X;
1307 Y := Trigger.Y;
1308 Width := Trigger.Width;
1309 Height := Trigger.Height;
1310 Enabled := Trigger.Enabled;
1311 //TexturePanel := Trigger.TexturePanel;
1312 TexturePanelGUID := atpanid;
1313 TexturePanelType := fTexturePanel1Type;
1314 ShotPanelType := fTexturePanel2Type;
1315 TriggerType := Trigger.TriggerType;
1316 ActivateType := Trigger.ActivateType;
1317 Keys := Trigger.Keys;
1318 trigPanelGUID := atrigpanid;
1319 //trigShotPanelId := ashotpanid;
1320 //Data.Default := Trigger.DATA;
1321 if (Trigger.trigRec = nil) then
1322 begin
1323 trigData := nil;
1324 if (TriggerType <> TRIGGER_SECRET) then
1325 begin
1326 e_LogWritefln('trigger of type %s has no triggerdata; wtf?!', [TriggerType], MSG_WARNING);
1327 end;
1328 end
1329 else
1330 begin
1331 trigData := Trigger.trigRec.clone(nil);
1332 end;
1333 end;
1335 g_Triggers_Create(_trigger);
1336 end;
1338 procedure CreateMonster(monster: TDynRecord);
1339 var
1340 a: Integer;
1341 mon: TMonster;
1342 begin
1343 if g_Game_IsClient then Exit;
1345 if (gGameSettings.GameType = GT_SINGLE)
1346 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1347 begin
1348 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1350 if gTriggers <> nil then
1351 begin
1352 for a := 0 to High(gTriggers) do
1353 begin
1354 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1355 begin
1356 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1357 if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1358 end;
1359 end;
1360 end;
1362 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1363 end;
1364 end;
1366 procedure g_Map_ReAdd_DieTriggers();
1368 function monsDieTrig (mon: TMonster): Boolean;
1369 var
1370 a: Integer;
1371 //tw: TStrTextWriter;
1372 begin
1373 result := false; // don't stop
1374 mon.ClearTriggers();
1375 for a := 0 to High(gTriggers) do
1376 begin
1377 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1378 begin
1379 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1381 tw := TStrTextWriter.Create();
1382 try
1383 gTriggers[a].trigData.writeTo(tw);
1384 e_LogWritefln('=== trigger #%s ==='#10'%s'#10'---', [a, tw.str]);
1385 finally
1386 tw.Free();
1387 end;
1389 if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1390 end;
1391 end;
1392 end;
1394 begin
1395 if g_Game_IsClient then Exit;
1397 g_Mons_ForEach(monsDieTrig);
1398 end;
1400 function extractWadName(resourceName: string): string;
1401 var
1402 posN: Integer;
1403 begin
1404 posN := Pos(':', resourceName);
1405 if posN > 0 then
1406 Result:= Copy(resourceName, 0, posN-1)
1407 else
1408 Result := '';
1409 end;
1411 procedure addResToExternalResList(res: string);
1412 begin
1413 res := extractWadName(res);
1414 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1415 gExternalResources.Add(res);
1416 end;
1418 procedure generateExternalResourcesList({mapReader: TMapReader_1}map: TDynRecord);
1419 //var
1420 //textures: TTexturesRec1Array;
1421 //textures: TDynField;
1422 //trec: TDynRecord;
1423 //mapHeader: TMapHeaderRec_1;
1424 //i: integer;
1425 //resFile: String = '';
1426 begin
1427 if gExternalResources = nil then
1428 gExternalResources := TStringList.Create;
1430 gExternalResources.Clear;
1432 (*
1434 textures := GetTextures(map);
1435 for i := 0 to High(textures) do
1436 begin
1437 addResToExternalResList(resFile);
1438 end;
1441 textures := map['texture'];
1442 if (textures <> nil) then
1443 begin
1444 for trec in textures do
1445 begin
1446 addResToExternalResList(resFile);
1447 end;
1448 end;
1450 textures := nil;
1451 *)
1453 //mapHeader := GetMapHeader(map);
1455 addResToExternalResList(map.MusicName);
1456 addResToExternalResList(map.SkyName);
1457 end;
1460 procedure mapCreateGrid ();
1461 var
1462 mapX0: Integer = $3fffffff;
1463 mapY0: Integer = $3fffffff;
1464 mapX1: Integer = -$3fffffff;
1465 mapY1: Integer = -$3fffffff;
1467 procedure calcBoundingBox (constref panels: TPanelArray);
1468 var
1469 idx: Integer;
1470 pan: TPanel;
1471 begin
1472 for idx := 0 to High(panels) do
1473 begin
1474 pan := panels[idx];
1475 if not pan.visvalid then continue;
1476 if (pan.Width < 1) or (pan.Height < 1) then continue;
1477 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1478 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1479 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1480 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1481 end;
1482 end;
1484 procedure addPanelsToGrid (constref panels: TPanelArray);
1485 var
1486 idx: Integer;
1487 pan: TPanel;
1488 newtag: Integer;
1489 begin
1490 //tag := panelTypeToTag(tag);
1491 for idx := 0 to High(panels) do
1492 begin
1493 pan := panels[idx];
1494 if not pan.visvalid then continue;
1495 if (pan.proxyId <> -1) then
1496 begin
1497 {$IF DEFINED(D2F_DEBUG)}
1498 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);
1499 {$ENDIF}
1500 continue;
1501 end;
1502 case pan.PanelType of
1503 PANEL_WALL: newtag := GridTagWall;
1504 PANEL_OPENDOOR, PANEL_CLOSEDOOR: newtag := GridTagDoor;
1505 PANEL_BACK: newtag := GridTagBack;
1506 PANEL_FORE: newtag := GridTagFore;
1507 PANEL_WATER: newtag := GridTagWater;
1508 PANEL_ACID1: newtag := GridTagAcid1;
1509 PANEL_ACID2: newtag := GridTagAcid2;
1510 PANEL_STEP: newtag := GridTagStep;
1511 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: newtag := GridTagLift;
1512 PANEL_BLOCKMON: newtag := GridTagBlockMon;
1513 else continue; // oops
1514 end;
1515 pan.tag := newtag;
1517 pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, newtag);
1518 // "enabled" flag has meaning only for doors and walls (engine assumes it); but meh...
1519 mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
1520 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
1522 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
1523 begin
1524 e_WriteLog(Format('INSERTED wall #%d(%d) enabled (%d)', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY);
1525 end;
1527 {$ENDIF}
1528 end;
1529 end;
1531 begin
1532 mapGrid.Free();
1533 mapGrid := nil;
1535 calcBoundingBox(gWalls);
1536 calcBoundingBox(gRenderBackgrounds);
1537 calcBoundingBox(gRenderForegrounds);
1538 calcBoundingBox(gWater);
1539 calcBoundingBox(gAcid1);
1540 calcBoundingBox(gAcid2);
1541 calcBoundingBox(gSteps);
1542 calcBoundingBox(gLifts);
1543 calcBoundingBox(gBlockMon);
1545 e_LogWritefln('map dimensions: (%d,%d)-(%d,%d); editor size:(0,0)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1, gMapInfo.Width, gMapInfo.Height]);
1547 if (mapX0 > 0) then mapX0 := 0;
1548 if (mapY0 > 0) then mapY0 := 0;
1550 if (mapX1 < gMapInfo.Width-1) then mapX1 := gMapInfo.Width-1;
1551 if (mapY1 < gMapInfo.Height-1) then mapY1 := gMapInfo.Height-1;
1553 mapGrid := TPanelGrid.Create(mapX0-128, mapY0-128, mapX1-mapX0+1+128*2, mapY1-mapY0+1+128*2);
1554 //mapGrid := TPanelGrid.Create(0, 0, gMapInfo.Width, gMapInfo.Height);
1556 addPanelsToGrid(gWalls);
1557 addPanelsToGrid(gRenderBackgrounds);
1558 addPanelsToGrid(gRenderForegrounds);
1559 addPanelsToGrid(gWater);
1560 addPanelsToGrid(gAcid1);
1561 addPanelsToGrid(gAcid2);
1562 addPanelsToGrid(gSteps);
1563 addPanelsToGrid(gLifts); // it doesn't matter which LIFT type is used here
1564 addPanelsToGrid(gBlockMon);
1566 mapGrid.dumpStats();
1568 g_Mons_InitTree(mapGrid.gridX0, mapGrid.gridY0, mapGrid.gridWidth, mapGrid.gridHeight);
1569 end;
1572 function g_Map_Load(Res: String): Boolean;
1573 const
1574 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1575 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1576 type
1577 PTRec = ^TTRec;
1578 TTRec = record
1579 //TexturePanel: Integer;
1580 texPanIdx: Integer;
1581 LiftPanelIdx: Integer;
1582 DoorPanelIdx: Integer;
1583 ShotPanelIdx: Integer;
1584 MPlatPanelIdx: Integer;
1585 trigrec: TDynRecord;
1586 texPan: TDynRecord;
1587 liftPan: TDynRecord;
1588 doorPan: TDynRecord;
1589 shotPan: TDynRecord;
1590 mplatPan: TDynRecord;
1591 end;
1592 var
1593 WAD: TWADFile;
1594 mapReader: TDynRecord = nil;
1595 mapTextureList: TDynField = nil; //TTexturesRec1Array; tagInt: texture index
1596 panels: TDynField = nil; //TPanelsRec1Array;
1597 items: TDynField = nil; //TItemsRec1Array;
1598 monsters: TDynField = nil; //TMonsterRec1Array;
1599 areas: TDynField = nil; //TAreasRec1Array;
1600 triggers: TDynField = nil; //TTriggersRec1Array;
1601 b, c, k: Integer;
1602 PanelID: DWORD;
1603 AddTextures: TAddTextureArray;
1604 TriggersTable: array of TTRec;
1605 FileName, mapResName, s, TexName: String;
1606 Data: Pointer;
1607 Len: Integer;
1608 ok, isAnim, trigRef: Boolean;
1609 CurTex, ntn: Integer;
1610 rec, texrec: TDynRecord;
1611 pttit: PTRec;
1612 pannum, trignum, cnt, tgpid: Integer;
1613 stt: UInt64;
1614 moveSpeed{, moveStart, moveEnd}: TDFPoint;
1615 //moveActive: Boolean;
1616 begin
1617 mapGrid.Free();
1618 mapGrid := nil;
1620 gCurrentMap.Free();
1621 gCurrentMap := nil;
1623 panByGUID := nil;
1625 Result := False;
1626 gMapInfo.Map := Res;
1627 TriggersTable := nil;
1628 mapReader := nil;
1630 sfsGCDisable(); // temporary disable removing of temporary volumes
1631 try
1632 // Çàãðóçêà WAD:
1633 FileName := g_ExtractWadName(Res);
1634 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1635 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1637 WAD := TWADFile.Create();
1638 if not WAD.ReadFile(FileName) then
1639 begin
1640 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1641 WAD.Free();
1642 Exit;
1643 end;
1645 //k8: why loader ignores path here?
1646 mapResName := g_ExtractFileName(Res);
1647 if not WAD.GetMapResource(mapResName, Data, Len) then
1648 begin
1649 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1650 WAD.Free();
1651 Exit;
1652 end;
1654 WAD.Free();
1656 if (Len < 4) then
1657 begin
1658 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1659 FreeMem(Data);
1660 exit;
1661 end;
1663 // Çàãðóçêà êàðòû:
1664 e_LogWritefln('Loading map: %s', [mapResName], MSG_NOTIFY);
1665 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1667 stt := curTimeMicro();
1669 try
1670 mapReader := g_Map_ParseMap(Data, Len);
1671 except
1672 mapReader.Free();
1673 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1674 FreeMem(Data);
1675 Exit;
1676 end;
1678 FreeMem(Data);
1680 if (mapReader = nil) then
1681 begin
1682 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1683 exit;
1684 end;
1686 generateExternalResourcesList(mapReader);
1687 mapTextureList := mapReader['texture'];
1688 // get all other lists here too
1689 panels := mapReader['panel'];
1690 triggers := mapReader['trigger'];
1691 items := mapReader['item'];
1692 areas := mapReader['area'];
1693 monsters := mapReader['monster'];
1695 // Çàãðóçêà îïèñàíèÿ êàðòû:
1696 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1697 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1699 with gMapInfo do
1700 begin
1701 Name := mapReader.MapName;
1702 Description := mapReader.MapDesc;
1703 Author := mapReader.MapAuthor;
1704 MusicName := mapReader.MusicName;
1705 SkyName := mapReader.SkyName;
1706 Height := mapReader.Height;
1707 Width := mapReader.Width;
1708 end;
1710 // Çàãðóçêà òåêñòóð:
1711 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1712 // Äîáàâëåíèå òåêñòóð â Textures[]:
1713 if (mapTextureList <> nil) and (mapTextureList.count > 0) then
1714 begin
1715 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1716 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], mapTextureList.count-1, False);
1718 cnt := -1;
1719 for rec in mapTextureList do
1720 begin
1721 Inc(cnt);
1722 s := rec.Resource;
1723 {$IF DEFINED(D2F_DEBUG_TXLOAD)}
1724 e_WriteLog(Format(' Loading texture #%d: %s', [cnt, s]), MSG_NOTIFY);
1725 {$ENDIF}
1726 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1727 if rec.Anim then
1728 begin
1729 // Àíèìèðîâàííàÿ òåêñòóðà
1730 ntn := CreateAnimTexture(rec.Resource, FileName, True);
1731 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1732 end
1733 else
1734 begin
1735 // Îáû÷íàÿ òåêñòóðà
1736 ntn := CreateTexture(rec.Resource, FileName, True);
1737 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1738 end;
1739 if (ntn < 0) then ntn := CreateNullTexture(rec.Resource);
1741 rec.tagInt := ntn; // remember texture number
1742 g_Game_StepLoading();
1743 end;
1745 // set panel tagInt to texture index
1746 if (panels <> nil) then
1747 begin
1748 for rec in panels do
1749 begin
1750 texrec := rec.TextureRec;
1751 if (texrec = nil) then rec.tagInt := -1 else rec.tagInt := texrec.tagInt;
1752 end;
1753 end;
1754 end;
1756 // Çàãðóçêà òðèããåðîâ
1757 gTriggerClientID := 0;
1758 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1759 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1761 // Çàãðóçêà ïàíåëåé
1762 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1763 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1765 // check texture numbers for panels
1766 if (panels <> nil) and (panels.count > 0) then
1767 begin
1768 for rec in panels do
1769 begin
1770 if (rec.tagInt < 0) then
1771 begin
1772 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1773 result := false;
1774 exit;
1775 end;
1776 end;
1777 end;
1779 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì)
1780 if (triggers <> nil) and (triggers.count > 0) then
1781 begin
1782 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1783 //SetLength(TriggersTable, triggers.count);
1784 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], triggers.count-1, False);
1786 for rec in triggers do
1787 begin
1788 SetLength(TriggersTable, Length(TriggersTable)+1);
1789 pttit := @TriggersTable[High(TriggersTable)];
1790 pttit.trigrec := rec;
1791 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè)
1792 pttit.texPan := mapReader.panel[rec.TexturePanel];
1793 pttit.liftPan := nil;
1794 pttit.doorPan := nil;
1795 pttit.shotPan := nil;
1796 pttit.mplatPan := nil;
1797 pttit.texPanIdx := -1;
1798 pttit.LiftPanelIdx := -1;
1799 pttit.DoorPanelIdx := -1;
1800 pttit.ShotPanelIdx := -1;
1801 pttit.MPlatPanelIdx := -1;
1802 // Ëèôòû
1803 if rec.TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1804 begin
1805 pttit.liftPan := mapReader.panel[rec.trigRec.tgPanelID];
1806 end;
1807 // Äâåðè
1808 if rec.TriggerType in [TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1809 begin
1810 pttit.doorPan := mapReader.panel[rec.trigRec.tgPanelID];
1811 end;
1812 // Òóðåëü
1813 if (rec.TriggerType = TRIGGER_SHOT) then
1814 begin
1815 pttit.shotPan := mapReader.panel[rec.trigRec.tgShotPanelID];
1816 end;
1817 //
1818 if rec.TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1819 begin
1820 pttit.mplatPan := mapReader.panel[rec.trigRec.tgPanelID];
1821 end;
1823 if (pttit.texPan <> nil) then pttit.texPan.userPanelTrigRef := true;
1824 if (pttit.liftPan <> nil) then pttit.liftPan.userPanelTrigRef := true;
1825 if (pttit.doorPan <> nil) then pttit.doorPan.userPanelTrigRef := true;
1826 if (pttit.shotPan <> nil) then pttit.shotPan.userPanelTrigRef := true;
1827 if (pttit.mplatPan <> nil) then pttit.mplatPan.userPanelTrigRef := true;
1829 g_Game_StepLoading();
1830 end;
1831 end;
1833 // Ñîçäàåì ïàíåëè
1834 if (panels <> nil) and (panels.count > 0) then
1835 begin
1836 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1837 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], panels.count-1, False);
1839 pannum := -1;
1840 for rec in panels do
1841 begin
1842 Inc(pannum);
1843 //e_LogWritefln('PANSTART: pannum=%s', [pannum]);
1844 texrec := nil;
1845 SetLength(AddTextures, 0);
1846 trigRef := False;
1847 CurTex := -1;
1848 ok := false;
1850 if (mapTextureList <> nil) then
1851 begin
1852 texrec := rec.TextureRec;
1853 ok := (texrec <> nil);
1854 end;
1856 if ok then
1857 begin
1858 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1859 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð
1860 ok := false;
1861 if (TriggersTable <> nil) and (mapTextureList <> nil) then
1862 begin
1864 for b := 0 to High(TriggersTable) do
1865 begin
1866 if (TriggersTable[b].texPan = rec) or (TriggersTable[b].shotPan = rec) then
1867 begin
1868 trigRef := True;
1869 ok := True;
1870 break;
1871 end;
1872 end;
1874 if rec.userPanelTrigRef then
1875 begin
1876 // e_LogWritefln('trigref for panel %s', [pannum]);
1877 trigRef := True;
1878 ok := True;
1879 end;
1880 end;
1881 end;
1883 if ok then
1884 begin
1885 // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1886 s := texrec.Resource;
1888 // Ñïåö-òåêñòóðû çàïðåùåíû
1889 if g_Map_IsSpecialTexture(s) then
1890 begin
1891 ok := false
1892 end
1893 else
1894 begin
1895 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè
1896 ok := g_Texture_NumNameFindStart(s);
1897 end;
1899 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1900 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #
1901 if ok then
1902 begin
1903 k := NNF_NAME_BEFORE;
1904 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû
1905 while ok or (k = NNF_NAME_BEFORE) or (k = NNF_NAME_EQUALS) do
1906 begin
1907 k := g_Texture_NumNameFindNext(TexName);
1909 if (k = NNF_NAME_BEFORE) or (k = NNF_NAME_AFTER) then
1910 begin
1911 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó
1912 if texrec.Anim then
1913 begin
1914 // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1915 isAnim := True;
1916 //e_LogWritefln('000: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1917 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1918 //e_LogWritefln('001: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1919 if not ok then
1920 begin
1921 // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1922 isAnim := False;
1923 //e_LogWritefln('002: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1924 ok := CreateTexture(TexName, FileName, False) >= 0;
1925 //e_LogWritefln('003: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1926 end;
1927 end
1928 else
1929 begin
1930 // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1931 isAnim := False;
1932 //e_LogWritefln('004: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1933 ok := CreateTexture(TexName, FileName, False) >= 0;
1934 //e_LogWritefln('005: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1935 if not ok then
1936 begin
1937 // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1938 isAnim := True;
1939 //e_LogWritefln('006: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1940 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1941 //e_LogWritefln('007: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1942 end;
1943 end;
1945 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè
1946 if ok then
1947 begin
1949 for c := 0 to High(Textures) do
1950 begin
1951 if (Textures[c].TextureName = TexName) then
1952 begin
1953 SetLength(AddTextures, Length(AddTextures)+1);
1954 AddTextures[High(AddTextures)].Texture := c;
1955 AddTextures[High(AddTextures)].Anim := isAnim;
1956 break;
1957 end;
1958 end;
1960 if (TextNameHash <> nil) and TextNameHash.get(toLowerCase1251(TexName), c) then
1961 begin
1962 SetLength(AddTextures, Length(AddTextures)+1);
1963 AddTextures[High(AddTextures)].Texture := c;
1964 AddTextures[High(AddTextures)].Anim := isAnim;
1965 end;
1966 end;
1967 end
1968 else
1969 begin
1970 if k = NNF_NAME_EQUALS then
1971 begin
1972 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî
1973 SetLength(AddTextures, Length(AddTextures)+1);
1974 AddTextures[High(AddTextures)].Texture := rec.tagInt; // internal texture number, not map index
1975 AddTextures[High(AddTextures)].Anim := texrec.Anim;
1976 CurTex := High(AddTextures);
1977 ok := true;
1978 end
1979 else // NNF_NO_NAME
1980 begin
1981 ok := false;
1982 end;
1983 end;
1984 end; // while ok...
1986 ok := true;
1987 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1988 end; // if ok - ññûëàþòñÿ òðèããåðû
1990 if not ok then
1991 begin
1992 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó
1993 SetLength(AddTextures, 1);
1994 AddTextures[0].Texture := rec.tagInt; // internal texture number, not map index
1995 AddTextures[0].Anim := false;
1996 if (texrec <> nil) then AddTextures[0].Anim := texrec.Anim;
1997 CurTex := 0;
1998 end;
2000 //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);
2002 //e_LogWritefln('PANADD: pannum=%s', [pannum]);
2004 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå GUID
2005 PanelID := CreatePanel(rec, AddTextures, CurTex, trigRef);
2006 //e_LogWritefln('panel #%s of type %s got guid #%s', [pannum, rec.PanelType, PanelID]);
2007 rec.userPanelId := PanelID; // remember game panel id, we'll fix triggers later
2009 // setup lifts
2010 moveSpeed := rec.moveSpeed;
2011 //moveStart := rec.moveStart;
2012 //moveEnd := rec.moveEnd;
2013 //moveActive := rec['move_active'].varvalue;
2014 if not moveSpeed.isZero then
2015 begin
2016 SetLength(gMovingWallIds, Length(gMovingWallIds)+1);
2017 gMovingWallIds[High(gMovingWallIds)] := PanelID;
2018 //e_LogWritefln('found moving panel ''%s'' (idx=%s; id=%s)', [rec.id, pannum, PanelID]);
2019 end;
2021 //e_LogWritefln('PANEND: pannum=%s', [pannum]);
2023 g_Game_StepLoading();
2024 end;
2025 end;
2027 // ×èíèì ID'û ïàíåëåé, êîòîðûå èñïîëüçóþòñÿ â òðèããåðàõ
2028 for b := 0 to High(TriggersTable) do
2029 begin
2030 if (TriggersTable[b].texPan <> nil) then TriggersTable[b].texPanIdx := TriggersTable[b].texPan.userPanelId;
2031 if (TriggersTable[b].liftPan <> nil) then TriggersTable[b].LiftPanelIdx := TriggersTable[b].liftPan.userPanelId;
2032 if (TriggersTable[b].doorPan <> nil) then TriggersTable[b].DoorPanelIdx := TriggersTable[b].doorPan.userPanelId;
2033 if (TriggersTable[b].shotPan <> nil) then TriggersTable[b].ShotPanelIdx := TriggersTable[b].shotPan.userPanelId;
2034 if (TriggersTable[b].mplatPan <> nil) then TriggersTable[b].MPlatPanelIdx := TriggersTable[b].mplatPan.userPanelId;
2035 end;
2037 // create map grid, init other grids (for monsters, for example)
2038 e_WriteLog('Creating map grid', MSG_NOTIFY);
2039 mapCreateGrid();
2041 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû
2042 if (triggers <> nil) and (panels <> nil) and (not gLoadGameMode) then
2043 begin
2044 e_LogWritefln(' Creating triggers (%d)...', [triggers.count]);
2045 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
2046 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü
2047 trignum := -1;
2048 for rec in triggers do
2049 begin
2050 Inc(trignum);
2051 if (TriggersTable[trignum].texPan <> nil) then b := TriggersTable[trignum].texPan.PanelType else b := 0;
2052 if (TriggersTable[trignum].shotPan <> nil) then c := TriggersTable[trignum].shotPan.PanelType else c := 0;
2053 // we can have only one of those
2054 if (TriggersTable[trignum].LiftPanelIdx <> -1) then tgpid := TriggersTable[trignum].LiftPanelIdx
2055 else if (TriggersTable[trignum].DoorPanelIdx <> -1) then tgpid := TriggersTable[trignum].DoorPanelIdx
2056 else if (TriggersTable[trignum].ShotPanelIdx <> -1) then tgpid := TriggersTable[trignum].ShotPanelIdx
2057 else if (TriggersTable[trignum].MPlatPanelIdx <> -1) then tgpid := TriggersTable[trignum].MPlatPanelIdx
2058 else tgpid := -1;
2059 //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]);
2060 CreateTrigger(trignum, rec, TriggersTable[trignum].texPanIdx, tgpid, Word(b), Word(c));
2061 end;
2062 end;
2064 // Çàãðóçêà ïðåäìåòîâ
2065 e_WriteLog(' Loading items...', MSG_NOTIFY);
2066 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
2068 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû
2069 if (items <> nil) and not gLoadGameMode then
2070 begin
2071 e_WriteLog(' Spawning items...', MSG_NOTIFY);
2072 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
2073 for rec in items do CreateItem(rec);
2074 end;
2076 // Çàãðóçêà îáëàñòåé
2077 e_WriteLog(' Loading areas...', MSG_NOTIFY);
2078 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
2080 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè
2081 if areas <> nil then
2082 begin
2083 e_WriteLog(' Creating areas...', MSG_NOTIFY);
2084 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
2085 for rec in areas do CreateArea(rec);
2086 end;
2088 // Çàãðóçêà ìîíñòðîâ
2089 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
2090 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
2092 gTotalMonsters := 0;
2094 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ
2095 if (monsters <> nil) and not gLoadGameMode then
2096 begin
2097 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
2098 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
2099 for rec in monsters do CreateMonster(rec);
2100 end;
2102 gCurrentMap := mapReader; // this will be our current map now
2103 gCurrentMapFileName := Res;
2104 mapReader := nil;
2106 // Çàãðóçêà íåáà
2107 if gMapInfo.SkyName <> '' then
2108 begin
2109 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
2110 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
2111 FileName := g_ExtractWadName(gMapInfo.SkyName);
2113 if FileName <> '' then
2114 FileName := GameDir+'/wads/'+FileName
2115 else
2116 begin
2117 FileName := g_ExtractWadName(Res);
2118 end;
2120 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
2121 if g_Texture_CreateWAD(BackID, s) then
2122 begin
2123 g_Game_SetupScreenSize();
2124 end
2125 else
2126 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
2127 end;
2129 // Çàãðóçêà ìóçûêè
2130 ok := False;
2131 if gMapInfo.MusicName <> '' then
2132 begin
2133 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
2134 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
2135 FileName := g_ExtractWadName(gMapInfo.MusicName);
2137 if FileName <> '' then
2138 FileName := GameDir+'/wads/'+FileName
2139 else
2140 begin
2141 FileName := g_ExtractWadName(Res);
2142 end;
2144 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
2145 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
2146 ok := True
2147 else
2148 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
2149 end;
2151 // Îñòàëüíûå óñòàíâêè
2152 CreateDoorMap();
2153 CreateLiftMap();
2155 g_Items_Init();
2156 g_Weapon_Init();
2157 g_Monsters_Init();
2159 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
2160 if not gLoadGameMode then g_GFX_Init();
2162 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
2163 mapTextureList := nil;
2164 panels := nil;
2165 items := nil;
2166 areas := nil;
2167 triggers := nil;
2168 TriggersTable := nil;
2169 AddTextures := nil;
2171 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
2172 if ok and (not gLoadGameMode) then
2173 begin
2174 gMusic.SetByName(gMapInfo.MusicName);
2175 gMusic.Play();
2176 end
2177 else
2178 begin
2179 gMusic.SetByName('');
2180 end;
2182 stt := curTimeMicro()-stt;
2183 e_LogWritefln('map loaded in %s.%s milliseconds', [Integer(stt div 1000), Integer(stt mod 1000)]);
2184 finally
2185 sfsGCEnable(); // enable releasing unused volumes
2186 mapReader.Free();
2187 e_ClearInputBuffer(); // why not?
2188 end;
2190 e_WriteLog('Done loading map.', MSG_NOTIFY);
2191 Result := True;
2192 end;
2195 function g_Map_GetMapInfo(Res: String): TMapInfo;
2196 var
2197 WAD: TWADFile;
2198 mapReader: TDynRecord;
2199 //Header: TMapHeaderRec_1;
2200 FileName: String;
2201 Data: Pointer;
2202 Len: Integer;
2203 begin
2204 FillChar(Result, SizeOf(Result), 0);
2205 FileName := g_ExtractWadName(Res);
2207 WAD := TWADFile.Create();
2208 if not WAD.ReadFile(FileName) then
2209 begin
2210 WAD.Free();
2211 Exit;
2212 end;
2214 //k8: it ignores path again
2215 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
2216 begin
2217 WAD.Free();
2218 Exit;
2219 end;
2221 WAD.Free();
2223 try
2224 mapReader := g_Map_ParseMap(Data, Len);
2225 except
2226 mapReader := nil;
2227 FreeMem(Data);
2228 exit;
2229 end;
2231 FreeMem(Data);
2233 if (mapReader = nil) then exit;
2235 if (mapReader.Width > 0) and (mapReader.Height > 0) then
2236 begin
2237 Result.Name := mapReader.MapName;
2238 Result.Description := mapReader.MapDesc;
2239 Result.Map := Res;
2240 Result.Author := mapReader.MapAuthor;
2241 Result.Height := mapReader.Height;
2242 Result.Width := mapReader.Width;
2243 end
2244 else
2245 begin
2246 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2247 //ZeroMemory(@Header, SizeOf(Header));
2248 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2249 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2250 Result.Map := Res;
2251 Result.Author := '';
2252 Result.Height := 0;
2253 Result.Width := 0;
2254 end;
2256 mapReader.Free();
2257 end;
2259 function g_Map_GetMapsList(WADName: string): SArray;
2260 var
2261 WAD: TWADFile;
2262 a: Integer;
2263 ResList: SArray;
2264 begin
2265 Result := nil;
2266 WAD := TWADFile.Create();
2267 if not WAD.ReadFile(WADName) then
2268 begin
2269 WAD.Free();
2270 Exit;
2271 end;
2272 ResList := WAD.GetMapResources();
2273 if ResList <> nil then
2274 begin
2275 for a := 0 to High(ResList) do
2276 begin
2277 SetLength(Result, Length(Result)+1);
2278 Result[High(Result)] := ResList[a];
2279 end;
2280 end;
2281 WAD.Free();
2282 end;
2284 function g_Map_Exist(Res: string): Boolean;
2285 var
2286 WAD: TWADFile;
2287 FileName, mnn: string;
2288 ResList: SArray;
2289 a: Integer;
2290 begin
2291 Result := False;
2293 FileName := addWadExtension(g_ExtractWadName(Res));
2295 WAD := TWADFile.Create;
2296 if not WAD.ReadFile(FileName) then
2297 begin
2298 WAD.Free();
2299 Exit;
2300 end;
2302 ResList := WAD.GetMapResources();
2303 WAD.Free();
2305 mnn := g_ExtractFileName(Res);
2306 if ResList <> nil then
2307 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
2308 begin
2309 Result := True;
2310 Exit;
2311 end;
2312 end;
2314 procedure g_Map_Free(freeTextures: Boolean=true);
2315 var
2316 a: Integer;
2318 procedure FreePanelArray(var panels: TPanelArray);
2319 var
2320 i: Integer;
2322 begin
2323 if panels <> nil then
2324 begin
2325 for i := 0 to High(panels) do
2326 panels[i].Free();
2327 panels := nil;
2328 end;
2329 end;
2331 begin
2332 g_GFX_Free();
2333 g_Weapon_Free();
2334 g_Items_Free();
2335 g_Triggers_Free();
2336 g_Monsters_Free();
2338 RespawnPoints := nil;
2339 if FlagPoints[FLAG_RED] <> nil then
2340 begin
2341 Dispose(FlagPoints[FLAG_RED]);
2342 FlagPoints[FLAG_RED] := nil;
2343 end;
2344 if FlagPoints[FLAG_BLUE] <> nil then
2345 begin
2346 Dispose(FlagPoints[FLAG_BLUE]);
2347 FlagPoints[FLAG_BLUE] := nil;
2348 end;
2349 //DOMFlagPoints := nil;
2351 //gDOMFlags := nil;
2353 if (Length(gCurrentMapFileName) <> 0) then
2354 begin
2355 e_LogWritefln('g_Map_Free: previous map was ''%s''...', [gCurrentMapFileName]);
2356 end
2357 else
2358 begin
2359 e_LogWritefln('g_Map_Free: no previous map.', []);
2360 end;
2361 if freeTextures then
2362 begin
2363 e_LogWritefln('g_Map_Free: clearing textures...', []);
2364 if (Textures <> nil) then
2365 begin
2366 for a := 0 to High(Textures) do
2367 begin
2368 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
2369 begin
2370 if Textures[a].Anim then
2371 begin
2372 g_Frames_DeleteByID(Textures[a].FramesID)
2373 end
2374 else
2375 begin
2376 if (Textures[a].TextureID <> LongWord(TEXTURE_NONE)) then
2377 begin
2378 e_DeleteTexture(Textures[a].TextureID);
2379 end;
2380 end;
2381 end;
2382 end;
2383 Textures := nil;
2384 end;
2385 TextNameHash.Free();
2386 TextNameHash := nil;
2387 BadTextNameHash.Free();
2388 BadTextNameHash := nil;
2389 end;
2391 panByGUID := nil;
2393 FreePanelArray(gWalls);
2394 FreePanelArray(gRenderBackgrounds);
2395 FreePanelArray(gRenderForegrounds);
2396 FreePanelArray(gWater);
2397 FreePanelArray(gAcid1);
2398 FreePanelArray(gAcid2);
2399 FreePanelArray(gSteps);
2400 FreePanelArray(gLifts);
2401 FreePanelArray(gBlockMon);
2402 gMovingWallIds := nil;
2404 if BackID <> DWORD(-1) then
2405 begin
2406 gBackSize.X := 0;
2407 gBackSize.Y := 0;
2408 e_DeleteTexture(BackID);
2409 BackID := DWORD(-1);
2410 end;
2412 g_Game_StopAllSounds(False);
2413 gMusic.FreeSound();
2414 g_Sound_Delete(gMapInfo.MusicName);
2416 gMapInfo.Name := '';
2417 gMapInfo.Description := '';
2418 gMapInfo.MusicName := '';
2419 gMapInfo.Height := 0;
2420 gMapInfo.Width := 0;
2422 gDoorMap := nil;
2423 gLiftMap := nil;
2424 end;
2426 procedure g_Map_Update();
2427 var
2428 a, d, j: Integer;
2429 m: Word;
2430 s: String;
2432 procedure UpdatePanelArray(var panels: TPanelArray);
2433 var
2434 i: Integer;
2436 begin
2437 for i := 0 to High(panels) do panels[i].Update();
2438 end;
2440 begin
2441 if g_dbgpan_mplat_step then g_dbgpan_mplat_active := true;
2443 UpdatePanelArray(gWalls);
2444 UpdatePanelArray(gRenderBackgrounds);
2445 UpdatePanelArray(gRenderForegrounds);
2446 UpdatePanelArray(gWater);
2447 UpdatePanelArray(gAcid1);
2448 UpdatePanelArray(gAcid2);
2449 UpdatePanelArray(gSteps);
2451 if g_dbgpan_mplat_step then begin g_dbgpan_mplat_step := false; g_dbgpan_mplat_active := false; end;
2453 if gGameSettings.GameMode = GM_CTF then
2454 begin
2455 for a := FLAG_RED to FLAG_BLUE do
2456 begin
2457 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2458 begin
2459 with gFlags[a] do
2460 begin
2461 if gFlags[a].Animation <> nil then gFlags[a].Animation.Update();
2463 m := g_Obj_Move(@Obj, True, True);
2465 if gTime mod (GAME_TICK*2) <> 0 then Continue;
2467 // Ñîïðîòèâëåíèå âîçäóõà
2468 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2470 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó
2471 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2472 begin
2473 g_Map_ResetFlag(a);
2474 gFlags[a].CaptureTime := 0;
2475 if a = FLAG_RED then
2476 s := _lc[I_PLAYER_FLAG_RED]
2477 else
2478 s := _lc[I_PLAYER_FLAG_BLUE];
2479 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2481 if g_Game_IsNet then
2482 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2483 Continue;
2484 end;
2486 if Count > 0 then Count -= 1;
2488 // Èãðîê áåðåò ôëàã
2489 if gPlayers <> nil then
2490 begin
2491 j := Random(Length(gPlayers)) - 1;
2492 for d := 0 to High(gPlayers) do
2493 begin
2494 Inc(j);
2495 if j > High(gPlayers) then j := 0;
2496 if gPlayers[j] <> nil then
2497 begin
2498 if gPlayers[j].alive and g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2499 begin
2500 if gPlayers[j].GetFlag(a) then Break;
2501 end;
2502 end;
2503 end;
2504 end;
2505 end;
2506 end;
2507 end;
2508 end;
2509 end;
2512 // old algo
2513 procedure g_Map_DrawPanels (PanelType: Word);
2515 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2516 var
2517 idx: Integer;
2518 begin
2519 if (panels <> nil) then
2520 begin
2521 // alas, no visible set
2522 for idx := 0 to High(panels) do
2523 begin
2524 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
2525 end;
2526 end;
2527 end;
2529 begin
2530 case PanelType of
2531 PANEL_WALL: DrawPanels(gWalls);
2532 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2533 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2534 PANEL_FORE: DrawPanels(gRenderForegrounds);
2535 PANEL_WATER: DrawPanels(gWater);
2536 PANEL_ACID1: DrawPanels(gAcid1);
2537 PANEL_ACID2: DrawPanels(gAcid2);
2538 PANEL_STEP: DrawPanels(gSteps);
2539 end;
2540 end;
2543 // new algo
2544 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2546 function checker (pan: TPanel; tag: Integer): Boolean;
2547 begin
2548 result := false; // don't stop, ever
2549 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2550 gDrawPanelList.insert(pan);
2551 end;
2553 begin
2554 dplClear();
2555 //tagmask := panelTypeToTag(PanelType);
2556 mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask);
2557 // list will be rendered in `g_game.DrawPlayer()`
2558 end;
2561 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2563 function checker (pan: TPanel; tag: Integer): Boolean;
2564 begin
2565 result := false; // don't stop, ever
2566 pan.DrawShadowVolume(lightX, lightY, radius);
2567 end;
2569 begin
2570 mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2571 end;
2574 procedure g_Map_DrawBack(dx, dy: Integer);
2575 begin
2576 if gDrawBackGround and (BackID <> DWORD(-1)) then
2577 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2578 else
2579 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2580 end;
2582 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2583 PanelType: Word; b1x3: Boolean=false): Boolean;
2584 var
2585 a, h: Integer;
2586 begin
2587 Result := False;
2589 if WordBool(PanelType and PANEL_WALL) then
2590 if gWalls <> nil then
2591 begin
2592 h := High(gWalls);
2594 for a := 0 to h do
2595 if gWalls[a].Enabled and
2596 g_Collide(X, Y, Width, Height,
2597 gWalls[a].X, gWalls[a].Y,
2598 gWalls[a].Width, gWalls[a].Height) then
2599 begin
2600 Result := True;
2601 Exit;
2602 end;
2603 end;
2605 if WordBool(PanelType and PANEL_WATER) then
2606 if gWater <> nil then
2607 begin
2608 h := High(gWater);
2610 for a := 0 to h do
2611 if g_Collide(X, Y, Width, Height,
2612 gWater[a].X, gWater[a].Y,
2613 gWater[a].Width, gWater[a].Height) then
2614 begin
2615 Result := True;
2616 Exit;
2617 end;
2618 end;
2620 if WordBool(PanelType and PANEL_ACID1) then
2621 if gAcid1 <> nil then
2622 begin
2623 h := High(gAcid1);
2625 for a := 0 to h do
2626 if g_Collide(X, Y, Width, Height,
2627 gAcid1[a].X, gAcid1[a].Y,
2628 gAcid1[a].Width, gAcid1[a].Height) then
2629 begin
2630 Result := True;
2631 Exit;
2632 end;
2633 end;
2635 if WordBool(PanelType and PANEL_ACID2) then
2636 if gAcid2 <> nil then
2637 begin
2638 h := High(gAcid2);
2640 for a := 0 to h do
2641 if g_Collide(X, Y, Width, Height,
2642 gAcid2[a].X, gAcid2[a].Y,
2643 gAcid2[a].Width, gAcid2[a].Height) then
2644 begin
2645 Result := True;
2646 Exit;
2647 end;
2648 end;
2650 if WordBool(PanelType and PANEL_STEP) then
2651 if gSteps <> nil then
2652 begin
2653 h := High(gSteps);
2655 for a := 0 to h do
2656 if g_Collide(X, Y, Width, Height,
2657 gSteps[a].X, gSteps[a].Y,
2658 gSteps[a].Width, gSteps[a].Height) then
2659 begin
2660 Result := True;
2661 Exit;
2662 end;
2663 end;
2665 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2666 if gLifts <> nil then
2667 begin
2668 h := High(gLifts);
2670 for a := 0 to h do
2671 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2672 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2673 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2674 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2675 g_Collide(X, Y, Width, Height,
2676 gLifts[a].X, gLifts[a].Y,
2677 gLifts[a].Width, gLifts[a].Height) then
2678 begin
2679 Result := True;
2680 Exit;
2681 end;
2682 end;
2684 if WordBool(PanelType and PANEL_BLOCKMON) then
2685 if gBlockMon <> nil then
2686 begin
2687 h := High(gBlockMon);
2689 for a := 0 to h do
2690 if ( (not b1x3) or
2691 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2692 g_Collide(X, Y, Width, Height,
2693 gBlockMon[a].X, gBlockMon[a].Y,
2694 gBlockMon[a].Width, gBlockMon[a].Height) then
2695 begin
2696 Result := True;
2697 Exit;
2698 end;
2699 end;
2700 end;
2702 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2703 var
2704 texid: DWORD;
2706 function checkPanels (constref panels: TPanelArray): Boolean;
2707 var
2708 a: Integer;
2709 begin
2710 result := false;
2711 if panels = nil then exit;
2712 for a := 0 to High(panels) do
2713 begin
2714 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2715 begin
2716 result := true;
2717 texid := panels[a].GetTextureID();
2718 exit;
2719 end;
2720 end;
2721 end;
2723 begin
2724 texid := LongWord(TEXTURE_NONE);
2725 result := texid;
2726 if not checkPanels(gWater) then
2727 if not checkPanels(gAcid1) then
2728 if not checkPanels(gAcid2) then exit;
2729 result := texid;
2730 end;
2733 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2734 const
2735 SlowMask = GridTagLift or GridTagBlockMon;
2736 function checker (pan: TPanel; tag: Integer): Boolean;
2737 begin
2739 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2740 begin
2741 result := pan.Enabled;
2742 exit;
2743 end;
2746 if ((tag and GridTagLift) <> 0) then
2747 begin
2748 result :=
2749 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2750 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2751 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2752 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
2753 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
2754 exit;
2755 end;
2757 if ((tag and GridTagBlockMon) <> 0) then
2758 begin
2759 result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2760 exit;
2761 end;
2763 // other shit
2764 //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2765 result := true; // i found her!
2766 end;
2768 var
2769 tagmask: Integer = 0;
2770 begin
2771 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2772 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2773 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2774 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2775 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2776 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2777 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2779 if (tagmask = 0) then begin result := false; exit; end; // just in case
2781 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2782 if gdbg_map_use_accel_coldet then
2783 begin
2784 if (Width = 1) and (Height = 1) then
2785 begin
2786 if ((tagmask and SlowMask) <> 0) then
2787 begin
2788 // slow
2789 result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil);
2790 end
2791 else
2792 begin
2793 // fast
2794 result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil);
2795 end;
2796 end
2797 else
2798 begin
2799 if ((tagmask and SlowMask) <> 0) then
2800 begin
2801 // slow
2802 result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil);
2803 end
2804 else
2805 begin
2806 // fast
2807 result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil);
2808 end;
2809 end;
2810 end
2811 else
2812 begin
2813 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2814 end;
2815 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2816 end;
2819 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2820 var
2821 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2822 texid: DWORD;
2824 // slightly different from the old code, but meh...
2825 function checker (pan: TPanel; tag: Integer): Boolean;
2826 begin
2827 result := false; // don't stop, ever
2828 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2829 // check priorities
2830 case cctype of
2831 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2832 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2833 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2834 end;
2835 // collision?
2836 //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2837 // yeah
2838 texid := pan.GetTextureID();
2839 // water? water has the highest priority, so stop right here
2840 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2841 // acid2?
2842 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2843 // acid1?
2844 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2845 end;
2847 begin
2848 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2849 if gdbg_map_use_accel_coldet then
2850 begin
2851 texid := LongWord(TEXTURE_NONE);
2852 if (Width = 1) and (Height = 1) then
2853 begin
2854 mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2855 end
2856 else
2857 begin
2858 mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2859 end;
2860 result := texid;
2861 end
2862 else
2863 begin
2864 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2865 end;
2866 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2867 end;
2870 procedure g_Map_EnableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_EnableWallGUID(gWalls[ID].guid); end;
2871 procedure g_Map_DisableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_DisableWallGUID(gWalls[ID].guid); end;
2872 procedure g_Map_SetLift_XXX (ID: DWORD; t: Integer); begin if (ID < Length(gLifts)) then g_Map_SetLiftGUID(gLifts[ID].guid, t); end;
2875 procedure g_Map_EnableWallGUID (pguid: Integer);
2876 var
2877 pan: TPanel;
2878 begin
2879 //pan := gWalls[ID];
2880 pan := g_Map_PanelByGUID(pguid);
2881 if (pan = nil) then exit;
2882 pan.Enabled := True;
2883 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, true);
2885 mapGrid.proxyEnabled[pan.proxyId] := true;
2886 //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
2887 //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
2889 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState({gWalls[ID]}pan.PanelType, pguid);
2891 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2892 //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);
2893 {$ENDIF}
2894 end;
2897 procedure g_Map_DisableWallGUID (pguid: Integer);
2898 var
2899 pan: TPanel;
2900 begin
2901 //pan := gWalls[ID];
2902 pan := g_Map_PanelByGUID(pguid);
2903 if (pan = nil) then exit;
2904 pan.Enabled := False;
2905 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, false);
2907 mapGrid.proxyEnabled[pan.proxyId] := false;
2908 //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
2910 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, pguid);
2912 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2913 //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);
2914 {$ENDIF}
2915 end;
2918 procedure g_Map_SwitchTextureGUID (PanelType: Word; pguid: Integer; AnimLoop: Byte = 0);
2919 var
2920 tp: TPanel;
2921 begin
2922 tp := g_Map_PanelByGUID(pguid);
2923 if (tp = nil) then exit;
2925 case PanelType of
2926 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: tp := gWalls[ID];
2927 PANEL_FORE: tp := gRenderForegrounds[ID];
2928 PANEL_BACK: tp := gRenderBackgrounds[ID];
2929 PANEL_WATER: tp := gWater[ID];
2930 PANEL_ACID1: tp := gAcid1[ID];
2931 PANEL_ACID2: tp := gAcid2[ID];
2932 PANEL_STEP: tp := gSteps[ID];
2933 else exit;
2934 end;
2937 tp.NextTexture(AnimLoop);
2938 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelTexture(PanelType, pguid, AnimLoop);
2939 end;
2942 procedure g_Map_SetLiftGUID (pguid: Integer; t: Integer);
2943 var
2944 pan: TPanel;
2945 begin
2946 //pan := gLifts[ID];
2947 pan := g_Map_PanelByGUID(pguid);
2948 if (pan = nil) then exit;
2949 if not pan.isGLift then exit;
2951 if ({gLifts[ID]}pan.LiftType = t) then exit; //!FIXME!TRIGANY!
2953 with {gLifts[ID]} pan do
2954 begin
2955 LiftType := t;
2957 g_Mark(X, Y, Width, Height, MARK_LIFT, false);
2958 //TODO: make separate lift tags, and change tag here
2960 case LiftType of
2961 0: g_Mark(X, Y, Width, Height, MARK_LIFTUP);
2962 1: g_Mark(X, Y, Width, Height, MARK_LIFTDOWN);
2963 2: g_Mark(X, Y, Width, Height, MARK_LIFTLEFT);
2964 3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT);
2965 end;
2967 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, pguid);
2968 end;
2969 end;
2972 function g_Map_GetPoint (PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2973 var
2974 a: Integer;
2975 PointsArray: Array of TRespawnPoint;
2976 begin
2977 Result := False;
2978 SetLength(PointsArray, 0);
2980 if RespawnPoints = nil then
2981 Exit;
2983 for a := 0 to High(RespawnPoints) do
2984 if RespawnPoints[a].PointType = PointType then
2985 begin
2986 SetLength(PointsArray, Length(PointsArray)+1);
2987 PointsArray[High(PointsArray)] := RespawnPoints[a];
2988 end;
2990 if PointsArray = nil then
2991 Exit;
2993 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2994 Result := True;
2995 end;
2997 function g_Map_GetPointCount(PointType: Byte): Word;
2998 var
2999 a: Integer;
3000 begin
3001 Result := 0;
3003 if RespawnPoints = nil then
3004 Exit;
3006 for a := 0 to High(RespawnPoints) do
3007 if RespawnPoints[a].PointType = PointType then
3008 Result := Result + 1;
3009 end;
3011 function g_Map_HaveFlagPoints(): Boolean;
3012 begin
3013 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
3014 end;
3016 procedure g_Map_ResetFlag(Flag: Byte);
3017 begin
3018 with gFlags[Flag] do
3019 begin
3020 Obj.X := -1000;
3021 Obj.Y := -1000;
3022 Obj.Vel.X := 0;
3023 Obj.Vel.Y := 0;
3024 Direction := D_LEFT;
3025 State := FLAG_STATE_NONE;
3026 if FlagPoints[Flag] <> nil then
3027 begin
3028 Obj.X := FlagPoints[Flag]^.X;
3029 Obj.Y := FlagPoints[Flag]^.Y;
3030 Direction := FlagPoints[Flag]^.Direction;
3031 State := FLAG_STATE_NORMAL;
3032 end;
3033 Count := -1;
3034 end;
3035 end;
3037 procedure g_Map_DrawFlags();
3038 var
3039 i, dx: Integer;
3040 Mirror: TMirrorType;
3041 begin
3042 if gGameSettings.GameMode <> GM_CTF then
3043 Exit;
3045 for i := FLAG_RED to FLAG_BLUE do
3046 with gFlags[i] do
3047 if State <> FLAG_STATE_CAPTURED then
3048 begin
3049 if State = FLAG_STATE_NONE then
3050 continue;
3052 if Direction = D_LEFT then
3053 begin
3054 Mirror := M_HORIZONTAL;
3055 dx := -1;
3056 end
3057 else
3058 begin
3059 Mirror := M_NONE;
3060 dx := 1;
3061 end;
3063 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
3065 if g_debug_Frames then
3066 begin
3067 e_DrawQuad(Obj.X+Obj.Rect.X,
3068 Obj.Y+Obj.Rect.Y,
3069 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
3070 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
3071 0, 255, 0);
3072 end;
3073 end;
3074 end;
3077 procedure g_Map_SaveState (var Mem: TBinMemoryWriter);
3078 var
3079 dw: DWORD;
3080 b: Byte;
3081 str: String;
3082 boo: Boolean;
3084 procedure savePanels ();
3085 var
3086 PAMem: TBinMemoryWriter;
3087 pan: TPanel;
3088 begin
3089 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé
3090 PAMem := TBinMemoryWriter.Create((Length(panByGUID)+1) * 40);
3092 for pan in panByGUID do
3093 begin
3094 if true{pan.SaveIt} then
3095 begin
3096 // ID ïàíåëè
3097 //PAMem.WriteInt(i);
3098 // Ñîõðàíÿåì ïàíåëü
3099 pan.SaveState(PAMem);
3100 end;
3101 end;
3103 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé
3104 PAMem.SaveToMemory(Mem);
3105 PAMem.Free();
3106 end;
3108 procedure SaveFlag (flag: PFlag);
3109 begin
3110 // Ñèãíàòóðà ôëàãà
3111 dw := FLAG_SIGNATURE; // 'FLAG'
3112 Mem.WriteDWORD(dw);
3113 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3114 Mem.WriteByte(flag^.RespawnType);
3115 // Ñîñòîÿíèå ôëàãà
3116 Mem.WriteByte(flag^.State);
3117 // Íàïðàâëåíèå ôëàãà
3118 if flag^.Direction = D_LEFT then b := 1 else b := 2; // D_RIGHT
3119 Mem.WriteByte(b);
3120 // Îáúåêò ôëàãà
3121 Obj_SaveState(@flag^.Obj, Mem);
3122 end;
3124 begin
3125 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
3127 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
3128 savePanels();
3129 ///// /////
3131 ///// Ñîõðàíÿåì ìóçûêó: /////
3132 // Ñèãíàòóðà ìóçûêè:
3133 dw := MUSIC_SIGNATURE; // 'MUSI'
3134 Mem.WriteDWORD(dw);
3135 // Íàçâàíèå ìóçûêè:
3136 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
3137 if gMusic.NoMusic then str := '' else str := gMusic.Name;
3138 Mem.WriteString(str, 64);
3139 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3140 dw := gMusic.GetPosition();
3141 Mem.WriteDWORD(dw);
3142 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3143 boo := gMusic.SpecPause;
3144 Mem.WriteBoolean(boo);
3145 ///// /////
3147 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
3148 Mem.WriteInt(gTotalMonsters);
3149 ///// /////
3151 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
3152 if gGameSettings.GameMode = GM_CTF then
3153 begin
3154 // Ôëàã Êðàñíîé êîìàíäû
3155 SaveFlag(@gFlags[FLAG_RED]);
3156 // Ôëàã Ñèíåé êîìàíäû
3157 SaveFlag(@gFlags[FLAG_BLUE]);
3158 end;
3159 ///// /////
3161 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3162 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3163 begin
3164 // Î÷êè Êðàñíîé êîìàíäû
3165 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
3166 // Î÷êè Ñèíåé êîìàíäû
3167 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
3168 end;
3169 ///// /////
3170 end;
3173 procedure g_Map_LoadState (var Mem: TBinMemoryReader);
3174 var
3175 dw: DWORD;
3176 b: Byte;
3177 str: String;
3178 boo: Boolean;
3180 procedure loadPanels ();
3181 var
3182 PAMem: TBinMemoryReader;
3183 pan: TPanel;
3184 begin
3185 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé
3186 PAMem := TBinMemoryReader.Create();
3187 PAMem.LoadFromMemory(Mem);
3189 for pan in panByGUID do
3190 begin
3191 if true{pan.SaveIt} then
3192 begin
3193 // ID ïàíåëè:
3194 //PAMem.ReadInt(id);
3196 if id <> i then raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
3198 // Çàãðóæàåì ïàíåëü
3199 pan.LoadState(PAMem);
3200 //if (panels[i].arrIdx <> i) then raise Exception.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel arrIdx');
3201 if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
3202 end;
3203 end;
3205 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí
3206 PAMem.Free();
3207 end;
3209 procedure LoadFlag(flag: PFlag);
3210 begin
3211 // Ñèãíàòóðà ôëàãà
3212 Mem.ReadDWORD(dw);
3213 // 'FLAG'
3214 if dw <> FLAG_SIGNATURE then raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
3215 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3216 Mem.ReadByte(flag^.RespawnType);
3217 // Ñîñòîÿíèå ôëàãà
3218 Mem.ReadByte(flag^.State);
3219 // Íàïðàâëåíèå ôëàãà
3220 Mem.ReadByte(b);
3221 if b = 1 then flag^.Direction := D_LEFT else flag^.Direction := D_RIGHT; // b = 2
3222 // Îáúåêò ôëàãà
3223 Obj_LoadState(@flag^.Obj, Mem);
3224 end;
3226 begin
3227 if Mem = nil then Exit;
3229 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
3230 loadPanels();
3231 ///// /////
3233 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó
3234 g_GFX_Init();
3235 //mapCreateGrid();
3237 ///// Çàãðóæàåì ìóçûêó: /////
3238 // Ñèãíàòóðà ìóçûêè
3239 Mem.ReadDWORD(dw);
3240 // 'MUSI'
3241 if dw <> MUSIC_SIGNATURE then raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
3242 // Íàçâàíèå ìóçûêè
3243 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
3244 Mem.ReadString(str);
3245 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3246 Mem.ReadDWORD(dw);
3247 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3248 Mem.ReadBoolean(boo);
3249 // Çàïóñêàåì ýòó ìóçûêó
3250 gMusic.SetByName(str);
3251 gMusic.SpecPause := boo;
3252 gMusic.Play();
3253 gMusic.Pause(True);
3254 gMusic.SetPosition(dw);
3255 ///// /////
3257 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
3258 Mem.ReadInt(gTotalMonsters);
3259 ///// /////
3261 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
3262 if gGameSettings.GameMode = GM_CTF then
3263 begin
3264 // Ôëàã Êðàñíîé êîìàíäû
3265 LoadFlag(@gFlags[FLAG_RED]);
3266 // Ôëàã Ñèíåé êîìàíäû
3267 LoadFlag(@gFlags[FLAG_BLUE]);
3268 end;
3269 ///// /////
3271 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3272 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3273 begin
3274 // Î÷êè Êðàñíîé êîìàíäû
3275 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
3276 // Î÷êè Ñèíåé êîìàíäû
3277 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
3278 end;
3279 ///// /////
3280 end;
3283 // trace liquid, stepping by `dx` and `dy`
3284 // return last seen liquid coords, and `false` if we're started outside of the liquid
3285 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
3286 const
3287 MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
3288 begin
3289 topx := x;
3290 topy := y;
3291 // started outside of the liquid?
3292 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end;
3293 if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check
3294 result := true;
3295 while true do
3296 begin
3297 Inc(x, dx);
3298 Inc(y, dy);
3299 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit
3300 topx := x;
3301 topy := y;
3302 end;
3303 end;
3306 end.