DEADSOFTWARE

Holmes: option to highlight panel cells in grid
[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.line, pr.col, 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 generateExternalResourcesList(mapReader);
1681 mapTextureList := mapReader['texture'];
1682 // get all other lists here too
1683 panels := mapReader['panel'];
1684 triggers := mapReader['trigger'];
1685 items := mapReader['item'];
1686 areas := mapReader['area'];
1687 monsters := mapReader['monster'];
1689 // Çàãðóçêà îïèñàíèÿ êàðòû:
1690 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1691 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1693 with gMapInfo do
1694 begin
1695 Name := mapReader.MapName;
1696 Description := mapReader.MapDesc;
1697 Author := mapReader.MapAuthor;
1698 MusicName := mapReader.MusicName;
1699 SkyName := mapReader.SkyName;
1700 Height := mapReader.Height;
1701 Width := mapReader.Width;
1702 end;
1704 // Çàãðóçêà òåêñòóð:
1705 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1706 // Äîáàâëåíèå òåêñòóð â Textures[]:
1707 if (mapTextureList <> nil) and (mapTextureList.count > 0) then
1708 begin
1709 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1710 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], mapTextureList.count-1, False);
1712 cnt := -1;
1713 for rec in mapTextureList do
1714 begin
1715 Inc(cnt);
1716 s := rec.Resource;
1717 {$IF DEFINED(D2F_DEBUG_TXLOAD)}
1718 e_WriteLog(Format(' Loading texture #%d: %s', [cnt, s]), MSG_NOTIFY);
1719 {$ENDIF}
1720 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1721 if rec.Anim then
1722 begin
1723 // Àíèìèðîâàííàÿ òåêñòóðà
1724 ntn := CreateAnimTexture(rec.Resource, FileName, True);
1725 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1726 end
1727 else
1728 begin
1729 // Îáû÷íàÿ òåêñòóðà
1730 ntn := CreateTexture(rec.Resource, FileName, True);
1731 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1732 end;
1733 if (ntn < 0) then ntn := CreateNullTexture(rec.Resource);
1735 rec.tagInt := ntn; // remember texture number
1736 g_Game_StepLoading();
1737 end;
1739 // set panel tagInt to texture index
1740 if (panels <> nil) then
1741 begin
1742 for rec in panels do
1743 begin
1744 texrec := rec.TextureRec;
1745 if (texrec = nil) then rec.tagInt := -1 else rec.tagInt := texrec.tagInt;
1746 end;
1747 end;
1748 end;
1750 // Çàãðóçêà òðèããåðîâ
1751 gTriggerClientID := 0;
1752 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1753 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1755 // Çàãðóçêà ïàíåëåé
1756 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1757 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1759 // check texture numbers for panels
1760 if (panels <> nil) and (panels.count > 0) then
1761 begin
1762 for rec in panels do
1763 begin
1764 if (rec.tagInt < 0) then
1765 begin
1766 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1767 result := false;
1768 exit;
1769 end;
1770 end;
1771 end;
1773 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì)
1774 if (triggers <> nil) and (triggers.count > 0) then
1775 begin
1776 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1777 //SetLength(TriggersTable, triggers.count);
1778 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], triggers.count-1, False);
1780 for rec in triggers do
1781 begin
1782 SetLength(TriggersTable, Length(TriggersTable)+1);
1783 pttit := @TriggersTable[High(TriggersTable)];
1784 pttit.trigrec := rec;
1785 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè)
1786 pttit.texPan := mapReader.panel[rec.TexturePanel];
1787 pttit.liftPan := nil;
1788 pttit.doorPan := nil;
1789 pttit.shotPan := nil;
1790 pttit.mplatPan := nil;
1791 pttit.texPanIdx := -1;
1792 pttit.LiftPanelIdx := -1;
1793 pttit.DoorPanelIdx := -1;
1794 pttit.ShotPanelIdx := -1;
1795 pttit.MPlatPanelIdx := -1;
1796 // Ëèôòû
1797 if rec.TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1798 begin
1799 pttit.liftPan := mapReader.panel[rec.trigRec.tgPanelID];
1800 end;
1801 // Äâåðè
1802 if rec.TriggerType in [TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1803 begin
1804 pttit.doorPan := mapReader.panel[rec.trigRec.tgPanelID];
1805 end;
1806 // Òóðåëü
1807 if (rec.TriggerType = TRIGGER_SHOT) then
1808 begin
1809 pttit.shotPan := mapReader.panel[rec.trigRec.tgShotPanelID];
1810 end;
1811 //
1812 if rec.TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1813 begin
1814 pttit.mplatPan := mapReader.panel[rec.trigRec.tgPanelID];
1815 end;
1817 if (pttit.texPan <> nil) then pttit.texPan.userPanelTrigRef := true;
1818 if (pttit.liftPan <> nil) then pttit.liftPan.userPanelTrigRef := true;
1819 if (pttit.doorPan <> nil) then pttit.doorPan.userPanelTrigRef := true;
1820 if (pttit.shotPan <> nil) then pttit.shotPan.userPanelTrigRef := true;
1821 if (pttit.mplatPan <> nil) then pttit.mplatPan.userPanelTrigRef := true;
1823 g_Game_StepLoading();
1824 end;
1825 end;
1827 // Ñîçäàåì ïàíåëè
1828 if (panels <> nil) and (panels.count > 0) then
1829 begin
1830 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1831 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], panels.count-1, False);
1833 pannum := -1;
1834 for rec in panels do
1835 begin
1836 Inc(pannum);
1837 //e_LogWritefln('PANSTART: pannum=%s', [pannum]);
1838 texrec := nil;
1839 SetLength(AddTextures, 0);
1840 trigRef := False;
1841 CurTex := -1;
1842 ok := false;
1844 if (mapTextureList <> nil) then
1845 begin
1846 texrec := rec.TextureRec;
1847 ok := (texrec <> nil);
1848 end;
1850 if ok then
1851 begin
1852 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1853 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð
1854 ok := false;
1855 if (TriggersTable <> nil) and (mapTextureList <> nil) then
1856 begin
1858 for b := 0 to High(TriggersTable) do
1859 begin
1860 if (TriggersTable[b].texPan = rec) or (TriggersTable[b].shotPan = rec) then
1861 begin
1862 trigRef := True;
1863 ok := True;
1864 break;
1865 end;
1866 end;
1868 if rec.userPanelTrigRef then
1869 begin
1870 // e_LogWritefln('trigref for panel %s', [pannum]);
1871 trigRef := True;
1872 ok := True;
1873 end;
1874 end;
1875 end;
1877 if ok then
1878 begin
1879 // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1880 s := texrec.Resource;
1882 // Ñïåö-òåêñòóðû çàïðåùåíû
1883 if g_Map_IsSpecialTexture(s) then
1884 begin
1885 ok := false
1886 end
1887 else
1888 begin
1889 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè
1890 ok := g_Texture_NumNameFindStart(s);
1891 end;
1893 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1894 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #
1895 if ok then
1896 begin
1897 k := NNF_NAME_BEFORE;
1898 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû
1899 while ok or (k = NNF_NAME_BEFORE) or (k = NNF_NAME_EQUALS) do
1900 begin
1901 k := g_Texture_NumNameFindNext(TexName);
1903 if (k = NNF_NAME_BEFORE) or (k = NNF_NAME_AFTER) then
1904 begin
1905 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó
1906 if texrec.Anim then
1907 begin
1908 // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1909 isAnim := True;
1910 //e_LogWritefln('000: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1911 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1912 //e_LogWritefln('001: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1913 if not ok then
1914 begin
1915 // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1916 isAnim := False;
1917 //e_LogWritefln('002: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1918 ok := CreateTexture(TexName, FileName, False) >= 0;
1919 //e_LogWritefln('003: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1920 end;
1921 end
1922 else
1923 begin
1924 // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1925 isAnim := False;
1926 //e_LogWritefln('004: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1927 ok := CreateTexture(TexName, FileName, False) >= 0;
1928 //e_LogWritefln('005: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1929 if not ok then
1930 begin
1931 // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1932 isAnim := True;
1933 //e_LogWritefln('006: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1934 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1935 //e_LogWritefln('007: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1936 end;
1937 end;
1939 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè
1940 if ok then
1941 begin
1943 for c := 0 to High(Textures) do
1944 begin
1945 if (Textures[c].TextureName = TexName) then
1946 begin
1947 SetLength(AddTextures, Length(AddTextures)+1);
1948 AddTextures[High(AddTextures)].Texture := c;
1949 AddTextures[High(AddTextures)].Anim := isAnim;
1950 break;
1951 end;
1952 end;
1954 if (TextNameHash <> nil) and TextNameHash.get(toLowerCase1251(TexName), c) then
1955 begin
1956 SetLength(AddTextures, Length(AddTextures)+1);
1957 AddTextures[High(AddTextures)].Texture := c;
1958 AddTextures[High(AddTextures)].Anim := isAnim;
1959 end;
1960 end;
1961 end
1962 else
1963 begin
1964 if k = NNF_NAME_EQUALS then
1965 begin
1966 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî
1967 SetLength(AddTextures, Length(AddTextures)+1);
1968 AddTextures[High(AddTextures)].Texture := rec.tagInt; // internal texture number, not map index
1969 AddTextures[High(AddTextures)].Anim := texrec.Anim;
1970 CurTex := High(AddTextures);
1971 ok := true;
1972 end
1973 else // NNF_NO_NAME
1974 begin
1975 ok := false;
1976 end;
1977 end;
1978 end; // while ok...
1980 ok := true;
1981 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1982 end; // if ok - ññûëàþòñÿ òðèããåðû
1984 if not ok then
1985 begin
1986 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó
1987 SetLength(AddTextures, 1);
1988 AddTextures[0].Texture := rec.tagInt; // internal texture number, not map index
1989 AddTextures[0].Anim := false;
1990 if (texrec <> nil) then AddTextures[0].Anim := texrec.Anim;
1991 CurTex := 0;
1992 end;
1994 //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);
1996 //e_LogWritefln('PANADD: pannum=%s', [pannum]);
1998 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå GUID
1999 PanelID := CreatePanel(rec, AddTextures, CurTex, trigRef);
2000 //e_LogWritefln('panel #%s of type %s got guid #%s', [pannum, rec.PanelType, PanelID]);
2001 rec.userPanelId := PanelID; // remember game panel id, we'll fix triggers later
2003 // setup lifts
2004 moveSpeed := rec.moveSpeed;
2005 //moveStart := rec.moveStart;
2006 //moveEnd := rec.moveEnd;
2007 //moveActive := rec['move_active'].varvalue;
2008 if not moveSpeed.isZero then
2009 begin
2010 SetLength(gMovingWallIds, Length(gMovingWallIds)+1);
2011 gMovingWallIds[High(gMovingWallIds)] := PanelID;
2012 //e_LogWritefln('found moving panel ''%s'' (idx=%s; id=%s)', [rec.id, pannum, PanelID]);
2013 end;
2015 //e_LogWritefln('PANEND: pannum=%s', [pannum]);
2017 g_Game_StepLoading();
2018 end;
2019 end;
2021 // ×èíèì ID'û ïàíåëåé, êîòîðûå èñïîëüçóþòñÿ â òðèããåðàõ
2022 for b := 0 to High(TriggersTable) do
2023 begin
2024 if (TriggersTable[b].texPan <> nil) then TriggersTable[b].texPanIdx := TriggersTable[b].texPan.userPanelId;
2025 if (TriggersTable[b].liftPan <> nil) then TriggersTable[b].LiftPanelIdx := TriggersTable[b].liftPan.userPanelId;
2026 if (TriggersTable[b].doorPan <> nil) then TriggersTable[b].DoorPanelIdx := TriggersTable[b].doorPan.userPanelId;
2027 if (TriggersTable[b].shotPan <> nil) then TriggersTable[b].ShotPanelIdx := TriggersTable[b].shotPan.userPanelId;
2028 if (TriggersTable[b].mplatPan <> nil) then TriggersTable[b].MPlatPanelIdx := TriggersTable[b].mplatPan.userPanelId;
2029 end;
2031 // create map grid, init other grids (for monsters, for example)
2032 e_WriteLog('Creating map grid', MSG_NOTIFY);
2033 mapCreateGrid();
2035 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû
2036 if (triggers <> nil) and (panels <> nil) and (not gLoadGameMode) then
2037 begin
2038 e_LogWritefln(' Creating triggers (%d)...', [triggers.count]);
2039 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
2040 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü
2041 trignum := -1;
2042 for rec in triggers do
2043 begin
2044 Inc(trignum);
2045 if (TriggersTable[trignum].texPan <> nil) then b := TriggersTable[trignum].texPan.PanelType else b := 0;
2046 if (TriggersTable[trignum].shotPan <> nil) then c := TriggersTable[trignum].shotPan.PanelType else c := 0;
2047 // we can have only one of those
2048 if (TriggersTable[trignum].LiftPanelIdx <> -1) then tgpid := TriggersTable[trignum].LiftPanelIdx
2049 else if (TriggersTable[trignum].DoorPanelIdx <> -1) then tgpid := TriggersTable[trignum].DoorPanelIdx
2050 else if (TriggersTable[trignum].ShotPanelIdx <> -1) then tgpid := TriggersTable[trignum].ShotPanelIdx
2051 else if (TriggersTable[trignum].MPlatPanelIdx <> -1) then tgpid := TriggersTable[trignum].MPlatPanelIdx
2052 else tgpid := -1;
2053 //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]);
2054 CreateTrigger(trignum, rec, TriggersTable[trignum].texPanIdx, tgpid, Word(b), Word(c));
2055 end;
2056 end;
2058 // Çàãðóçêà ïðåäìåòîâ
2059 e_WriteLog(' Loading items...', MSG_NOTIFY);
2060 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
2062 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû
2063 if (items <> nil) and not gLoadGameMode then
2064 begin
2065 e_WriteLog(' Spawning items...', MSG_NOTIFY);
2066 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
2067 for rec in items do CreateItem(rec);
2068 end;
2070 // Çàãðóçêà îáëàñòåé
2071 e_WriteLog(' Loading areas...', MSG_NOTIFY);
2072 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
2074 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè
2075 if areas <> nil then
2076 begin
2077 e_WriteLog(' Creating areas...', MSG_NOTIFY);
2078 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
2079 for rec in areas do CreateArea(rec);
2080 end;
2082 // Çàãðóçêà ìîíñòðîâ
2083 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
2084 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
2086 gTotalMonsters := 0;
2088 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ
2089 if (monsters <> nil) and not gLoadGameMode then
2090 begin
2091 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
2092 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
2093 for rec in monsters do CreateMonster(rec);
2094 end;
2096 gCurrentMap := mapReader; // this will be our current map now
2097 gCurrentMapFileName := Res;
2098 mapReader := nil;
2100 // Çàãðóçêà íåáà
2101 if gMapInfo.SkyName <> '' then
2102 begin
2103 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
2104 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
2105 FileName := g_ExtractWadName(gMapInfo.SkyName);
2107 if FileName <> '' then
2108 FileName := GameDir+'/wads/'+FileName
2109 else
2110 begin
2111 FileName := g_ExtractWadName(Res);
2112 end;
2114 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
2115 if g_Texture_CreateWAD(BackID, s) then
2116 begin
2117 g_Game_SetupScreenSize();
2118 end
2119 else
2120 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
2121 end;
2123 // Çàãðóçêà ìóçûêè
2124 ok := False;
2125 if gMapInfo.MusicName <> '' then
2126 begin
2127 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
2128 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
2129 FileName := g_ExtractWadName(gMapInfo.MusicName);
2131 if FileName <> '' then
2132 FileName := GameDir+'/wads/'+FileName
2133 else
2134 begin
2135 FileName := g_ExtractWadName(Res);
2136 end;
2138 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
2139 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
2140 ok := True
2141 else
2142 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
2143 end;
2145 // Îñòàëüíûå óñòàíâêè
2146 CreateDoorMap();
2147 CreateLiftMap();
2149 g_Items_Init();
2150 g_Weapon_Init();
2151 g_Monsters_Init();
2153 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
2154 if not gLoadGameMode then g_GFX_Init();
2156 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
2157 mapTextureList := nil;
2158 panels := nil;
2159 items := nil;
2160 areas := nil;
2161 triggers := nil;
2162 TriggersTable := nil;
2163 AddTextures := nil;
2165 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
2166 if ok and (not gLoadGameMode) then
2167 begin
2168 gMusic.SetByName(gMapInfo.MusicName);
2169 gMusic.Play();
2170 end
2171 else
2172 begin
2173 gMusic.SetByName('');
2174 end;
2176 stt := curTimeMicro()-stt;
2177 e_LogWritefln('map loaded in %s.%s milliseconds', [Integer(stt div 1000), Integer(stt mod 1000)]);
2178 finally
2179 sfsGCEnable(); // enable releasing unused volumes
2180 mapReader.Free();
2181 e_ClearInputBuffer(); // why not?
2182 end;
2184 e_WriteLog('Done loading map.', MSG_NOTIFY);
2185 Result := True;
2186 end;
2189 function g_Map_GetMapInfo(Res: String): TMapInfo;
2190 var
2191 WAD: TWADFile;
2192 mapReader: TDynRecord;
2193 //Header: TMapHeaderRec_1;
2194 FileName: String;
2195 Data: Pointer;
2196 Len: Integer;
2197 begin
2198 FillChar(Result, SizeOf(Result), 0);
2199 FileName := g_ExtractWadName(Res);
2201 WAD := TWADFile.Create();
2202 if not WAD.ReadFile(FileName) then
2203 begin
2204 WAD.Free();
2205 Exit;
2206 end;
2208 //k8: it ignores path again
2209 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
2210 begin
2211 WAD.Free();
2212 Exit;
2213 end;
2215 WAD.Free();
2217 try
2218 mapReader := g_Map_ParseMap(Data, Len);
2219 except
2220 mapReader := nil;
2221 FreeMem(Data);
2222 exit;
2223 end;
2225 FreeMem(Data);
2227 if (mapReader.Width > 0) and (mapReader.Height > 0) then
2228 begin
2229 Result.Name := mapReader.MapName;
2230 Result.Description := mapReader.MapDesc;
2231 Result.Map := Res;
2232 Result.Author := mapReader.MapAuthor;
2233 Result.Height := mapReader.Height;
2234 Result.Width := mapReader.Width;
2235 end
2236 else
2237 begin
2238 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2239 //ZeroMemory(@Header, SizeOf(Header));
2240 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2241 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2242 Result.Map := Res;
2243 Result.Author := '';
2244 Result.Height := 0;
2245 Result.Width := 0;
2246 end;
2248 mapReader.Free();
2249 end;
2251 function g_Map_GetMapsList(WADName: string): SArray;
2252 var
2253 WAD: TWADFile;
2254 a: Integer;
2255 ResList: SArray;
2256 begin
2257 Result := nil;
2258 WAD := TWADFile.Create();
2259 if not WAD.ReadFile(WADName) then
2260 begin
2261 WAD.Free();
2262 Exit;
2263 end;
2264 ResList := WAD.GetMapResources();
2265 if ResList <> nil then
2266 begin
2267 for a := 0 to High(ResList) do
2268 begin
2269 SetLength(Result, Length(Result)+1);
2270 Result[High(Result)] := ResList[a];
2271 end;
2272 end;
2273 WAD.Free();
2274 end;
2276 function g_Map_Exist(Res: string): Boolean;
2277 var
2278 WAD: TWADFile;
2279 FileName, mnn: string;
2280 ResList: SArray;
2281 a: Integer;
2282 begin
2283 Result := False;
2285 FileName := addWadExtension(g_ExtractWadName(Res));
2287 WAD := TWADFile.Create;
2288 if not WAD.ReadFile(FileName) then
2289 begin
2290 WAD.Free();
2291 Exit;
2292 end;
2294 ResList := WAD.GetMapResources();
2295 WAD.Free();
2297 mnn := g_ExtractFileName(Res);
2298 if ResList <> nil then
2299 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
2300 begin
2301 Result := True;
2302 Exit;
2303 end;
2304 end;
2306 procedure g_Map_Free(freeTextures: Boolean=true);
2307 var
2308 a: Integer;
2310 procedure FreePanelArray(var panels: TPanelArray);
2311 var
2312 i: Integer;
2314 begin
2315 if panels <> nil then
2316 begin
2317 for i := 0 to High(panels) do
2318 panels[i].Free();
2319 panels := nil;
2320 end;
2321 end;
2323 begin
2324 g_GFX_Free();
2325 g_Weapon_Free();
2326 g_Items_Free();
2327 g_Triggers_Free();
2328 g_Monsters_Free();
2330 RespawnPoints := nil;
2331 if FlagPoints[FLAG_RED] <> nil then
2332 begin
2333 Dispose(FlagPoints[FLAG_RED]);
2334 FlagPoints[FLAG_RED] := nil;
2335 end;
2336 if FlagPoints[FLAG_BLUE] <> nil then
2337 begin
2338 Dispose(FlagPoints[FLAG_BLUE]);
2339 FlagPoints[FLAG_BLUE] := nil;
2340 end;
2341 //DOMFlagPoints := nil;
2343 //gDOMFlags := nil;
2345 if (Length(gCurrentMapFileName) <> 0) then
2346 begin
2347 e_LogWritefln('g_Map_Free: previous map was ''%s''...', [gCurrentMapFileName]);
2348 end
2349 else
2350 begin
2351 e_LogWritefln('g_Map_Free: no previous map.', []);
2352 end;
2353 if freeTextures then
2354 begin
2355 e_LogWritefln('g_Map_Free: clearing textures...', []);
2356 if (Textures <> nil) then
2357 begin
2358 for a := 0 to High(Textures) do
2359 begin
2360 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
2361 begin
2362 if Textures[a].Anim then
2363 begin
2364 g_Frames_DeleteByID(Textures[a].FramesID)
2365 end
2366 else
2367 begin
2368 if (Textures[a].TextureID <> LongWord(TEXTURE_NONE)) then
2369 begin
2370 e_DeleteTexture(Textures[a].TextureID);
2371 end;
2372 end;
2373 end;
2374 end;
2375 Textures := nil;
2376 end;
2377 TextNameHash.Free();
2378 TextNameHash := nil;
2379 BadTextNameHash.Free();
2380 BadTextNameHash := nil;
2381 end;
2383 panByGUID := nil;
2385 FreePanelArray(gWalls);
2386 FreePanelArray(gRenderBackgrounds);
2387 FreePanelArray(gRenderForegrounds);
2388 FreePanelArray(gWater);
2389 FreePanelArray(gAcid1);
2390 FreePanelArray(gAcid2);
2391 FreePanelArray(gSteps);
2392 FreePanelArray(gLifts);
2393 FreePanelArray(gBlockMon);
2394 gMovingWallIds := nil;
2396 if BackID <> DWORD(-1) then
2397 begin
2398 gBackSize.X := 0;
2399 gBackSize.Y := 0;
2400 e_DeleteTexture(BackID);
2401 BackID := DWORD(-1);
2402 end;
2404 g_Game_StopAllSounds(False);
2405 gMusic.FreeSound();
2406 g_Sound_Delete(gMapInfo.MusicName);
2408 gMapInfo.Name := '';
2409 gMapInfo.Description := '';
2410 gMapInfo.MusicName := '';
2411 gMapInfo.Height := 0;
2412 gMapInfo.Width := 0;
2414 gDoorMap := nil;
2415 gLiftMap := nil;
2416 end;
2418 procedure g_Map_Update();
2419 var
2420 a, d, j: Integer;
2421 m: Word;
2422 s: String;
2424 procedure UpdatePanelArray(var panels: TPanelArray);
2425 var
2426 i: Integer;
2428 begin
2429 for i := 0 to High(panels) do panels[i].Update();
2430 end;
2432 begin
2433 if g_dbgpan_mplat_step then g_dbgpan_mplat_active := true;
2435 UpdatePanelArray(gWalls);
2436 UpdatePanelArray(gRenderBackgrounds);
2437 UpdatePanelArray(gRenderForegrounds);
2438 UpdatePanelArray(gWater);
2439 UpdatePanelArray(gAcid1);
2440 UpdatePanelArray(gAcid2);
2441 UpdatePanelArray(gSteps);
2443 if g_dbgpan_mplat_step then begin g_dbgpan_mplat_step := false; g_dbgpan_mplat_active := false; end;
2445 if gGameSettings.GameMode = GM_CTF then
2446 begin
2447 for a := FLAG_RED to FLAG_BLUE do
2448 begin
2449 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2450 begin
2451 with gFlags[a] do
2452 begin
2453 if gFlags[a].Animation <> nil then gFlags[a].Animation.Update();
2455 m := g_Obj_Move(@Obj, True, True);
2457 if gTime mod (GAME_TICK*2) <> 0 then Continue;
2459 // Ñîïðîòèâëåíèå âîçäóõà
2460 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2462 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó
2463 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2464 begin
2465 g_Map_ResetFlag(a);
2466 gFlags[a].CaptureTime := 0;
2467 if a = FLAG_RED then
2468 s := _lc[I_PLAYER_FLAG_RED]
2469 else
2470 s := _lc[I_PLAYER_FLAG_BLUE];
2471 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2473 if g_Game_IsNet then
2474 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2475 Continue;
2476 end;
2478 if Count > 0 then Count -= 1;
2480 // Èãðîê áåðåò ôëàã
2481 if gPlayers <> nil then
2482 begin
2483 j := Random(Length(gPlayers)) - 1;
2484 for d := 0 to High(gPlayers) do
2485 begin
2486 Inc(j);
2487 if j > High(gPlayers) then j := 0;
2488 if gPlayers[j] <> nil then
2489 begin
2490 if gPlayers[j].Live and g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2491 begin
2492 if gPlayers[j].GetFlag(a) then Break;
2493 end;
2494 end;
2495 end;
2496 end;
2497 end;
2498 end;
2499 end;
2500 end;
2501 end;
2504 // old algo
2505 procedure g_Map_DrawPanels (PanelType: Word);
2507 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2508 var
2509 idx: Integer;
2510 begin
2511 if (panels <> nil) then
2512 begin
2513 // alas, no visible set
2514 for idx := 0 to High(panels) do
2515 begin
2516 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
2517 end;
2518 end;
2519 end;
2521 begin
2522 case PanelType of
2523 PANEL_WALL: DrawPanels(gWalls);
2524 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2525 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2526 PANEL_FORE: DrawPanels(gRenderForegrounds);
2527 PANEL_WATER: DrawPanels(gWater);
2528 PANEL_ACID1: DrawPanels(gAcid1);
2529 PANEL_ACID2: DrawPanels(gAcid2);
2530 PANEL_STEP: DrawPanels(gSteps);
2531 end;
2532 end;
2535 // new algo
2536 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2538 function checker (pan: TPanel; tag: Integer): Boolean;
2539 begin
2540 result := false; // don't stop, ever
2541 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2542 gDrawPanelList.insert(pan);
2543 end;
2545 begin
2546 dplClear();
2547 //tagmask := panelTypeToTag(PanelType);
2548 mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask);
2549 // list will be rendered in `g_game.DrawPlayer()`
2550 end;
2553 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2555 function checker (pan: TPanel; tag: Integer): Boolean;
2556 begin
2557 result := false; // don't stop, ever
2558 pan.DrawShadowVolume(lightX, lightY, radius);
2559 end;
2561 begin
2562 mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2563 end;
2566 procedure g_Map_DrawBack(dx, dy: Integer);
2567 begin
2568 if gDrawBackGround and (BackID <> DWORD(-1)) then
2569 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2570 else
2571 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2572 end;
2574 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2575 PanelType: Word; b1x3: Boolean=false): Boolean;
2576 var
2577 a, h: Integer;
2578 begin
2579 Result := False;
2581 if WordBool(PanelType and PANEL_WALL) then
2582 if gWalls <> nil then
2583 begin
2584 h := High(gWalls);
2586 for a := 0 to h do
2587 if gWalls[a].Enabled and
2588 g_Collide(X, Y, Width, Height,
2589 gWalls[a].X, gWalls[a].Y,
2590 gWalls[a].Width, gWalls[a].Height) then
2591 begin
2592 Result := True;
2593 Exit;
2594 end;
2595 end;
2597 if WordBool(PanelType and PANEL_WATER) then
2598 if gWater <> nil then
2599 begin
2600 h := High(gWater);
2602 for a := 0 to h do
2603 if g_Collide(X, Y, Width, Height,
2604 gWater[a].X, gWater[a].Y,
2605 gWater[a].Width, gWater[a].Height) then
2606 begin
2607 Result := True;
2608 Exit;
2609 end;
2610 end;
2612 if WordBool(PanelType and PANEL_ACID1) then
2613 if gAcid1 <> nil then
2614 begin
2615 h := High(gAcid1);
2617 for a := 0 to h do
2618 if g_Collide(X, Y, Width, Height,
2619 gAcid1[a].X, gAcid1[a].Y,
2620 gAcid1[a].Width, gAcid1[a].Height) then
2621 begin
2622 Result := True;
2623 Exit;
2624 end;
2625 end;
2627 if WordBool(PanelType and PANEL_ACID2) then
2628 if gAcid2 <> nil then
2629 begin
2630 h := High(gAcid2);
2632 for a := 0 to h do
2633 if g_Collide(X, Y, Width, Height,
2634 gAcid2[a].X, gAcid2[a].Y,
2635 gAcid2[a].Width, gAcid2[a].Height) then
2636 begin
2637 Result := True;
2638 Exit;
2639 end;
2640 end;
2642 if WordBool(PanelType and PANEL_STEP) then
2643 if gSteps <> nil then
2644 begin
2645 h := High(gSteps);
2647 for a := 0 to h do
2648 if g_Collide(X, Y, Width, Height,
2649 gSteps[a].X, gSteps[a].Y,
2650 gSteps[a].Width, gSteps[a].Height) then
2651 begin
2652 Result := True;
2653 Exit;
2654 end;
2655 end;
2657 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2658 if gLifts <> nil then
2659 begin
2660 h := High(gLifts);
2662 for a := 0 to h do
2663 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2664 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2665 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2666 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2667 g_Collide(X, Y, Width, Height,
2668 gLifts[a].X, gLifts[a].Y,
2669 gLifts[a].Width, gLifts[a].Height) then
2670 begin
2671 Result := True;
2672 Exit;
2673 end;
2674 end;
2676 if WordBool(PanelType and PANEL_BLOCKMON) then
2677 if gBlockMon <> nil then
2678 begin
2679 h := High(gBlockMon);
2681 for a := 0 to h do
2682 if ( (not b1x3) or
2683 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2684 g_Collide(X, Y, Width, Height,
2685 gBlockMon[a].X, gBlockMon[a].Y,
2686 gBlockMon[a].Width, gBlockMon[a].Height) then
2687 begin
2688 Result := True;
2689 Exit;
2690 end;
2691 end;
2692 end;
2694 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2695 var
2696 texid: DWORD;
2698 function checkPanels (constref panels: TPanelArray): Boolean;
2699 var
2700 a: Integer;
2701 begin
2702 result := false;
2703 if panels = nil then exit;
2704 for a := 0 to High(panels) do
2705 begin
2706 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2707 begin
2708 result := true;
2709 texid := panels[a].GetTextureID();
2710 exit;
2711 end;
2712 end;
2713 end;
2715 begin
2716 texid := LongWord(TEXTURE_NONE);
2717 result := texid;
2718 if not checkPanels(gWater) then
2719 if not checkPanels(gAcid1) then
2720 if not checkPanels(gAcid2) then exit;
2721 result := texid;
2722 end;
2725 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2726 const
2727 SlowMask = GridTagLift or GridTagBlockMon;
2728 function checker (pan: TPanel; tag: Integer): Boolean;
2729 begin
2731 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2732 begin
2733 result := pan.Enabled;
2734 exit;
2735 end;
2738 if ((tag and GridTagLift) <> 0) then
2739 begin
2740 result :=
2741 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2742 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2743 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2744 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
2745 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
2746 exit;
2747 end;
2749 if ((tag and GridTagBlockMon) <> 0) then
2750 begin
2751 result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2752 exit;
2753 end;
2755 // other shit
2756 //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2757 result := true; // i found her!
2758 end;
2760 var
2761 tagmask: Integer = 0;
2762 begin
2763 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2764 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2765 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2766 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2767 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2768 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2769 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2771 if (tagmask = 0) then begin result := false; exit; end; // just in case
2773 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2774 if gdbg_map_use_accel_coldet then
2775 begin
2776 if (Width = 1) and (Height = 1) then
2777 begin
2778 if ((tagmask and SlowMask) <> 0) then
2779 begin
2780 // slow
2781 result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil);
2782 end
2783 else
2784 begin
2785 // fast
2786 result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil);
2787 end;
2788 end
2789 else
2790 begin
2791 if ((tagmask and SlowMask) <> 0) then
2792 begin
2793 // slow
2794 result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil);
2795 end
2796 else
2797 begin
2798 // fast
2799 result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil);
2800 end;
2801 end;
2802 end
2803 else
2804 begin
2805 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2806 end;
2807 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2808 end;
2811 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2812 var
2813 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2814 texid: DWORD;
2816 // slightly different from the old code, but meh...
2817 function checker (pan: TPanel; tag: Integer): Boolean;
2818 begin
2819 result := false; // don't stop, ever
2820 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2821 // check priorities
2822 case cctype of
2823 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2824 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2825 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2826 end;
2827 // collision?
2828 //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2829 // yeah
2830 texid := pan.GetTextureID();
2831 // water? water has the highest priority, so stop right here
2832 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2833 // acid2?
2834 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2835 // acid1?
2836 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2837 end;
2839 begin
2840 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2841 if gdbg_map_use_accel_coldet then
2842 begin
2843 texid := LongWord(TEXTURE_NONE);
2844 if (Width = 1) and (Height = 1) then
2845 begin
2846 mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2847 end
2848 else
2849 begin
2850 mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2851 end;
2852 result := texid;
2853 end
2854 else
2855 begin
2856 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2857 end;
2858 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2859 end;
2862 procedure g_Map_EnableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_EnableWallGUID(gWalls[ID].guid); end;
2863 procedure g_Map_DisableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_DisableWallGUID(gWalls[ID].guid); end;
2864 procedure g_Map_SetLift_XXX (ID: DWORD; t: Integer); begin if (ID < Length(gLifts)) then g_Map_SetLiftGUID(gLifts[ID].guid, t); end;
2867 procedure g_Map_EnableWallGUID (pguid: Integer);
2868 var
2869 pan: TPanel;
2870 begin
2871 //pan := gWalls[ID];
2872 pan := g_Map_PanelByGUID(pguid);
2873 if (pan = nil) then exit;
2874 pan.Enabled := True;
2875 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, true);
2877 mapGrid.proxyEnabled[pan.proxyId] := true;
2878 //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
2879 //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
2881 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState({gWalls[ID]}pan.PanelType, pguid);
2883 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2884 //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);
2885 {$ENDIF}
2886 end;
2889 procedure g_Map_DisableWallGUID (pguid: Integer);
2890 var
2891 pan: TPanel;
2892 begin
2893 //pan := gWalls[ID];
2894 pan := g_Map_PanelByGUID(pguid);
2895 if (pan = nil) then exit;
2896 pan.Enabled := False;
2897 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, false);
2899 mapGrid.proxyEnabled[pan.proxyId] := false;
2900 //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
2902 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, pguid);
2904 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2905 //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);
2906 {$ENDIF}
2907 end;
2910 procedure g_Map_SwitchTextureGUID (PanelType: Word; pguid: Integer; AnimLoop: Byte = 0);
2911 var
2912 tp: TPanel;
2913 begin
2914 tp := g_Map_PanelByGUID(pguid);
2915 if (tp = nil) then exit;
2917 case PanelType of
2918 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: tp := gWalls[ID];
2919 PANEL_FORE: tp := gRenderForegrounds[ID];
2920 PANEL_BACK: tp := gRenderBackgrounds[ID];
2921 PANEL_WATER: tp := gWater[ID];
2922 PANEL_ACID1: tp := gAcid1[ID];
2923 PANEL_ACID2: tp := gAcid2[ID];
2924 PANEL_STEP: tp := gSteps[ID];
2925 else exit;
2926 end;
2929 tp.NextTexture(AnimLoop);
2930 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelTexture(PanelType, pguid, AnimLoop);
2931 end;
2934 procedure g_Map_SetLiftGUID (pguid: Integer; t: Integer);
2935 var
2936 pan: TPanel;
2937 begin
2938 //pan := gLifts[ID];
2939 pan := g_Map_PanelByGUID(pguid);
2940 if (pan = nil) then exit;
2941 if not pan.isGLift then exit;
2943 if ({gLifts[ID]}pan.LiftType = t) then exit; //!FIXME!TRIGANY!
2945 with {gLifts[ID]} pan do
2946 begin
2947 LiftType := t;
2949 g_Mark(X, Y, Width, Height, MARK_LIFT, false);
2950 //TODO: make separate lift tags, and change tag here
2952 case LiftType of
2953 0: g_Mark(X, Y, Width, Height, MARK_LIFTUP);
2954 1: g_Mark(X, Y, Width, Height, MARK_LIFTDOWN);
2955 2: g_Mark(X, Y, Width, Height, MARK_LIFTLEFT);
2956 3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT);
2957 end;
2959 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, pguid);
2960 end;
2961 end;
2964 function g_Map_GetPoint (PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2965 var
2966 a: Integer;
2967 PointsArray: Array of TRespawnPoint;
2968 begin
2969 Result := False;
2970 SetLength(PointsArray, 0);
2972 if RespawnPoints = nil then
2973 Exit;
2975 for a := 0 to High(RespawnPoints) do
2976 if RespawnPoints[a].PointType = PointType then
2977 begin
2978 SetLength(PointsArray, Length(PointsArray)+1);
2979 PointsArray[High(PointsArray)] := RespawnPoints[a];
2980 end;
2982 if PointsArray = nil then
2983 Exit;
2985 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2986 Result := True;
2987 end;
2989 function g_Map_GetPointCount(PointType: Byte): Word;
2990 var
2991 a: Integer;
2992 begin
2993 Result := 0;
2995 if RespawnPoints = nil then
2996 Exit;
2998 for a := 0 to High(RespawnPoints) do
2999 if RespawnPoints[a].PointType = PointType then
3000 Result := Result + 1;
3001 end;
3003 function g_Map_HaveFlagPoints(): Boolean;
3004 begin
3005 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
3006 end;
3008 procedure g_Map_ResetFlag(Flag: Byte);
3009 begin
3010 with gFlags[Flag] do
3011 begin
3012 Obj.X := -1000;
3013 Obj.Y := -1000;
3014 Obj.Vel.X := 0;
3015 Obj.Vel.Y := 0;
3016 Direction := D_LEFT;
3017 State := FLAG_STATE_NONE;
3018 if FlagPoints[Flag] <> nil then
3019 begin
3020 Obj.X := FlagPoints[Flag]^.X;
3021 Obj.Y := FlagPoints[Flag]^.Y;
3022 Direction := FlagPoints[Flag]^.Direction;
3023 State := FLAG_STATE_NORMAL;
3024 end;
3025 Count := -1;
3026 end;
3027 end;
3029 procedure g_Map_DrawFlags();
3030 var
3031 i, dx: Integer;
3032 Mirror: TMirrorType;
3033 begin
3034 if gGameSettings.GameMode <> GM_CTF then
3035 Exit;
3037 for i := FLAG_RED to FLAG_BLUE do
3038 with gFlags[i] do
3039 if State <> FLAG_STATE_CAPTURED then
3040 begin
3041 if State = FLAG_STATE_NONE then
3042 continue;
3044 if Direction = D_LEFT then
3045 begin
3046 Mirror := M_HORIZONTAL;
3047 dx := -1;
3048 end
3049 else
3050 begin
3051 Mirror := M_NONE;
3052 dx := 1;
3053 end;
3055 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
3057 if g_debug_Frames then
3058 begin
3059 e_DrawQuad(Obj.X+Obj.Rect.X,
3060 Obj.Y+Obj.Rect.Y,
3061 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
3062 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
3063 0, 255, 0);
3064 end;
3065 end;
3066 end;
3069 procedure g_Map_SaveState (var Mem: TBinMemoryWriter);
3070 var
3071 dw: DWORD;
3072 b: Byte;
3073 str: String;
3074 boo: Boolean;
3076 procedure savePanels ();
3077 var
3078 PAMem: TBinMemoryWriter;
3079 pan: TPanel;
3080 begin
3081 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé
3082 PAMem := TBinMemoryWriter.Create((Length(panByGUID)+1) * 40);
3084 for pan in panByGUID do
3085 begin
3086 if true{pan.SaveIt} then
3087 begin
3088 // ID ïàíåëè
3089 //PAMem.WriteInt(i);
3090 // Ñîõðàíÿåì ïàíåëü
3091 pan.SaveState(PAMem);
3092 end;
3093 end;
3095 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé
3096 PAMem.SaveToMemory(Mem);
3097 PAMem.Free();
3098 end;
3100 procedure SaveFlag (flag: PFlag);
3101 begin
3102 // Ñèãíàòóðà ôëàãà
3103 dw := FLAG_SIGNATURE; // 'FLAG'
3104 Mem.WriteDWORD(dw);
3105 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3106 Mem.WriteByte(flag^.RespawnType);
3107 // Ñîñòîÿíèå ôëàãà
3108 Mem.WriteByte(flag^.State);
3109 // Íàïðàâëåíèå ôëàãà
3110 if flag^.Direction = D_LEFT then b := 1 else b := 2; // D_RIGHT
3111 Mem.WriteByte(b);
3112 // Îáúåêò ôëàãà
3113 Obj_SaveState(@flag^.Obj, Mem);
3114 end;
3116 begin
3117 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
3119 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
3120 savePanels();
3121 ///// /////
3123 ///// Ñîõðàíÿåì ìóçûêó: /////
3124 // Ñèãíàòóðà ìóçûêè:
3125 dw := MUSIC_SIGNATURE; // 'MUSI'
3126 Mem.WriteDWORD(dw);
3127 // Íàçâàíèå ìóçûêè:
3128 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
3129 if gMusic.NoMusic then str := '' else str := gMusic.Name;
3130 Mem.WriteString(str, 64);
3131 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3132 dw := gMusic.GetPosition();
3133 Mem.WriteDWORD(dw);
3134 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3135 boo := gMusic.SpecPause;
3136 Mem.WriteBoolean(boo);
3137 ///// /////
3139 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
3140 Mem.WriteInt(gTotalMonsters);
3141 ///// /////
3143 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
3144 if gGameSettings.GameMode = GM_CTF then
3145 begin
3146 // Ôëàã Êðàñíîé êîìàíäû
3147 SaveFlag(@gFlags[FLAG_RED]);
3148 // Ôëàã Ñèíåé êîìàíäû
3149 SaveFlag(@gFlags[FLAG_BLUE]);
3150 end;
3151 ///// /////
3153 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3154 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3155 begin
3156 // Î÷êè Êðàñíîé êîìàíäû
3157 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
3158 // Î÷êè Ñèíåé êîìàíäû
3159 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
3160 end;
3161 ///// /////
3162 end;
3165 procedure g_Map_LoadState (var Mem: TBinMemoryReader);
3166 var
3167 dw: DWORD;
3168 b: Byte;
3169 str: String;
3170 boo: Boolean;
3172 procedure loadPanels ();
3173 var
3174 PAMem: TBinMemoryReader;
3175 pan: TPanel;
3176 begin
3177 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé
3178 PAMem := TBinMemoryReader.Create();
3179 PAMem.LoadFromMemory(Mem);
3181 for pan in panByGUID do
3182 begin
3183 if true{pan.SaveIt} then
3184 begin
3185 // ID ïàíåëè:
3186 //PAMem.ReadInt(id);
3188 if id <> i then raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
3190 // Çàãðóæàåì ïàíåëü
3191 pan.LoadState(PAMem);
3192 //if (panels[i].arrIdx <> i) then raise Exception.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel arrIdx');
3193 if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
3194 end;
3195 end;
3197 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí
3198 PAMem.Free();
3199 end;
3201 procedure LoadFlag(flag: PFlag);
3202 begin
3203 // Ñèãíàòóðà ôëàãà
3204 Mem.ReadDWORD(dw);
3205 // 'FLAG'
3206 if dw <> FLAG_SIGNATURE then raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
3207 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3208 Mem.ReadByte(flag^.RespawnType);
3209 // Ñîñòîÿíèå ôëàãà
3210 Mem.ReadByte(flag^.State);
3211 // Íàïðàâëåíèå ôëàãà
3212 Mem.ReadByte(b);
3213 if b = 1 then flag^.Direction := D_LEFT else flag^.Direction := D_RIGHT; // b = 2
3214 // Îáúåêò ôëàãà
3215 Obj_LoadState(@flag^.Obj, Mem);
3216 end;
3218 begin
3219 if Mem = nil then Exit;
3221 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
3222 loadPanels();
3223 ///// /////
3225 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó
3226 g_GFX_Init();
3227 //mapCreateGrid();
3229 ///// Çàãðóæàåì ìóçûêó: /////
3230 // Ñèãíàòóðà ìóçûêè
3231 Mem.ReadDWORD(dw);
3232 // 'MUSI'
3233 if dw <> MUSIC_SIGNATURE then raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
3234 // Íàçâàíèå ìóçûêè
3235 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
3236 Mem.ReadString(str);
3237 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3238 Mem.ReadDWORD(dw);
3239 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3240 Mem.ReadBoolean(boo);
3241 // Çàïóñêàåì ýòó ìóçûêó
3242 gMusic.SetByName(str);
3243 gMusic.SpecPause := boo;
3244 gMusic.Play();
3245 gMusic.Pause(True);
3246 gMusic.SetPosition(dw);
3247 ///// /////
3249 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
3250 Mem.ReadInt(gTotalMonsters);
3251 ///// /////
3253 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
3254 if gGameSettings.GameMode = GM_CTF then
3255 begin
3256 // Ôëàã Êðàñíîé êîìàíäû
3257 LoadFlag(@gFlags[FLAG_RED]);
3258 // Ôëàã Ñèíåé êîìàíäû
3259 LoadFlag(@gFlags[FLAG_BLUE]);
3260 end;
3261 ///// /////
3263 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3264 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3265 begin
3266 // Î÷êè Êðàñíîé êîìàíäû
3267 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
3268 // Î÷êè Ñèíåé êîìàíäû
3269 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
3270 end;
3271 ///// /////
3272 end;
3275 // trace liquid, stepping by `dx` and `dy`
3276 // return last seen liquid coords, and `false` if we're started outside of the liquid
3277 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
3278 const
3279 MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
3280 begin
3281 topx := x;
3282 topy := y;
3283 // started outside of the liquid?
3284 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end;
3285 if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check
3286 result := true;
3287 while true do
3288 begin
3289 Inc(x, dx);
3290 Inc(y, dy);
3291 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit
3292 topx := x;
3293 topy := y;
3294 end;
3295 end;
3298 end.