DEADSOFTWARE

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