DEADSOFTWARE

bye-bye, bineditor, we won't miss you
[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 SysUtils, Classes,
24 e_graphics, g_basic, MAPDEF, g_textures,
25 g_phys, wadreader, g_panel, g_grid, md5, binheap, xprofiler, xparser, xdynrec;
27 type
28 TMapInfo = record
29 Map: String;
30 Name: String;
31 Description: String;
32 Author: String;
33 MusicName: String;
34 SkyName: String;
35 Height: Word;
36 Width: Word;
37 end;
39 PRespawnPoint = ^TRespawnPoint;
40 TRespawnPoint = record
41 X, Y: Integer;
42 Direction: TDirection;
43 PointType: Byte;
44 end;
46 PFlagPoint = ^TFlagPoint;
47 TFlagPoint = TRespawnPoint;
49 PFlag = ^TFlag;
50 TFlag = record
51 Obj: TObj;
52 RespawnType: Byte;
53 State: Byte;
54 Count: Integer;
55 CaptureTime: LongWord;
56 Animation: TAnimation;
57 Direction: TDirection;
58 end;
60 function g_Map_Load(Res: String): Boolean;
61 function g_Map_GetMapInfo(Res: String): TMapInfo;
62 function g_Map_GetMapsList(WADName: String): SArray;
63 function g_Map_Exist(Res: String): Boolean;
64 procedure g_Map_Free(freeTextures: Boolean=true);
65 procedure g_Map_Update();
67 function g_Map_PanelByGUID (aguid: Integer): TPanel; inline;
69 procedure g_Map_DrawPanels (PanelType: Word; hasAmbient: Boolean; constref ambColor: TDFColor); // unaccelerated
70 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
72 procedure g_Map_DrawBack(dx, dy: Integer);
73 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
74 PanelType: Word; b1x3: Boolean=false): Boolean;
75 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
77 procedure g_Map_EnableWallGUID (pguid: Integer);
78 procedure g_Map_DisableWallGUID (pguid: Integer);
79 procedure g_Map_SetLiftGUID (pguid: Integer; t: Integer);
81 // HACK!!!
82 procedure g_Map_EnableWall_XXX (ID: DWORD);
83 procedure g_Map_DisableWall_XXX (ID: DWORD);
84 procedure g_Map_SetLift_XXX (ID: DWORD; t: Integer);
86 procedure g_Map_SwitchTextureGUID (pguid: Integer; AnimLoop: Byte = 0);
88 procedure g_Map_ReAdd_DieTriggers();
89 function g_Map_IsSpecialTexture(Texture: String): Boolean;
91 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
92 function g_Map_GetPointCount(PointType: Byte): Word;
94 function g_Map_HaveFlagPoints(): Boolean;
96 procedure g_Map_ResetFlag(Flag: Byte);
97 procedure g_Map_DrawFlags();
99 procedure g_Map_SaveState (st: TStream);
100 procedure g_Map_LoadState (st: TStream);
102 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
104 // returns panel or nil
105 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
106 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
108 // returns panel or nil
109 // sets `ex` and `ey` to `x1` and `y1` when no hit was detected
110 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
112 type
113 TForEachPanelCB = function (pan: TPanel): Boolean is nested; // return `true` to stop
115 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
116 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
118 // trace liquid, stepping by `dx` and `dy`
119 // return last seen liquid coords, and `false` if we're started outside of the liquid
120 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
123 // return `true` from `cb` to stop
124 function g_Map_ForEachPanel (cb: TForEachPanelCB): TPanel;
126 procedure g_Map_NetSendInterestingPanels (); // yay!
129 procedure g_Map_ProfilersBegin ();
130 procedure g_Map_ProfilersEnd ();
133 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
136 function g_Map_MinX (): Integer; inline;
137 function g_Map_MinY (): Integer; inline;
138 function g_Map_MaxX (): Integer; inline;
139 function g_Map_MaxY (): Integer; inline;
141 const
142 NNF_NO_NAME = 0;
143 NNF_NAME_BEFORE = 1;
144 NNF_NAME_EQUALS = 2;
145 NNF_NAME_AFTER = 3;
147 function g_Texture_NumNameFindStart(name: String): Boolean;
148 function g_Texture_NumNameFindNext(var newName: String): Byte;
150 const
151 RESPAWNPOINT_PLAYER1 = 1;
152 RESPAWNPOINT_PLAYER2 = 2;
153 RESPAWNPOINT_DM = 3;
154 RESPAWNPOINT_RED = 4;
155 RESPAWNPOINT_BLUE = 5;
157 FLAG_NONE = 0;
158 FLAG_RED = 1;
159 FLAG_BLUE = 2;
160 FLAG_DOM = 3;
162 FLAG_STATE_NONE = 0;
163 FLAG_STATE_NORMAL = 1;
164 FLAG_STATE_DROPPED = 2;
165 FLAG_STATE_CAPTURED = 3;
166 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
167 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
169 FLAG_TIME = 720; // 20 seconds
171 SKY_STRETCH: Single = 1.5;
173 const
174 GridTagInvalid = 0;
176 (* draw order:
177 PANEL_BACK
178 PANEL_STEP
179 PANEL_WALL
180 PANEL_CLOSEDOOR
181 PANEL_ACID1
182 PANEL_ACID2
183 PANEL_WATER
184 PANEL_FORE
185 *)
186 // sorted by draw priority
187 GridTagBack = 1 shl 0; // gRenderBackgrounds
188 GridTagStep = 1 shl 1; // gSteps
189 GridTagWall = 1 shl 2; // gWalls
190 GridTagDoor = 1 shl 3; // gWalls
191 GridTagAcid1 = 1 shl 4; // gAcid1
192 GridTagAcid2 = 1 shl 5; // gAcid2
193 GridTagWater = 1 shl 6; // gWater
194 GridTagFore = 1 shl 7; // gRenderForegrounds
195 // the following are invisible
196 GridTagLift = 1 shl 8; // gLifts
197 GridTagBlockMon = 1 shl 9; // gBlockMon
199 GridTagObstacle = (GridTagStep or GridTagWall or GridTagDoor);
200 GridTagLiquid = (GridTagAcid1 or GridTagAcid2 or GridTagWater);
202 GridDrawableMask = (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore);
205 var
206 gWalls: TPanelArray;
207 gRenderBackgrounds: TPanelArray;
208 gRenderForegrounds: TPanelArray;
209 gWater, gAcid1, gAcid2: TPanelArray;
210 gSteps: TPanelArray;
211 gLifts: TPanelArray;
212 gBlockMon: TPanelArray;
213 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
214 //gDOMFlags: array of TFlag;
215 gMapInfo: TMapInfo;
216 gBackSize: TDFPoint;
217 gDoorMap: array of array of DWORD;
218 gLiftMap: array of array of DWORD;
219 gWADHash: TMD5Digest;
220 BackID: DWORD = DWORD(-1);
221 gExternalResources: TStringList;
222 gMovingWallIds: array of Integer = nil;
224 gdbg_map_use_accel_render: Boolean = true;
225 gdbg_map_use_accel_coldet: Boolean = true;
226 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
227 gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
229 gCurrentMap: TDynRecord = nil;
230 gCurrentMapFileName: AnsiString = ''; // so we can skip texture reloading
233 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
236 type
237 TPanelGrid = specialize TBodyGridBase<TPanel>;
239 var
240 mapGrid: TPanelGrid = nil; // DO NOT USE! public for debugging only!
243 implementation
245 uses
246 e_input, g_main, e_log, e_texture, g_items, g_gfx, g_console,
247 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
248 g_options, g_triggers, g_player,
249 Math, g_monsters, g_saveload, g_language, g_netmsg,
250 utils, sfs, xstreams, hashtable,
251 ImagingTypes, Imaging, ImagingUtility,
252 ImagingGif, ImagingNetworkGraphics;
254 const
255 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
256 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
257 FLAG_SIGNATURE = $47414C46; // 'FLAG'
260 var
261 panByGUID: array of TPanel = nil;
264 // ////////////////////////////////////////////////////////////////////////// //
265 function g_Map_PanelByGUID (aguid: Integer): TPanel; inline;
266 begin
267 //if (panByGUID = nil) or (not panByGUID.get(aguid, result)) then result := nil;
268 if (aguid >= 0) and (aguid < Length(panByGUID)) then result := panByGUID[aguid] else result := nil;
269 end;
272 // return `true` from `cb` to stop
273 function g_Map_ForEachPanel (cb: TForEachPanelCB): TPanel;
274 var
275 pan: TPanel;
276 begin
277 result := nil;
278 if not assigned(cb) then exit;
279 for pan in panByGUID do
280 begin
281 if cb(pan) then begin result := pan; exit; end;
282 end;
283 end;
286 procedure g_Map_NetSendInterestingPanels ();
287 var
288 pan: TPanel;
289 begin
290 if g_Game_IsServer and g_Game_IsNet then
291 begin
292 for pan in panByGUID do
293 begin
294 if pan.gncNeedSend then MH_SEND_PanelState(pan.guid);
295 end;
296 end;
297 end;
300 // ////////////////////////////////////////////////////////////////////////// //
301 function g_Map_MinX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0 else result := 0; end;
302 function g_Map_MinY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0 else result := 0; end;
303 function g_Map_MaxX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0+mapGrid.gridWidth-1 else result := 0; end;
304 function g_Map_MaxY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0+mapGrid.gridHeight-1 else result := 0; end;
307 // ////////////////////////////////////////////////////////////////////////// //
308 var
309 dfmapdef: TDynMapDef = nil;
312 procedure loadMapDefinition ();
313 var
314 pr: TTextParser = nil;
315 st: TStream = nil;
316 WAD: TWADFile = nil;
317 begin
318 if (dfmapdef <> nil) then exit;
320 try
321 e_LogWritefln('parsing "mapdef.txt"...', []);
322 st := openDiskFileRO(DataDir+'mapdef.txt');
323 e_LogWritefln('found local "%smapdef.txt"', [DataDir]);
324 except
325 st := nil;
326 end;
328 if (st = nil) then
329 begin
330 WAD := TWADFile.Create();
331 if not WAD.ReadFile(GameWAD) then
332 begin
333 //raise Exception.Create('cannot load "game.wad"');
334 st := nil;
335 end
336 else
337 begin
338 st := WAD.openFileStream('mapdef.txt');
339 end;
340 end;
342 try
343 if (st = nil) then
344 begin
345 //raise Exception.Create('cannot open "mapdef.txt"');
346 e_LogWriteln('using default "mapdef.txt"...');
347 pr := TStrTextParser.Create(defaultMapDef);
348 end
349 else
350 begin
351 pr := TFileTextParser.Create(st);
352 end;
353 except on e: Exception do
354 begin
355 e_LogWritefln('something is VERY wrong here! -- ', [e.message]);
356 raise;
357 end;
358 end;
360 try
361 dfmapdef := TDynMapDef.Create(pr);
362 except
363 on e: TDynParseException do
364 raise Exception.CreateFmt('ERROR in "mapdef.txt" at (%s,%s): %s', [e.tokLine, e.tokCol, e.message]);
365 on e: Exception do
366 raise Exception.CreateFmt('ERROR in "mapdef.txt" at (%s,%s): %s', [pr.tokLine, pr.tokCol, e.message]);
367 end;
369 st.Free();
370 WAD.Free();
371 end;
374 // ////////////////////////////////////////////////////////////////////////// //
375 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
376 var
377 wst: TSFSMemoryChunkStream = nil;
378 begin
379 result := nil;
380 if (dataLen < 4) then exit;
382 if (dfmapdef = nil) then writeln('need to load mapdef');
383 loadMapDefinition();
384 if (dfmapdef = nil) then raise Exception.Create('internal map loader error');
386 wst := TSFSMemoryChunkStream.Create(data, dataLen);
387 try
388 result := dfmapdef.parseMap(wst);
389 except
390 on e: TDynParseException do
391 begin
392 e_LogWritefln('ERROR at (%s,%s): %s', [e.tokLine, e.tokCol, e.message]);
393 wst.Free();
394 result := nil;
395 exit;
396 end;
397 on e: Exception do
398 begin
399 e_LogWritefln('ERROR: %s', [e.message]);
400 wst.Free();
401 result := nil;
402 exit;
403 end;
404 end;
406 //e_LogWriteln('map parsed.');
407 end;
410 // ////////////////////////////////////////////////////////////////////////// //
411 var
412 NNF_PureName: String; // Èìÿ òåêñòóðû áåç öèôð â êîíöå
413 NNF_FirstNum: Integer; // ×èñëî ó íà÷àëüíîé òåêñòóðû
414 NNF_CurrentNum: Integer; // Ñëåäóþùåå ÷èñëî ó òåêñòóðû
417 function g_Texture_NumNameFindStart(name: String): Boolean;
418 var
419 i: Integer;
421 begin
422 Result := False;
423 NNF_PureName := '';
424 NNF_FirstNum := -1;
425 NNF_CurrentNum := -1;
427 for i := Length(name) downto 1 do
428 if (name[i] = '_') then // "_" - ñèìâîë íà÷àëà íîìåðíîãî ïîñòôèêñà
429 begin
430 if i = Length(name) then
431 begin // Íåò öèôð â êîíöå ñòðîêè
432 Exit;
433 end
434 else
435 begin
436 NNF_PureName := Copy(name, 1, i);
437 Delete(name, 1, i);
438 Break;
439 end;
440 end;
442 // Íå ïåðåâåñòè â ÷èñëî:
443 if not TryStrToInt(name, NNF_FirstNum) then
444 Exit;
446 NNF_CurrentNum := 0;
448 Result := True;
449 end;
452 function g_Texture_NumNameFindNext(var newName: String): Byte;
453 begin
454 if (NNF_PureName = '') or (NNF_CurrentNum < 0) then
455 begin
456 newName := '';
457 Result := NNF_NO_NAME;
458 Exit;
459 end;
461 newName := NNF_PureName + IntToStr(NNF_CurrentNum);
463 if NNF_CurrentNum < NNF_FirstNum then
464 Result := NNF_NAME_BEFORE
465 else
466 if NNF_CurrentNum > NNF_FirstNum then
467 Result := NNF_NAME_AFTER
468 else
469 Result := NNF_NAME_EQUALS;
471 Inc(NNF_CurrentNum);
472 end;
475 // ////////////////////////////////////////////////////////////////////////// //
476 function panelTypeToTag (panelType: Word): Integer;
477 begin
478 case panelType of
479 PANEL_WALL: result := GridTagWall; // gWalls
480 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
481 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
482 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
483 PANEL_WATER: result := GridTagWater; // gWater
484 PANEL_ACID1: result := GridTagAcid1; // gAcid1
485 PANEL_ACID2: result := GridTagAcid2; // gAcid2
486 PANEL_STEP: result := GridTagStep; // gSteps
487 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
488 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
489 else result := GridTagInvalid;
490 end;
491 end;
494 function dplLess (a, b: TObject): Boolean;
495 var
496 pa, pb: TPanel;
497 begin
498 pa := TPanel(a);
499 pb := TPanel(b);
500 if (pa.tag < pb.tag) then begin result := true; exit; end;
501 if (pa.tag > pb.tag) then begin result := false; exit; end;
502 result := (pa.arrIdx < pb.arrIdx);
503 end;
505 procedure dplClear ();
506 begin
507 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
508 end;
511 var
512 Textures: TLevelTextureArray = nil;
513 TextNameHash: THashStrInt = nil; // key: texture name; value: index in `Textures`
514 BadTextNameHash: THashStrInt = nil; // set; so we won't spam with non-existing texture messages
515 RespawnPoints: array of TRespawnPoint;
516 FlagPoints: array[FLAG_RED..FLAG_BLUE] of PFlagPoint;
517 //DOMFlagPoints: Array of TFlagPoint;
520 procedure g_Map_ProfilersBegin ();
521 begin
522 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
523 profMapCollision.mainBegin(g_profile_collision);
524 // create sections
525 if g_profile_collision then
526 begin
527 profMapCollision.sectionBegin('*solids');
528 profMapCollision.sectionEnd();
529 profMapCollision.sectionBegin('liquids');
530 profMapCollision.sectionEnd();
531 end;
532 end;
534 procedure g_Map_ProfilersEnd ();
535 begin
536 if (profMapCollision <> nil) then profMapCollision.mainEnd();
537 end;
540 // wall index in `gWalls` or -1
541 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
542 var
543 ex, ey: Integer;
544 begin
545 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, (GridTagWall or GridTagDoor));
546 if (result <> nil) then
547 begin
548 if (hitx <> nil) then hitx^ := ex;
549 if (hity <> nil) then hity^ := ey;
550 end
551 else
552 begin
553 if (hitx <> nil) then hitx^ := x1;
554 if (hity <> nil) then hity^ := y1;
555 end;
556 end;
558 // returns panel or nil
559 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
560 var
561 ex, ey: Integer;
562 begin
563 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, tag);
564 if (result <> nil) then
565 begin
566 if (hitx <> nil) then hitx^ := ex;
567 if (hity <> nil) then hity^ := ey;
568 end
569 else
570 begin
571 if (hitx <> nil) then hitx^ := x1;
572 if (hity <> nil) then hity^ := y1;
573 end;
574 end;
577 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
579 function checker (pan: TPanel; tag: Integer): Boolean;
580 begin
582 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
583 begin
584 result := pan.Enabled; // stop if wall is enabled
585 exit;
586 end;
589 if ((tag and GridTagLift) <> 0) then
590 begin
591 // stop if the lift of the right type
592 result :=
593 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
594 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
595 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
596 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
597 exit;
598 end;
600 result := true; // otherwise, stop anyway, 'cause `forEachAtPoint()` is guaranteed to call this only for correct panels
601 end;
603 var
604 tagmask: Integer = 0;
605 begin
606 result := false;
608 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
609 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
610 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
611 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
612 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
613 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
614 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
616 if (tagmask = 0) then exit;// just in case
617 if ((tagmask and GridTagLift) <> 0) then
618 begin
619 // slow
620 result := (mapGrid.forEachAtPoint(x, y, checker, tagmask) <> nil);
621 end
622 else
623 begin
624 // fast
625 result := (mapGrid.forEachAtPoint(x, y, nil, tagmask) <> nil);
626 end;
627 end;
630 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
631 begin
632 result := nil;
633 if (tagmask = 0) then exit;
634 result := mapGrid.forEachAtPoint(x, y, nil, tagmask);
635 end;
638 function g_Map_IsSpecialTexture(Texture: String): Boolean;
639 begin
640 Result := (Texture = TEXTURE_NAME_WATER) or
641 (Texture = TEXTURE_NAME_ACID1) or
642 (Texture = TEXTURE_NAME_ACID2);
643 end;
645 procedure CreateDoorMap();
646 var
647 PanelArray: Array of record
648 X, Y: Integer;
649 Width, Height: Word;
650 Active: Boolean;
651 PanelID: DWORD;
652 end;
653 a, b, c, m, i, len: Integer;
654 ok: Boolean;
655 begin
656 if gWalls = nil then
657 Exit;
659 i := 0;
660 len := 128;
661 SetLength(PanelArray, len);
663 for a := 0 to High(gWalls) do
664 if gWalls[a].Door then
665 begin
666 PanelArray[i].X := gWalls[a].X;
667 PanelArray[i].Y := gWalls[a].Y;
668 PanelArray[i].Width := gWalls[a].Width;
669 PanelArray[i].Height := gWalls[a].Height;
670 PanelArray[i].Active := True;
671 PanelArray[i].PanelID := a;
673 i := i + 1;
674 if i = len then
675 begin
676 len := len + 128;
677 SetLength(PanelArray, len);
678 end;
679 end;
681 // Íåò äâåðåé:
682 if i = 0 then
683 begin
684 PanelArray := nil;
685 Exit;
686 end;
688 SetLength(gDoorMap, 0);
690 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
692 for a := 0 to i-1 do
693 if PanelArray[a].Active then
694 begin
695 PanelArray[a].Active := False;
696 m := Length(gDoorMap);
697 SetLength(gDoorMap, m+1);
698 SetLength(gDoorMap[m], 1);
699 gDoorMap[m, 0] := PanelArray[a].PanelID;
700 ok := True;
702 while ok do
703 begin
704 ok := False;
706 for b := 0 to i-1 do
707 if PanelArray[b].Active then
708 for c := 0 to High(gDoorMap[m]) do
709 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
710 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
711 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
712 PanelArray[b].Width, PanelArray[b].Height,
713 gWalls[gDoorMap[m, c]].X,
714 gWalls[gDoorMap[m, c]].Y,
715 gWalls[gDoorMap[m, c]].Width,
716 gWalls[gDoorMap[m, c]].Height) then
717 begin
718 PanelArray[b].Active := False;
719 SetLength(gDoorMap[m],
720 Length(gDoorMap[m])+1);
721 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
722 ok := True;
723 Break;
724 end;
725 end;
727 g_Game_StepLoading();
728 end;
730 PanelArray := nil;
731 end;
733 procedure CreateLiftMap();
734 var
735 PanelArray: Array of record
736 X, Y: Integer;
737 Width, Height: Word;
738 Active: Boolean;
739 end;
740 a, b, c, len, i, j: Integer;
741 ok: Boolean;
742 begin
743 if gLifts = nil then
744 Exit;
746 len := Length(gLifts);
747 SetLength(PanelArray, len);
749 for a := 0 to len-1 do
750 begin
751 PanelArray[a].X := gLifts[a].X;
752 PanelArray[a].Y := gLifts[a].Y;
753 PanelArray[a].Width := gLifts[a].Width;
754 PanelArray[a].Height := gLifts[a].Height;
755 PanelArray[a].Active := True;
756 end;
758 SetLength(gLiftMap, len);
759 i := 0;
761 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
763 for a := 0 to len-1 do
764 if PanelArray[a].Active then
765 begin
766 PanelArray[a].Active := False;
767 SetLength(gLiftMap[i], 32);
768 j := 0;
769 gLiftMap[i, j] := a;
770 ok := True;
772 while ok do
773 begin
774 ok := False;
775 for b := 0 to len-1 do
776 if PanelArray[b].Active then
777 for c := 0 to j do
778 if g_CollideAround(PanelArray[b].X,
779 PanelArray[b].Y,
780 PanelArray[b].Width,
781 PanelArray[b].Height,
782 PanelArray[gLiftMap[i, c]].X,
783 PanelArray[gLiftMap[i, c]].Y,
784 PanelArray[gLiftMap[i, c]].Width,
785 PanelArray[gLiftMap[i, c]].Height) then
786 begin
787 PanelArray[b].Active := False;
788 j := j+1;
789 if j > High(gLiftMap[i]) then
790 SetLength(gLiftMap[i],
791 Length(gLiftMap[i])+32);
793 gLiftMap[i, j] := b;
794 ok := True;
796 Break;
797 end;
798 end;
800 SetLength(gLiftMap[i], j+1);
801 i := i+1;
803 g_Game_StepLoading();
804 end;
806 SetLength(gLiftMap, i);
808 PanelArray := nil;
809 end;
811 function CreatePanel (PanelRec: TDynRecord; AddTextures: TAddTextureArray; CurTex: Integer): Integer;
812 var
813 len: Integer;
814 panels: ^TPanelArray;
815 pan: TPanel;
816 pguid: Integer;
817 begin
818 Result := -1;
820 case PanelRec.PanelType of
821 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: panels := @gWalls;
822 PANEL_BACK: panels := @gRenderBackgrounds;
823 PANEL_FORE: panels := @gRenderForegrounds;
824 PANEL_WATER: panels := @gWater;
825 PANEL_ACID1: panels := @gAcid1;
826 PANEL_ACID2: panels := @gAcid2;
827 PANEL_STEP: panels := @gSteps;
828 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: panels := @gLifts;
829 PANEL_BLOCKMON: panels := @gBlockMon;
830 else exit;
831 end;
833 len := Length(panels^);
834 SetLength(panels^, len+1);
836 pguid := Length(panByGUID);
837 SetLength(panByGUID, pguid+1); //FIXME!
838 pan := TPanel.Create(PanelRec, AddTextures, CurTex, Textures, pguid);
839 assert(pguid >= 0);
840 assert(pguid < Length(panByGUID));
841 panByGUID[pguid] := pan;
842 panels^[len] := pan;
843 pan.arrIdx := len;
844 pan.proxyId := -1;
845 pan.tag := panelTypeToTag(PanelRec.PanelType);
847 PanelRec.user['panel_guid'] := pguid;
849 //result := len;
850 result := pguid;
851 end;
854 function CreateNullTexture(RecName: String): Integer;
855 begin
856 RecName := toLowerCase1251(RecName);
857 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
858 if TextNameHash.get(RecName, result) then exit; // i found her!
860 SetLength(Textures, Length(Textures)+1);
861 result := High(Textures);
863 with Textures[High(Textures)] do
864 begin
865 TextureName := RecName;
866 Width := 1;
867 Height := 1;
868 Anim := False;
869 TextureID := LongWord(TEXTURE_NONE);
870 end;
872 TextNameHash.put(RecName, result);
873 end;
876 function CreateTexture(RecName: AnsiString; Map: string; log: Boolean): Integer;
877 var
878 WAD: TWADFile;
879 TextureData: Pointer;
880 WADName: String;
881 a, ResLength: Integer;
882 begin
883 RecName := toLowerCase1251(RecName);
884 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
885 if TextNameHash.get(RecName, result) then
886 begin
887 // i found her!
888 //e_LogWritefln('texture ''%s'' already loaded', [RecName]);
889 exit;
890 end;
892 Result := -1;
894 if (BadTextNameHash <> nil) and BadTextNameHash.has(RecName) then exit; // don't do it again and again
897 if Textures <> nil then
898 begin
899 for a := 0 to High(Textures) do
900 begin
901 if (Textures[a].TextureName = RecName) then
902 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
903 e_LogWritefln('texture ''%s'' already loaded', [RecName]);
904 Result := a;
905 Exit;
906 end;
907 end;
908 end;
911 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
912 if (RecName = TEXTURE_NAME_WATER) or
913 (RecName = TEXTURE_NAME_ACID1) or
914 (RecName = TEXTURE_NAME_ACID2) then
915 begin
916 SetLength(Textures, Length(Textures)+1);
918 with Textures[High(Textures)] do
919 begin
920 TextureName := RecName;
921 if (TextureName = TEXTURE_NAME_WATER) then TextureID := LongWord(TEXTURE_SPECIAL_WATER)
922 else if (TextureName = TEXTURE_NAME_ACID1) then TextureID := LongWord(TEXTURE_SPECIAL_ACID1)
923 else if (TextureName = TEXTURE_NAME_ACID2) then TextureID := LongWord(TEXTURE_SPECIAL_ACID2);
925 Anim := False;
926 end;
928 result := High(Textures);
929 TextNameHash.put(RecName, result);
930 Exit;
931 end;
933 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
934 WADName := g_ExtractWadName(RecName);
936 WAD := TWADFile.Create();
938 if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
940 WAD.ReadFile(WADName);
942 //txname := RecName;
944 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
945 begin
946 FreeMem(TextureData);
947 RecName := 'COMMON\ALIEN';
948 end;
951 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength, log) then
952 begin
953 SetLength(Textures, Length(Textures)+1);
954 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
955 begin
956 SetLength(Textures, Length(Textures)-1);
957 Exit;
958 end;
959 e_GetTextureSize(Textures[High(Textures)].TextureID, @Textures[High(Textures)].Width, @Textures[High(Textures)].Height);
960 FreeMem(TextureData);
961 Textures[High(Textures)].TextureName := RecName;
962 Textures[High(Textures)].Anim := False;
964 result := High(Textures);
965 TextNameHash.put(RecName, result);
966 end
967 else // Íåò òàêîãî ðåóñðñà â WAD'å
968 begin
969 //e_WriteLog(Format('SHIT! Error loading texture %s : %s', [RecName, g_ExtractFilePathName(RecName)]), MSG_WARNING);
970 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
971 if log and (not BadTextNameHash.get(RecName, a)) then
972 begin
973 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
974 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
975 end;
976 BadTextNameHash.put(RecName, -1);
977 end;
979 WAD.Free();
980 end;
983 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
984 var
985 WAD: TWADFile;
986 TextureWAD: PChar = nil;
987 TextData: Pointer = nil;
988 TextureData: Pointer = nil;
989 cfg: TConfig = nil;
990 WADName: String;
991 ResLength: Integer;
992 TextureResource: String;
993 _width, _height, _framecount, _speed: Integer;
994 _backanimation: Boolean;
995 //imgfmt: string;
996 ia: TDynImageDataArray = nil;
997 f, c, frdelay, frloop: Integer;
998 begin
999 RecName := toLowerCase1251(RecName);
1000 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
1001 if TextNameHash.get(RecName, result) then
1002 begin
1003 // i found her!
1004 //e_LogWritefln('animated texture ''%s'' already loaded', [RecName]);
1005 exit;
1006 end;
1008 result := -1;
1010 //e_LogWritefln('*** Loading animated texture "%s"', [RecName]);
1012 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1013 if BadTextNameHash.get(RecName, f) then
1014 begin
1015 //e_WriteLog(Format('no animation texture %s (don''t worry)', [RecName]), MSG_NOTIFY);
1016 exit;
1017 end;
1019 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
1020 WADName := g_ExtractWadName(RecName);
1022 WAD := TWADFile.Create();
1023 try
1024 if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
1026 WAD.ReadFile(WADName);
1028 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength, log) then
1029 begin
1030 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1031 if log and (not BadTextNameHash.get(RecName, f)) then
1032 begin
1033 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
1034 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
1035 end;
1036 BadTextNameHash.put(RecName, -1);
1037 exit;
1038 end;
1040 {TEST
1041 if WADName = Map then
1042 begin
1043 //FreeMem(TextureWAD);
1044 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
1045 end;
1048 WAD.FreeWAD();
1050 if ResLength < 6 then
1051 begin
1052 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
1053 BadTextNameHash.put(RecName, -1);
1054 exit;
1055 end;
1057 // ýòî ïòèöà? ýòî ñàìîë¸ò?
1058 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
1059 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
1060 begin
1061 // íåò, ýòî ñóïåðìåí!
1062 if not WAD.ReadMemory(TextureWAD, ResLength) then
1063 begin
1064 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
1065 BadTextNameHash.put(RecName, -1);
1066 exit;
1067 end;
1069 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
1070 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
1071 begin
1072 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
1073 BadTextNameHash.put(RecName, -1);
1074 exit;
1075 end;
1077 cfg := TConfig.CreateMem(TextData, ResLength);
1079 TextureResource := cfg.ReadStr('', 'resource', '');
1080 if TextureResource = '' then
1081 begin
1082 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
1083 BadTextNameHash.put(RecName, -1);
1084 exit;
1085 end;
1087 _width := cfg.ReadInt('', 'framewidth', 0);
1088 _height := cfg.ReadInt('', 'frameheight', 0);
1089 _framecount := cfg.ReadInt('', 'framecount', 0);
1090 _speed := cfg.ReadInt('', 'waitcount', 0);
1091 _backanimation := cfg.ReadBool('', 'backanimation', False);
1093 cfg.Free();
1094 cfg := nil;
1096 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
1097 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
1098 begin
1099 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
1100 BadTextNameHash.put(RecName, -1);
1101 exit;
1102 end;
1104 WAD.Free();
1105 WAD := nil;
1107 SetLength(Textures, Length(Textures)+1);
1108 with Textures[High(Textures)] do
1109 begin
1110 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
1111 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
1112 begin
1113 TextureName := RecName;
1114 Width := _width;
1115 Height := _height;
1116 Anim := True;
1117 FramesCount := _framecount;
1118 Speed := _speed;
1119 result := High(Textures);
1120 TextNameHash.put(RecName, result);
1121 end
1122 else
1123 begin
1124 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1125 if log and (not BadTextNameHash.get(RecName, f)) then
1126 begin
1127 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
1128 end;
1129 BadTextNameHash.put(RecName, -1);
1130 end;
1131 end;
1132 end
1133 else
1134 begin
1135 // try animated image
1137 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
1138 if length(imgfmt) = 0 then
1139 begin
1140 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
1141 exit;
1142 end;
1144 GlobalMetadata.ClearMetaItems();
1145 GlobalMetadata.ClearMetaItemsForSaving();
1146 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
1147 begin
1148 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
1149 BadTextNameHash.put(RecName, -1);
1150 exit;
1151 end;
1152 if length(ia) = 0 then
1153 begin
1154 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
1155 BadTextNameHash.put(RecName, -1);
1156 exit;
1157 end;
1159 WAD.Free();
1160 WAD := nil;
1162 _width := ia[0].width;
1163 _height := ia[0].height;
1164 _framecount := length(ia);
1165 _speed := 1;
1166 _backanimation := false;
1167 frdelay := -1;
1168 frloop := -666;
1169 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
1170 begin
1171 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
1172 try
1173 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
1174 frdelay := f;
1175 if f < 0 then f := 0;
1176 // rounding ;-)
1177 c := f mod 28;
1178 if c < 13 then c := 0 else c := 1;
1179 f := (f div 28)+c;
1180 if f < 1 then f := 1 else if f > 255 then f := 255;
1181 _speed := f;
1182 except
1183 end;
1184 end;
1185 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
1186 begin
1187 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
1188 try
1189 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
1190 frloop := f;
1191 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
1192 except
1193 end;
1194 end;
1195 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
1196 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
1197 f := ord(_backanimation);
1198 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);
1200 SetLength(Textures, Length(Textures)+1);
1201 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
1202 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
1203 begin
1204 Textures[High(Textures)].TextureName := RecName;
1205 Textures[High(Textures)].Width := _width;
1206 Textures[High(Textures)].Height := _height;
1207 Textures[High(Textures)].Anim := True;
1208 Textures[High(Textures)].FramesCount := length(ia);
1209 Textures[High(Textures)].Speed := _speed;
1210 result := High(Textures);
1211 TextNameHash.put(RecName, result);
1212 //writeln(' CREATED!');
1213 end
1214 else
1215 begin
1216 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1217 if log and (not BadTextNameHash.get(RecName, f)) then
1218 begin
1219 e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
1220 end;
1221 BadTextNameHash.put(RecName, -1);
1222 end;
1223 end;
1224 finally
1225 for f := 0 to High(ia) do FreeImage(ia[f]);
1226 WAD.Free();
1227 cfg.Free();
1228 if (TextureWAD <> nil) then FreeMem(TextureWAD);
1229 if (TextData <> nil) then FreeMem(TextData);
1230 if (TextureData <> nil) then FreeMem(TextureData);
1231 end;
1232 end;
1234 procedure CreateItem(Item: TDynRecord);
1235 begin
1236 if g_Game_IsClient then Exit;
1238 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
1239 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
1240 Exit;
1242 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
1243 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
1244 end;
1246 procedure CreateArea(Area: TDynRecord);
1247 var
1248 a: Integer;
1249 id: DWORD = 0;
1250 begin
1251 case Area.AreaType of
1252 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
1253 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
1254 begin
1255 SetLength(RespawnPoints, Length(RespawnPoints)+1);
1256 with RespawnPoints[High(RespawnPoints)] do
1257 begin
1258 X := Area.X;
1259 Y := Area.Y;
1260 Direction := TDirection(Area.Direction);
1262 case Area.AreaType of
1263 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
1264 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
1265 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
1266 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
1267 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
1268 end;
1269 end;
1270 end;
1272 AREA_REDFLAG, AREA_BLUEFLAG:
1273 begin
1274 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
1276 if FlagPoints[a] <> nil then Exit;
1278 New(FlagPoints[a]);
1280 with FlagPoints[a]^ do
1281 begin
1282 X := Area.X-FLAGRECT.X;
1283 Y := Area.Y-FLAGRECT.Y;
1284 Direction := TDirection(Area.Direction);
1285 end;
1287 with gFlags[a] do
1288 begin
1289 case a of
1290 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
1291 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
1292 end;
1294 Animation := TAnimation.Create(id, True, 8);
1295 Obj.Rect := FLAGRECT;
1297 g_Map_ResetFlag(a);
1298 end;
1299 end;
1301 AREA_DOMFLAG:
1302 begin
1303 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
1304 with DOMFlagPoints[High(DOMFlagPoints)] do
1305 begin
1306 X := Area.X;
1307 Y := Area.Y;
1308 Direction := TDirection(Area.Direction);
1309 end;
1311 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
1312 end;
1313 end;
1314 end;
1316 function CreateTrigger (amapIdx: Integer; Trigger: TDynRecord; atpanid, atrigpanid: Integer): Integer;
1317 var
1318 _trigger: TTrigger;
1319 begin
1320 result := -1;
1321 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
1323 with _trigger do
1324 begin
1325 mapId := Trigger.id;
1326 mapIndex := amapIdx;
1327 X := Trigger.X;
1328 Y := Trigger.Y;
1329 Width := Trigger.Width;
1330 Height := Trigger.Height;
1331 Enabled := Trigger.Enabled;
1332 TexturePanelGUID := atpanid;
1333 TriggerType := Trigger.TriggerType;
1334 ActivateType := Trigger.ActivateType;
1335 Keys := Trigger.Keys;
1336 trigPanelGUID := atrigpanid;
1337 end;
1339 result := Integer(g_Triggers_Create(_trigger, Trigger));
1340 end;
1342 procedure CreateMonster(monster: TDynRecord);
1343 var
1344 a: Integer;
1345 mon: TMonster;
1346 begin
1347 if g_Game_IsClient then Exit;
1349 if (gGameSettings.GameType = GT_SINGLE)
1350 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1351 begin
1352 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1354 if gTriggers <> nil then
1355 begin
1356 for a := 0 to High(gTriggers) do
1357 begin
1358 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1359 begin
1360 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1361 if (gTriggers[a].trigDataRec.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1362 end;
1363 end;
1364 end;
1366 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1367 end;
1368 end;
1370 procedure g_Map_ReAdd_DieTriggers();
1372 function monsDieTrig (mon: TMonster): Boolean;
1373 var
1374 a: Integer;
1375 //tw: TStrTextWriter;
1376 begin
1377 result := false; // don't stop
1378 mon.ClearTriggers();
1379 for a := 0 to High(gTriggers) do
1380 begin
1381 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1382 begin
1383 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1385 tw := TStrTextWriter.Create();
1386 try
1387 gTriggers[a].trigData.writeTo(tw);
1388 e_LogWritefln('=== trigger #%s ==='#10'%s'#10'---', [a, tw.str]);
1389 finally
1390 tw.Free();
1391 end;
1393 if (gTriggers[a].trigDataRec.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1394 end;
1395 end;
1396 end;
1398 begin
1399 if g_Game_IsClient then Exit;
1401 g_Mons_ForEach(monsDieTrig);
1402 end;
1404 function extractWadName(resourceName: string): string;
1405 var
1406 posN: Integer;
1407 begin
1408 posN := Pos(':', resourceName);
1409 if posN > 0 then
1410 Result:= Copy(resourceName, 0, posN-1)
1411 else
1412 Result := '';
1413 end;
1415 procedure addResToExternalResList(res: string);
1416 begin
1417 res := extractWadName(res);
1418 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1419 gExternalResources.Add(res);
1420 end;
1422 procedure generateExternalResourcesList({mapReader: TMapReader_1}map: TDynRecord);
1423 //var
1424 //textures: TTexturesRec1Array;
1425 //textures: TDynField;
1426 //trec: TDynRecord;
1427 //mapHeader: TMapHeaderRec_1;
1428 //i: integer;
1429 //resFile: String = '';
1430 begin
1431 if gExternalResources = nil then
1432 gExternalResources := TStringList.Create;
1434 gExternalResources.Clear;
1436 (*
1438 textures := GetTextures(map);
1439 for i := 0 to High(textures) do
1440 begin
1441 addResToExternalResList(resFile);
1442 end;
1445 textures := map['texture'];
1446 if (textures <> nil) then
1447 begin
1448 for trec in textures do
1449 begin
1450 addResToExternalResList(resFile);
1451 end;
1452 end;
1454 textures := nil;
1455 *)
1457 //mapHeader := GetMapHeader(map);
1459 addResToExternalResList(map.MusicName);
1460 addResToExternalResList(map.SkyName);
1461 end;
1464 procedure mapCreateGrid ();
1465 var
1466 mapX0: Integer = $3fffffff;
1467 mapY0: Integer = $3fffffff;
1468 mapX1: Integer = -$3fffffff;
1469 mapY1: Integer = -$3fffffff;
1471 procedure calcBoundingBox (constref panels: TPanelArray);
1472 var
1473 idx: Integer;
1474 pan: TPanel;
1475 begin
1476 for idx := 0 to High(panels) do
1477 begin
1478 pan := panels[idx];
1479 if not pan.visvalid then continue;
1480 if (pan.Width < 1) or (pan.Height < 1) then continue;
1481 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1482 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1483 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1484 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1485 end;
1486 end;
1488 procedure addPanelsToGrid (constref panels: TPanelArray);
1489 var
1490 idx: Integer;
1491 pan: TPanel;
1492 newtag: Integer;
1493 begin
1494 //tag := panelTypeToTag(tag);
1495 for idx := 0 to High(panels) do
1496 begin
1497 pan := panels[idx];
1498 if not pan.visvalid then continue;
1499 if (pan.proxyId <> -1) then
1500 begin
1501 {$IF DEFINED(D2F_DEBUG)}
1502 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);
1503 {$ENDIF}
1504 continue;
1505 end;
1506 case pan.PanelType of
1507 PANEL_WALL: newtag := GridTagWall;
1508 PANEL_OPENDOOR, PANEL_CLOSEDOOR: newtag := GridTagDoor;
1509 PANEL_BACK: newtag := GridTagBack;
1510 PANEL_FORE: newtag := GridTagFore;
1511 PANEL_WATER: newtag := GridTagWater;
1512 PANEL_ACID1: newtag := GridTagAcid1;
1513 PANEL_ACID2: newtag := GridTagAcid2;
1514 PANEL_STEP: newtag := GridTagStep;
1515 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: newtag := GridTagLift;
1516 PANEL_BLOCKMON: newtag := GridTagBlockMon;
1517 else continue; // oops
1518 end;
1519 pan.tag := newtag;
1521 pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, newtag);
1522 // "enabled" flag has meaning only for doors and walls (engine assumes it); but meh...
1523 mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
1524 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
1526 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
1527 begin
1528 e_WriteLog(Format('INSERTED wall #%d(%d) enabled (%d)', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY);
1529 end;
1531 {$ENDIF}
1532 end;
1533 end;
1535 begin
1536 mapGrid.Free();
1537 mapGrid := nil;
1539 calcBoundingBox(gWalls);
1540 calcBoundingBox(gRenderBackgrounds);
1541 calcBoundingBox(gRenderForegrounds);
1542 calcBoundingBox(gWater);
1543 calcBoundingBox(gAcid1);
1544 calcBoundingBox(gAcid2);
1545 calcBoundingBox(gSteps);
1546 calcBoundingBox(gLifts);
1547 calcBoundingBox(gBlockMon);
1549 e_LogWritefln('map dimensions: (%d,%d)-(%d,%d); editor size:(0,0)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1, gMapInfo.Width, gMapInfo.Height]);
1551 if (mapX0 > 0) then mapX0 := 0;
1552 if (mapY0 > 0) then mapY0 := 0;
1554 if (mapX1 < gMapInfo.Width-1) then mapX1 := gMapInfo.Width-1;
1555 if (mapY1 < gMapInfo.Height-1) then mapY1 := gMapInfo.Height-1;
1557 mapGrid := TPanelGrid.Create(mapX0-128, mapY0-128, mapX1-mapX0+1+128*2, mapY1-mapY0+1+128*2);
1558 //mapGrid := TPanelGrid.Create(0, 0, gMapInfo.Width, gMapInfo.Height);
1560 addPanelsToGrid(gWalls);
1561 addPanelsToGrid(gRenderBackgrounds);
1562 addPanelsToGrid(gRenderForegrounds);
1563 addPanelsToGrid(gWater);
1564 addPanelsToGrid(gAcid1);
1565 addPanelsToGrid(gAcid2);
1566 addPanelsToGrid(gSteps);
1567 addPanelsToGrid(gLifts); // it doesn't matter which LIFT type is used here
1568 addPanelsToGrid(gBlockMon);
1570 mapGrid.dumpStats();
1572 g_Mons_InitTree(mapGrid.gridX0, mapGrid.gridY0, mapGrid.gridWidth, mapGrid.gridHeight);
1573 end;
1576 function g_Map_Load(Res: String): Boolean;
1577 const
1578 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1579 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1580 type
1581 PTRec = ^TTRec;
1582 TTRec = record
1583 //TexturePanel: Integer;
1584 tnum: Integer;
1585 id: Integer;
1586 trigrec: TDynRecord;
1587 // texture pane;
1588 texPanelIdx: Integer;
1589 texPanel: TDynRecord;
1590 // "action" panel
1591 actPanelIdx: Integer;
1592 actPanel: TDynRecord;
1593 end;
1594 var
1595 WAD: TWADFile;
1596 //mapReader: TDynRecord = nil;
1597 mapTextureList: TDynField = nil; //TTexturesRec1Array; tagInt: texture index
1598 panels: TDynField = nil; //TPanelsRec1Array;
1599 items: TDynField = nil; //TItemsRec1Array;
1600 monsters: TDynField = nil; //TMonsterRec1Array;
1601 areas: TDynField = nil; //TAreasRec1Array;
1602 triggers: TDynField = nil; //TTriggersRec1Array;
1603 b, c, k: Integer;
1604 PanelID: DWORD;
1605 AddTextures: TAddTextureArray;
1606 TriggersTable: array of TTRec;
1607 FileName, mapResName, s, TexName: String;
1608 Data: Pointer;
1609 Len: Integer;
1610 ok, isAnim: Boolean;
1611 CurTex, ntn: Integer;
1612 rec, texrec: TDynRecord;
1613 pttit: PTRec;
1614 pannum, trignum, cnt, tgpid: Integer;
1615 stt: UInt64;
1616 moveSpeed{, moveStart, moveEnd}: TDFPoint;
1617 //moveActive: Boolean;
1618 pan: TPanel;
1619 mapOk: Boolean = false;
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 if (gCurrentMap = nil) then
1638 begin
1639 FileName := g_ExtractWadName(Res);
1640 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1641 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1643 WAD := TWADFile.Create();
1644 if not WAD.ReadFile(FileName) then
1645 begin
1646 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1647 WAD.Free();
1648 Exit;
1649 end;
1651 //k8: why loader ignores path here?
1652 mapResName := g_ExtractFileName(Res);
1653 if not WAD.GetMapResource(mapResName, Data, Len) then
1654 begin
1655 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1656 WAD.Free();
1657 Exit;
1658 end;
1660 WAD.Free();
1662 if (Len < 4) then
1663 begin
1664 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1665 FreeMem(Data);
1666 exit;
1667 end;
1669 // Çàãðóçêà êàðòû:
1670 e_LogWritefln('Loading map: %s', [mapResName], MSG_NOTIFY);
1671 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1673 stt := getTimeMicro();
1675 try
1676 gCurrentMap := g_Map_ParseMap(Data, Len);
1677 except
1678 gCurrentMap.Free();
1679 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1680 FreeMem(Data);
1681 gCurrentMapFileName := '';
1682 Exit;
1683 end;
1685 FreeMem(Data);
1687 if (gCurrentMap = nil) then
1688 begin
1689 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1690 gCurrentMapFileName := '';
1691 exit;
1692 end;
1693 end
1694 else
1695 begin
1696 stt := getTimeMicro();
1697 end;
1699 //gCurrentMap := mapReader;
1701 generateExternalResourcesList(gCurrentMap);
1702 mapTextureList := gCurrentMap['texture'];
1703 // get all other lists here too
1704 panels := gCurrentMap['panel'];
1705 triggers := gCurrentMap['trigger'];
1706 items := gCurrentMap['item'];
1707 areas := gCurrentMap['area'];
1708 monsters := gCurrentMap['monster'];
1710 // Çàãðóçêà îïèñàíèÿ êàðòû:
1711 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1712 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1714 with gMapInfo do
1715 begin
1716 Name := gCurrentMap.MapName;
1717 Description := gCurrentMap.MapDesc;
1718 Author := gCurrentMap.MapAuthor;
1719 MusicName := gCurrentMap.MusicName;
1720 SkyName := gCurrentMap.SkyName;
1721 Height := gCurrentMap.Height;
1722 Width := gCurrentMap.Width;
1723 end;
1725 // Çàãðóçêà òåêñòóð:
1726 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1727 // Äîáàâëåíèå òåêñòóð â Textures[]:
1728 if (mapTextureList <> nil) and (mapTextureList.count > 0) then
1729 begin
1730 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1731 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], mapTextureList.count-1, False);
1733 cnt := -1;
1734 for rec in mapTextureList do
1735 begin
1736 Inc(cnt);
1737 s := rec.Resource;
1738 {$IF DEFINED(D2F_DEBUG_TXLOAD)}
1739 e_WriteLog(Format(' Loading texture #%d: %s', [cnt, s]), MSG_NOTIFY);
1740 {$ENDIF}
1741 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1742 if rec.Anim then
1743 begin
1744 // Àíèìèðîâàííàÿ òåêñòóðà
1745 ntn := CreateAnimTexture(rec.Resource, FileName, True);
1746 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1747 end
1748 else
1749 begin
1750 // Îáû÷íàÿ òåêñòóðà
1751 ntn := CreateTexture(rec.Resource, FileName, True);
1752 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1753 end;
1754 if (ntn < 0) then ntn := CreateNullTexture(rec.Resource);
1756 rec.tagInt := ntn; // remember texture number
1757 g_Game_StepLoading();
1758 end;
1760 // set panel tagInt to texture index
1761 if (panels <> nil) then
1762 begin
1763 for rec in panels do
1764 begin
1765 texrec := rec.TextureRec;
1766 if (texrec = nil) then rec.tagInt := -1 else rec.tagInt := texrec.tagInt;
1767 end;
1768 end;
1769 end;
1771 // Çàãðóçêà òðèããåðîâ
1772 gTriggerClientID := 0;
1773 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1774 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1776 // Çàãðóçêà ïàíåëåé
1777 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1778 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1780 // check texture numbers for panels
1781 if (panels <> nil) and (panels.count > 0) then
1782 begin
1783 for rec in panels do
1784 begin
1785 if (rec.tagInt < 0) then
1786 begin
1787 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1788 result := false;
1789 gCurrentMap.Free();
1790 gCurrentMap := nil;
1791 gCurrentMapFileName := '';
1792 exit;
1793 end;
1794 end;
1795 end;
1797 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì)
1798 if (triggers <> nil) and (triggers.count > 0) then
1799 begin
1800 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1801 //SetLength(TriggersTable, triggers.count);
1802 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], triggers.count-1, False);
1804 SetLength(TriggersTable, triggers.count);
1805 trignum := -1;
1806 for rec in triggers do
1807 begin
1808 Inc(trignum);
1809 pttit := @TriggersTable[trignum];
1810 pttit.trigrec := rec;
1811 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè)
1812 pttit.texPanelIdx := -1; // will be fixed later
1813 pttit.texPanel := rec.TexturePanelRec;
1814 // action panel
1815 pttit.actPanelIdx := -1;
1816 if (rec.trigRec <> nil) then pttit.actPanel := rec.trigRec.tgPanelRec else pttit.actPanel := nil;
1817 // set flag
1818 if (pttit.texPanel <> nil) then pttit.texPanel.userPanelTrigRef := true;
1819 if (pttit.actPanel <> nil) then pttit.actPanel.userPanelTrigRef := true;
1820 // update progress
1821 g_Game_StepLoading();
1822 end;
1823 end;
1825 // Ñîçäàåì ïàíåëè
1826 if (panels <> nil) and (panels.count > 0) then
1827 begin
1828 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1829 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], panels.count-1, False);
1831 pannum := -1;
1832 for rec in panels do
1833 begin
1834 Inc(pannum);
1835 //e_LogWritefln('PANSTART: pannum=%s', [pannum]);
1836 texrec := nil;
1837 SetLength(AddTextures, 0);
1838 CurTex := -1;
1839 ok := false;
1841 if (mapTextureList <> nil) then
1842 begin
1843 texrec := rec.TextureRec;
1844 ok := (texrec <> nil);
1845 end;
1847 if ok then
1848 begin
1849 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1850 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð
1851 ok := false;
1852 if (TriggersTable <> nil) and (mapTextureList <> nil) then
1853 begin
1854 if rec.userPanelTrigRef then
1855 begin
1856 // e_LogWritefln('trigref for panel %s', [pannum]);
1857 ok := True;
1858 end;
1859 end;
1860 end;
1862 if ok then
1863 begin
1864 // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1865 s := texrec.Resource;
1867 // Ñïåö-òåêñòóðû çàïðåùåíû
1868 if g_Map_IsSpecialTexture(s) then
1869 begin
1870 ok := false
1871 end
1872 else
1873 begin
1874 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè
1875 ok := g_Texture_NumNameFindStart(s);
1876 end;
1878 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1879 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #
1880 if ok then
1881 begin
1882 k := NNF_NAME_BEFORE;
1883 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû
1884 while ok or (k = NNF_NAME_BEFORE) or (k = NNF_NAME_EQUALS) do
1885 begin
1886 k := g_Texture_NumNameFindNext(TexName);
1888 if (k = NNF_NAME_BEFORE) or (k = NNF_NAME_AFTER) then
1889 begin
1890 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó
1891 if texrec.Anim then
1892 begin
1893 // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1894 isAnim := True;
1895 //e_LogWritefln('000: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1896 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1897 //e_LogWritefln('001: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1898 if not ok then
1899 begin
1900 // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1901 isAnim := False;
1902 //e_LogWritefln('002: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1903 ok := CreateTexture(TexName, FileName, False) >= 0;
1904 //e_LogWritefln('003: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1905 end;
1906 end
1907 else
1908 begin
1909 // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1910 isAnim := False;
1911 //e_LogWritefln('004: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1912 ok := CreateTexture(TexName, FileName, False) >= 0;
1913 //e_LogWritefln('005: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1914 if not ok then
1915 begin
1916 // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1917 isAnim := True;
1918 //e_LogWritefln('006: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1919 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1920 //e_LogWritefln('007: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1921 end;
1922 end;
1924 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè
1925 if ok then
1926 begin
1928 for c := 0 to High(Textures) do
1929 begin
1930 if (Textures[c].TextureName = TexName) then
1931 begin
1932 SetLength(AddTextures, Length(AddTextures)+1);
1933 AddTextures[High(AddTextures)].Texture := c;
1934 AddTextures[High(AddTextures)].Anim := isAnim;
1935 break;
1936 end;
1937 end;
1939 if (TextNameHash <> nil) and TextNameHash.get(toLowerCase1251(TexName), c) then
1940 begin
1941 SetLength(AddTextures, Length(AddTextures)+1);
1942 AddTextures[High(AddTextures)].Texture := c;
1943 AddTextures[High(AddTextures)].Anim := isAnim;
1944 end;
1945 end;
1946 end
1947 else
1948 begin
1949 if k = NNF_NAME_EQUALS then
1950 begin
1951 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî
1952 SetLength(AddTextures, Length(AddTextures)+1);
1953 AddTextures[High(AddTextures)].Texture := rec.tagInt; // internal texture number, not map index
1954 AddTextures[High(AddTextures)].Anim := texrec.Anim;
1955 CurTex := High(AddTextures);
1956 ok := true;
1957 end
1958 else // NNF_NO_NAME
1959 begin
1960 ok := false;
1961 end;
1962 end;
1963 end; // while ok...
1965 ok := true;
1966 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1967 end; // if ok - ññûëàþòñÿ òðèããåðû
1969 if not ok then
1970 begin
1971 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó
1972 SetLength(AddTextures, 1);
1973 AddTextures[0].Texture := rec.tagInt; // internal texture number, not map index
1974 AddTextures[0].Anim := false;
1975 if (texrec <> nil) then AddTextures[0].Anim := texrec.Anim;
1976 CurTex := 0;
1977 end;
1979 //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);
1981 //e_LogWritefln('PANADD: pannum=%s', [pannum]);
1983 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå GUID
1984 PanelID := CreatePanel(rec, AddTextures, CurTex);
1985 //e_LogWritefln('panel #%s of type %s got guid #%s', [pannum, rec.PanelType, PanelID]);
1986 rec.userPanelId := PanelID; // remember game panel id, we'll fix triggers later
1988 // setup lifts
1989 moveSpeed := rec.moveSpeed;
1990 //moveStart := rec.moveStart;
1991 //moveEnd := rec.moveEnd;
1992 //moveActive := rec['move_active'].value;
1993 if not moveSpeed.isZero then
1994 begin
1995 SetLength(gMovingWallIds, Length(gMovingWallIds)+1);
1996 gMovingWallIds[High(gMovingWallIds)] := PanelID;
1997 //e_LogWritefln('found moving panel ''%s'' (idx=%s; id=%s)', [rec.id, pannum, PanelID]);
1998 end;
2000 //e_LogWritefln('PANEND: pannum=%s', [pannum]);
2002 g_Game_StepLoading();
2003 end;
2004 end;
2006 // ×èíèì ID'û ïàíåëåé, êîòîðûå èñïîëüçóþòñÿ â òðèããåðàõ
2007 for b := 0 to High(TriggersTable) do
2008 begin
2009 if (TriggersTable[b].texPanel <> nil) then TriggersTable[b].texPanelIdx := TriggersTable[b].texPanel.userPanelId;
2010 if (TriggersTable[b].actPanel <> nil) then TriggersTable[b].actPanelIdx := TriggersTable[b].actPanel.userPanelId;
2011 end;
2013 // create map grid, init other grids (for monsters, for example)
2014 e_WriteLog('Creating map grid', MSG_NOTIFY);
2015 mapCreateGrid();
2017 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû
2018 if (triggers <> nil) and (panels <> nil) and (not gLoadGameMode) then
2019 begin
2020 e_LogWritefln(' Creating triggers (%d)...', [triggers.count]);
2021 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
2022 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü
2023 trignum := -1;
2024 for rec in triggers do
2025 begin
2026 Inc(trignum);
2027 tgpid := TriggersTable[trignum].actPanelIdx;
2028 //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]);
2029 TriggersTable[trignum].tnum := trignum;
2030 TriggersTable[trignum].id := CreateTrigger(trignum, rec, TriggersTable[trignum].texPanelIdx, tgpid);
2031 end;
2032 end;
2034 //FIXME: use hashtable!
2035 for pan in panByGUID do
2036 begin
2037 if (pan.endPosTrigId >= 0) and (pan.endPosTrigId < Length(TriggersTable)) then
2038 begin
2039 pan.endPosTrigId := TriggersTable[pan.endPosTrigId].id;
2040 end;
2041 if (pan.endSizeTrigId >= 0) and (pan.endSizeTrigId < Length(TriggersTable)) then
2042 begin
2043 pan.endSizeTrigId := TriggersTable[pan.endSizeTrigId].id;
2044 end;
2045 end;
2047 // Çàãðóçêà ïðåäìåòîâ
2048 e_WriteLog(' Loading items...', MSG_NOTIFY);
2049 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
2051 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû
2052 if (items <> nil) and not gLoadGameMode then
2053 begin
2054 e_WriteLog(' Spawning items...', MSG_NOTIFY);
2055 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
2056 for rec in items do CreateItem(rec);
2057 end;
2059 // Çàãðóçêà îáëàñòåé
2060 e_WriteLog(' Loading areas...', MSG_NOTIFY);
2061 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
2063 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè
2064 if areas <> nil then
2065 begin
2066 e_WriteLog(' Creating areas...', MSG_NOTIFY);
2067 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
2068 for rec in areas do CreateArea(rec);
2069 end;
2071 // Çàãðóçêà ìîíñòðîâ
2072 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
2073 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
2075 gTotalMonsters := 0;
2077 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ
2078 if (monsters <> nil) and not gLoadGameMode then
2079 begin
2080 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
2081 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
2082 for rec in monsters do CreateMonster(rec);
2083 end;
2085 //gCurrentMap := mapReader; // this will be our current map now
2086 gCurrentMapFileName := Res;
2087 //mapReader := nil;
2089 // Çàãðóçêà íåáà
2090 if (gMapInfo.SkyName <> '') then
2091 begin
2092 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
2093 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
2094 FileName := g_ExtractWadName(gMapInfo.SkyName);
2096 if (FileName <> '') then FileName := GameDir+'/wads/'+FileName else FileName := g_ExtractWadName(Res);
2098 if gTextureFilter then TEXTUREFILTER := GL_LINEAR else TEXTUREFILTER := GL_NEAREST;
2099 try
2100 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
2101 if g_Texture_CreateWAD(BackID, s) then
2102 begin
2103 g_Game_SetupScreenSize();
2104 end
2105 else
2106 begin
2107 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
2108 end;
2109 finally
2110 TEXTUREFILTER := GL_NEAREST;
2111 end;
2112 end;
2114 // Çàãðóçêà ìóçûêè
2115 ok := False;
2116 if gMapInfo.MusicName <> '' then
2117 begin
2118 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
2119 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
2120 FileName := g_ExtractWadName(gMapInfo.MusicName);
2122 if FileName <> '' then
2123 FileName := GameDir+'/wads/'+FileName
2124 else
2125 begin
2126 FileName := g_ExtractWadName(Res);
2127 end;
2129 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
2130 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
2131 ok := True
2132 else
2133 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
2134 end;
2136 // Îñòàëüíûå óñòàíâêè
2137 CreateDoorMap();
2138 CreateLiftMap();
2140 g_Items_Init();
2141 g_Weapon_Init();
2142 g_Monsters_Init();
2144 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
2145 if not gLoadGameMode then g_GFX_Init();
2147 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
2148 mapTextureList := nil;
2149 panels := nil;
2150 items := nil;
2151 areas := nil;
2152 triggers := nil;
2153 TriggersTable := nil;
2154 AddTextures := nil;
2156 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
2157 if ok and (not gLoadGameMode) then
2158 begin
2159 gMusic.SetByName(gMapInfo.MusicName);
2160 gMusic.Play();
2161 end
2162 else
2163 begin
2164 gMusic.SetByName('');
2165 end;
2167 stt := getTimeMicro()-stt;
2168 e_LogWritefln('map loaded in %s.%s milliseconds', [Integer(stt div 1000), Integer(stt mod 1000)]);
2169 mapOk := true;
2170 finally
2171 sfsGCEnable(); // enable releasing unused volumes
2172 //mapReader.Free();
2173 e_ClearInputBuffer(); // why not?
2174 if not mapOk then
2175 begin
2176 gCurrentMap.Free();
2177 gCurrentMap := nil;
2178 gCurrentMapFileName := '';
2179 end;
2180 end;
2182 e_WriteLog('Done loading map.', MSG_NOTIFY);
2183 Result := True;
2184 end;
2187 function g_Map_GetMapInfo(Res: String): TMapInfo;
2188 var
2189 WAD: TWADFile;
2190 mapReader: TDynRecord;
2191 //Header: TMapHeaderRec_1;
2192 FileName: String;
2193 Data: Pointer;
2194 Len: Integer;
2195 begin
2196 FillChar(Result, SizeOf(Result), 0);
2197 FileName := g_ExtractWadName(Res);
2199 WAD := TWADFile.Create();
2200 if not WAD.ReadFile(FileName) then
2201 begin
2202 WAD.Free();
2203 Exit;
2204 end;
2206 //k8: it ignores path again
2207 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
2208 begin
2209 WAD.Free();
2210 Exit;
2211 end;
2213 WAD.Free();
2215 try
2216 mapReader := g_Map_ParseMap(Data, Len);
2217 except
2218 mapReader := nil;
2219 FreeMem(Data);
2220 exit;
2221 end;
2223 FreeMem(Data);
2225 if (mapReader = nil) then exit;
2227 if (mapReader.Width > 0) and (mapReader.Height > 0) then
2228 begin
2229 Result.Name := mapReader.MapName;
2230 Result.Description := mapReader.MapDesc;
2231 Result.Map := Res;
2232 Result.Author := mapReader.MapAuthor;
2233 Result.Height := mapReader.Height;
2234 Result.Width := mapReader.Width;
2235 end
2236 else
2237 begin
2238 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2239 //ZeroMemory(@Header, SizeOf(Header));
2240 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2241 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2242 Result.Map := Res;
2243 Result.Author := '';
2244 Result.Height := 0;
2245 Result.Width := 0;
2246 end;
2248 mapReader.Free();
2249 end;
2251 function g_Map_GetMapsList(WADName: string): SArray;
2252 var
2253 WAD: TWADFile;
2254 a: Integer;
2255 ResList: SArray;
2256 begin
2257 Result := nil;
2258 WAD := TWADFile.Create();
2259 if not WAD.ReadFile(WADName) then
2260 begin
2261 WAD.Free();
2262 Exit;
2263 end;
2264 ResList := WAD.GetMapResources();
2265 if ResList <> nil then
2266 begin
2267 for a := 0 to High(ResList) do
2268 begin
2269 SetLength(Result, Length(Result)+1);
2270 Result[High(Result)] := ResList[a];
2271 end;
2272 end;
2273 WAD.Free();
2274 end;
2276 function g_Map_Exist(Res: string): Boolean;
2277 var
2278 WAD: TWADFile;
2279 FileName, mnn: string;
2280 ResList: SArray;
2281 a: Integer;
2282 begin
2283 Result := False;
2285 FileName := addWadExtension(g_ExtractWadName(Res));
2287 WAD := TWADFile.Create;
2288 if not WAD.ReadFile(FileName) then
2289 begin
2290 WAD.Free();
2291 Exit;
2292 end;
2294 ResList := WAD.GetMapResources();
2295 WAD.Free();
2297 mnn := g_ExtractFileName(Res);
2298 if ResList <> nil then
2299 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
2300 begin
2301 Result := True;
2302 Exit;
2303 end;
2304 end;
2306 procedure g_Map_Free(freeTextures: Boolean=true);
2307 var
2308 a: Integer;
2310 procedure FreePanelArray(var panels: TPanelArray);
2311 var
2312 i: Integer;
2314 begin
2315 if panels <> nil then
2316 begin
2317 for i := 0 to High(panels) do
2318 panels[i].Free();
2319 panels := nil;
2320 end;
2321 end;
2323 begin
2324 g_GFX_Free();
2325 g_Weapon_Free();
2326 g_Items_Free();
2327 g_Triggers_Free();
2328 g_Monsters_Free();
2330 RespawnPoints := nil;
2331 if FlagPoints[FLAG_RED] <> nil then
2332 begin
2333 Dispose(FlagPoints[FLAG_RED]);
2334 FlagPoints[FLAG_RED] := nil;
2335 end;
2336 if FlagPoints[FLAG_BLUE] <> nil then
2337 begin
2338 Dispose(FlagPoints[FLAG_BLUE]);
2339 FlagPoints[FLAG_BLUE] := nil;
2340 end;
2341 //DOMFlagPoints := nil;
2343 //gDOMFlags := nil;
2345 if (Length(gCurrentMapFileName) <> 0) then
2346 begin
2347 e_LogWritefln('g_Map_Free: previous map was ''%s''...', [gCurrentMapFileName]);
2348 end
2349 else
2350 begin
2351 e_LogWritefln('g_Map_Free: no previous map.', []);
2352 end;
2354 if freeTextures then
2355 begin
2356 e_LogWritefln('g_Map_Free: clearing textures...', []);
2357 if (Textures <> nil) then
2358 begin
2359 for a := 0 to High(Textures) do
2360 begin
2361 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
2362 begin
2363 if Textures[a].Anim then
2364 begin
2365 g_Frames_DeleteByID(Textures[a].FramesID)
2366 end
2367 else
2368 begin
2369 if (Textures[a].TextureID <> LongWord(TEXTURE_NONE)) then
2370 begin
2371 e_DeleteTexture(Textures[a].TextureID);
2372 end;
2373 end;
2374 end;
2375 end;
2376 Textures := nil;
2377 end;
2378 TextNameHash.Free();
2379 TextNameHash := nil;
2380 BadTextNameHash.Free();
2381 BadTextNameHash := nil;
2382 gCurrentMapFileName := '';
2383 gCurrentMap.Free();
2384 gCurrentMap := nil;
2385 end;
2387 panByGUID := nil;
2389 FreePanelArray(gWalls);
2390 FreePanelArray(gRenderBackgrounds);
2391 FreePanelArray(gRenderForegrounds);
2392 FreePanelArray(gWater);
2393 FreePanelArray(gAcid1);
2394 FreePanelArray(gAcid2);
2395 FreePanelArray(gSteps);
2396 FreePanelArray(gLifts);
2397 FreePanelArray(gBlockMon);
2398 gMovingWallIds := nil;
2400 if BackID <> DWORD(-1) then
2401 begin
2402 gBackSize.X := 0;
2403 gBackSize.Y := 0;
2404 e_DeleteTexture(BackID);
2405 BackID := DWORD(-1);
2406 end;
2408 g_Game_StopAllSounds(False);
2409 gMusic.FreeSound();
2410 g_Sound_Delete(gMapInfo.MusicName);
2412 gMapInfo.Name := '';
2413 gMapInfo.Description := '';
2414 gMapInfo.MusicName := '';
2415 gMapInfo.Height := 0;
2416 gMapInfo.Width := 0;
2418 gDoorMap := nil;
2419 gLiftMap := nil;
2420 end;
2422 procedure g_Map_Update();
2423 var
2424 a, d, j: Integer;
2425 m: Word;
2426 s: String;
2428 procedure UpdatePanelArray(var panels: TPanelArray);
2429 var
2430 i: Integer;
2432 begin
2433 for i := 0 to High(panels) do panels[i].Update();
2434 end;
2436 begin
2437 if g_dbgpan_mplat_step then g_dbgpan_mplat_active := true;
2439 UpdatePanelArray(gWalls);
2440 UpdatePanelArray(gRenderBackgrounds);
2441 UpdatePanelArray(gRenderForegrounds);
2442 UpdatePanelArray(gWater);
2443 UpdatePanelArray(gAcid1);
2444 UpdatePanelArray(gAcid2);
2445 UpdatePanelArray(gSteps);
2447 if g_dbgpan_mplat_step then begin g_dbgpan_mplat_step := false; g_dbgpan_mplat_active := false; end;
2449 if gGameSettings.GameMode = GM_CTF then
2450 begin
2451 for a := FLAG_RED to FLAG_BLUE do
2452 begin
2453 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2454 begin
2455 with gFlags[a] do
2456 begin
2457 if gFlags[a].Animation <> nil then gFlags[a].Animation.Update();
2459 m := g_Obj_Move(@Obj, True, True);
2461 if gTime mod (GAME_TICK*2) <> 0 then Continue;
2463 // Ñîïðîòèâëåíèå âîçäóõà
2464 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2466 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó
2467 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2468 begin
2469 g_Map_ResetFlag(a);
2470 gFlags[a].CaptureTime := 0;
2471 if a = FLAG_RED then
2472 s := _lc[I_PLAYER_FLAG_RED]
2473 else
2474 s := _lc[I_PLAYER_FLAG_BLUE];
2475 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2477 if g_Game_IsNet then
2478 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2479 Continue;
2480 end;
2482 if Count > 0 then Count -= 1;
2484 // Èãðîê áåðåò ôëàã
2485 if gPlayers <> nil then
2486 begin
2487 j := Random(Length(gPlayers)) - 1;
2488 for d := 0 to High(gPlayers) do
2489 begin
2490 Inc(j);
2491 if j > High(gPlayers) then j := 0;
2492 if gPlayers[j] <> nil then
2493 begin
2494 if gPlayers[j].alive and g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2495 begin
2496 if gPlayers[j].GetFlag(a) then Break;
2497 end;
2498 end;
2499 end;
2500 end;
2501 end;
2502 end;
2503 end;
2504 end;
2505 end;
2508 // old algo
2509 procedure g_Map_DrawPanels (PanelType: Word; hasAmbient: Boolean; constref ambColor: TDFColor);
2511 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2512 var
2513 idx: Integer;
2514 begin
2515 if (panels <> nil) then
2516 begin
2517 // alas, no visible set
2518 for idx := 0 to High(panels) do
2519 begin
2520 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw(hasAmbient, ambColor);
2521 end;
2522 end;
2523 end;
2525 begin
2526 case PanelType of
2527 PANEL_WALL: DrawPanels(gWalls);
2528 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2529 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2530 PANEL_FORE: DrawPanels(gRenderForegrounds);
2531 PANEL_WATER: DrawPanels(gWater);
2532 PANEL_ACID1: DrawPanels(gAcid1);
2533 PANEL_ACID2: DrawPanels(gAcid2);
2534 PANEL_STEP: DrawPanels(gSteps);
2535 end;
2536 end;
2539 // new algo
2540 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2542 function checker (pan: TPanel; tag: Integer): Boolean;
2543 begin
2544 result := false; // don't stop, ever
2545 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2546 gDrawPanelList.insert(pan);
2547 end;
2549 begin
2550 dplClear();
2551 //tagmask := panelTypeToTag(PanelType);
2552 mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask);
2553 // list will be rendered in `g_game.DrawPlayer()`
2554 end;
2557 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2559 function checker (pan: TPanel; tag: Integer): Boolean;
2560 begin
2561 result := false; // don't stop, ever
2562 pan.DrawShadowVolume(lightX, lightY, radius);
2563 end;
2565 begin
2566 mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2567 end;
2570 procedure g_Map_DrawBack(dx, dy: Integer);
2571 begin
2572 if gDrawBackGround and (BackID <> DWORD(-1)) then
2573 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2574 else
2575 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2576 end;
2578 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2579 PanelType: Word; b1x3: Boolean=false): Boolean;
2580 var
2581 a, h: Integer;
2582 begin
2583 Result := False;
2585 if WordBool(PanelType and PANEL_WALL) then
2586 if gWalls <> nil then
2587 begin
2588 h := High(gWalls);
2590 for a := 0 to h do
2591 if gWalls[a].Enabled and
2592 g_Collide(X, Y, Width, Height,
2593 gWalls[a].X, gWalls[a].Y,
2594 gWalls[a].Width, gWalls[a].Height) then
2595 begin
2596 Result := True;
2597 Exit;
2598 end;
2599 end;
2601 if WordBool(PanelType and PANEL_WATER) then
2602 if gWater <> nil then
2603 begin
2604 h := High(gWater);
2606 for a := 0 to h do
2607 if g_Collide(X, Y, Width, Height,
2608 gWater[a].X, gWater[a].Y,
2609 gWater[a].Width, gWater[a].Height) then
2610 begin
2611 Result := True;
2612 Exit;
2613 end;
2614 end;
2616 if WordBool(PanelType and PANEL_ACID1) then
2617 if gAcid1 <> nil then
2618 begin
2619 h := High(gAcid1);
2621 for a := 0 to h do
2622 if g_Collide(X, Y, Width, Height,
2623 gAcid1[a].X, gAcid1[a].Y,
2624 gAcid1[a].Width, gAcid1[a].Height) then
2625 begin
2626 Result := True;
2627 Exit;
2628 end;
2629 end;
2631 if WordBool(PanelType and PANEL_ACID2) then
2632 if gAcid2 <> nil then
2633 begin
2634 h := High(gAcid2);
2636 for a := 0 to h do
2637 if g_Collide(X, Y, Width, Height,
2638 gAcid2[a].X, gAcid2[a].Y,
2639 gAcid2[a].Width, gAcid2[a].Height) then
2640 begin
2641 Result := True;
2642 Exit;
2643 end;
2644 end;
2646 if WordBool(PanelType and PANEL_STEP) then
2647 if gSteps <> nil then
2648 begin
2649 h := High(gSteps);
2651 for a := 0 to h do
2652 if g_Collide(X, Y, Width, Height,
2653 gSteps[a].X, gSteps[a].Y,
2654 gSteps[a].Width, gSteps[a].Height) then
2655 begin
2656 Result := True;
2657 Exit;
2658 end;
2659 end;
2661 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2662 if gLifts <> nil then
2663 begin
2664 h := High(gLifts);
2666 for a := 0 to h do
2667 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2668 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2669 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2670 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2671 g_Collide(X, Y, Width, Height,
2672 gLifts[a].X, gLifts[a].Y,
2673 gLifts[a].Width, gLifts[a].Height) then
2674 begin
2675 Result := True;
2676 Exit;
2677 end;
2678 end;
2680 if WordBool(PanelType and PANEL_BLOCKMON) then
2681 if gBlockMon <> nil then
2682 begin
2683 h := High(gBlockMon);
2685 for a := 0 to h do
2686 if ( (not b1x3) or
2687 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2688 g_Collide(X, Y, Width, Height,
2689 gBlockMon[a].X, gBlockMon[a].Y,
2690 gBlockMon[a].Width, gBlockMon[a].Height) then
2691 begin
2692 Result := True;
2693 Exit;
2694 end;
2695 end;
2696 end;
2698 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2699 var
2700 texid: DWORD;
2702 function checkPanels (constref panels: TPanelArray): Boolean;
2703 var
2704 a: Integer;
2705 begin
2706 result := false;
2707 if panels = nil then exit;
2708 for a := 0 to High(panels) do
2709 begin
2710 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2711 begin
2712 result := true;
2713 texid := panels[a].GetTextureID();
2714 exit;
2715 end;
2716 end;
2717 end;
2719 begin
2720 texid := LongWord(TEXTURE_NONE);
2721 result := texid;
2722 if not checkPanels(gWater) then
2723 if not checkPanels(gAcid1) then
2724 if not checkPanels(gAcid2) then exit;
2725 result := texid;
2726 end;
2729 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2730 const
2731 SlowMask = GridTagLift or GridTagBlockMon;
2732 function checker (pan: TPanel; tag: Integer): Boolean;
2733 begin
2735 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2736 begin
2737 result := pan.Enabled;
2738 exit;
2739 end;
2742 if ((tag and GridTagLift) <> 0) then
2743 begin
2744 result :=
2745 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2746 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2747 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2748 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
2749 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
2750 exit;
2751 end;
2753 if ((tag and GridTagBlockMon) <> 0) then
2754 begin
2755 result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2756 exit;
2757 end;
2759 // other shit
2760 //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2761 result := true; // i found her!
2762 end;
2764 var
2765 tagmask: Integer = 0;
2766 begin
2767 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2768 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2769 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2770 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2771 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2772 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2773 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2775 if (tagmask = 0) then begin result := false; exit; end; // just in case
2777 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2778 if gdbg_map_use_accel_coldet then
2779 begin
2780 if (Width = 1) and (Height = 1) then
2781 begin
2782 if ((tagmask and SlowMask) <> 0) then
2783 begin
2784 // slow
2785 result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil);
2786 end
2787 else
2788 begin
2789 // fast
2790 result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil);
2791 end;
2792 end
2793 else
2794 begin
2795 if ((tagmask and SlowMask) <> 0) then
2796 begin
2797 // slow
2798 result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil);
2799 end
2800 else
2801 begin
2802 // fast
2803 result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil);
2804 end;
2805 end;
2806 end
2807 else
2808 begin
2809 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2810 end;
2811 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2812 end;
2815 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2816 var
2817 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2818 texid: DWORD;
2820 // slightly different from the old code, but meh...
2821 function checker (pan: TPanel; tag: Integer): Boolean;
2822 begin
2823 result := false; // don't stop, ever
2824 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2825 // check priorities
2826 case cctype of
2827 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2828 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2829 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2830 end;
2831 // collision?
2832 //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2833 // yeah
2834 texid := pan.GetTextureID();
2835 // water? water has the highest priority, so stop right here
2836 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2837 // acid2?
2838 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2839 // acid1?
2840 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2841 end;
2843 begin
2844 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2845 if gdbg_map_use_accel_coldet then
2846 begin
2847 texid := LongWord(TEXTURE_NONE);
2848 if (Width = 1) and (Height = 1) then
2849 begin
2850 mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2851 end
2852 else
2853 begin
2854 mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2855 end;
2856 result := texid;
2857 end
2858 else
2859 begin
2860 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2861 end;
2862 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2863 end;
2866 procedure g_Map_EnableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_EnableWallGUID(gWalls[ID].guid); end;
2867 procedure g_Map_DisableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_DisableWallGUID(gWalls[ID].guid); end;
2868 procedure g_Map_SetLift_XXX (ID: DWORD; t: Integer); begin if (ID < Length(gLifts)) then g_Map_SetLiftGUID(gLifts[ID].guid, t); end;
2871 procedure g_Map_EnableWallGUID (pguid: Integer);
2872 var
2873 pan: TPanel;
2874 begin
2875 //pan := gWalls[ID];
2876 pan := g_Map_PanelByGUID(pguid);
2877 if (pan = nil) then exit;
2878 if pan.Enabled and mapGrid.proxyEnabled[pan.proxyId] then exit;
2880 pan.Enabled := True;
2881 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, true);
2883 mapGrid.proxyEnabled[pan.proxyId] := true;
2884 //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
2885 //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
2887 //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pguid);
2888 // mark platform as interesting
2889 pan.setDirty();
2891 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2892 //e_WriteLog(Format('ENABLE: wall #%d(%d) enabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY);
2893 {$ENDIF}
2894 end;
2897 procedure g_Map_DisableWallGUID (pguid: Integer);
2898 var
2899 pan: TPanel;
2900 begin
2901 //pan := gWalls[ID];
2902 pan := g_Map_PanelByGUID(pguid);
2903 if (pan = nil) then exit;
2904 if (not pan.Enabled) and (not mapGrid.proxyEnabled[pan.proxyId]) then exit;
2906 pan.Enabled := False;
2907 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, false);
2909 mapGrid.proxyEnabled[pan.proxyId] := false;
2910 //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
2912 //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pguid);
2913 // mark platform as interesting
2914 pan.setDirty();
2916 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2917 //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);
2918 {$ENDIF}
2919 end;
2922 procedure g_Map_SwitchTextureGUID (pguid: Integer; AnimLoop: Byte = 0);
2923 var
2924 tp: TPanel;
2925 begin
2926 tp := g_Map_PanelByGUID(pguid);
2927 if (tp = nil) then exit;
2928 tp.NextTexture(AnimLoop);
2929 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelTexture(pguid, AnimLoop);
2930 end;
2933 procedure g_Map_SetLiftGUID (pguid: Integer; t: Integer);
2934 var
2935 pan: TPanel;
2936 begin
2937 //pan := gLifts[ID];
2938 pan := g_Map_PanelByGUID(pguid);
2939 if (pan = nil) then exit;
2940 if not pan.isGLift then exit;
2942 if ({gLifts[ID]}pan.LiftType = t) then exit; //!FIXME!TRIGANY!
2944 with {gLifts[ID]} pan do
2945 begin
2946 LiftType := t;
2948 g_Mark(X, Y, Width, Height, MARK_LIFT, false);
2949 //TODO: make separate lift tags, and change tag here
2951 case LiftType of
2952 0: g_Mark(X, Y, Width, Height, MARK_LIFTUP);
2953 1: g_Mark(X, Y, Width, Height, MARK_LIFTDOWN);
2954 2: g_Mark(X, Y, Width, Height, MARK_LIFTLEFT);
2955 3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT);
2956 end;
2958 //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pguid);
2959 // mark platform as interesting
2960 pan.setDirty();
2961 end;
2962 end;
2965 function g_Map_GetPoint (PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2966 var
2967 a: Integer;
2968 PointsArray: Array of TRespawnPoint;
2969 begin
2970 Result := False;
2971 SetLength(PointsArray, 0);
2973 if RespawnPoints = nil then
2974 Exit;
2976 for a := 0 to High(RespawnPoints) do
2977 if RespawnPoints[a].PointType = PointType then
2978 begin
2979 SetLength(PointsArray, Length(PointsArray)+1);
2980 PointsArray[High(PointsArray)] := RespawnPoints[a];
2981 end;
2983 if PointsArray = nil then
2984 Exit;
2986 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2987 Result := True;
2988 end;
2990 function g_Map_GetPointCount(PointType: Byte): Word;
2991 var
2992 a: Integer;
2993 begin
2994 Result := 0;
2996 if RespawnPoints = nil then
2997 Exit;
2999 for a := 0 to High(RespawnPoints) do
3000 if RespawnPoints[a].PointType = PointType then
3001 Result := Result + 1;
3002 end;
3004 function g_Map_HaveFlagPoints(): Boolean;
3005 begin
3006 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
3007 end;
3009 procedure g_Map_ResetFlag(Flag: Byte);
3010 begin
3011 with gFlags[Flag] do
3012 begin
3013 Obj.X := -1000;
3014 Obj.Y := -1000;
3015 Obj.Vel.X := 0;
3016 Obj.Vel.Y := 0;
3017 Direction := D_LEFT;
3018 State := FLAG_STATE_NONE;
3019 if FlagPoints[Flag] <> nil then
3020 begin
3021 Obj.X := FlagPoints[Flag]^.X;
3022 Obj.Y := FlagPoints[Flag]^.Y;
3023 Direction := FlagPoints[Flag]^.Direction;
3024 State := FLAG_STATE_NORMAL;
3025 end;
3026 Count := -1;
3027 end;
3028 end;
3030 procedure g_Map_DrawFlags();
3031 var
3032 i, dx: Integer;
3033 Mirror: TMirrorType;
3034 begin
3035 if gGameSettings.GameMode <> GM_CTF then
3036 Exit;
3038 for i := FLAG_RED to FLAG_BLUE do
3039 with gFlags[i] do
3040 if State <> FLAG_STATE_CAPTURED then
3041 begin
3042 if State = FLAG_STATE_NONE then
3043 continue;
3045 if Direction = D_LEFT then
3046 begin
3047 Mirror := M_HORIZONTAL;
3048 dx := -1;
3049 end
3050 else
3051 begin
3052 Mirror := M_NONE;
3053 dx := 1;
3054 end;
3056 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
3058 if g_debug_Frames then
3059 begin
3060 e_DrawQuad(Obj.X+Obj.Rect.X,
3061 Obj.Y+Obj.Rect.Y,
3062 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
3063 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
3064 0, 255, 0);
3065 end;
3066 end;
3067 end;
3070 procedure g_Map_SaveState (st: TStream);
3071 var
3072 str: String;
3074 procedure savePanels ();
3075 var
3076 pan: TPanel;
3077 begin
3078 // Ñîõðàíÿåì ïàíåëè
3079 utils.writeInt(st, LongInt(Length(panByGUID)));
3080 for pan in panByGUID do pan.SaveState(st);
3081 end;
3083 procedure saveFlag (flag: PFlag);
3084 var
3085 b: Byte;
3086 begin
3087 utils.writeSign(st, 'FLAG');
3088 utils.writeInt(st, Byte(0)); // version
3089 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3090 utils.writeInt(st, Byte(flag^.RespawnType));
3091 // Ñîñòîÿíèå ôëàãà
3092 utils.writeInt(st, Byte(flag^.State));
3093 // Íàïðàâëåíèå ôëàãà
3094 if flag^.Direction = D_LEFT then b := 1 else b := 2; // D_RIGHT
3095 utils.writeInt(st, Byte(b));
3096 // Îáúåêò ôëàãà
3097 Obj_SaveState(st, @flag^.Obj);
3098 end;
3100 begin
3101 savePanels();
3103 // Ñîõðàíÿåì ìóçûêó
3104 utils.writeSign(st, 'MUSI');
3105 utils.writeInt(st, Byte(0));
3106 // Íàçâàíèå ìóçûêè
3107 assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
3108 if gMusic.NoMusic then str := '' else str := gMusic.Name;
3109 utils.writeStr(st, str);
3110 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3111 utils.writeInt(st, LongWord(gMusic.GetPosition()));
3112 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3113 utils.writeBool(st, gMusic.SpecPause);
3115 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
3116 utils.writeInt(st, LongInt(gTotalMonsters));
3117 ///// /////
3119 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
3120 if (gGameSettings.GameMode = GM_CTF) then
3121 begin
3122 // Ôëàã Êðàñíîé êîìàíäû
3123 saveFlag(@gFlags[FLAG_RED]);
3124 // Ôëàã Ñèíåé êîìàíäû
3125 saveFlag(@gFlags[FLAG_BLUE]);
3126 end;
3127 ///// /////
3129 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3130 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3131 begin
3132 // Î÷êè Êðàñíîé êîìàíäû
3133 utils.writeInt(st, SmallInt(gTeamStat[TEAM_RED].Goals));
3134 // Î÷êè Ñèíåé êîìàíäû
3135 utils.writeInt(st, SmallInt(gTeamStat[TEAM_BLUE].Goals));
3136 end;
3137 ///// /////
3138 end;
3141 procedure g_Map_LoadState (st: TStream);
3142 var
3143 dw: DWORD;
3144 str: String;
3145 boo: Boolean;
3147 procedure loadPanels ();
3148 var
3149 pan: TPanel;
3150 begin
3151 // Çàãðóæàåì ïàíåëè
3152 if (Length(panByGUID) <> utils.readLongInt(st)) then raise XStreamError.Create('invalid number of saved panels');
3153 for pan in panByGUID do
3154 begin
3155 pan.LoadState(st);
3156 if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
3157 end;
3158 end;
3160 procedure loadFlag (flag: PFlag);
3161 var
3162 b: Byte;
3163 begin
3164 // Ñèãíàòóðà ôëàãà
3165 if not utils.checkSign(st, 'FLAG') then raise XStreamError.Create('invalid flag signature');
3166 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid flag version');
3167 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3168 flag^.RespawnType := utils.readByte(st);
3169 // Ñîñòîÿíèå ôëàãà
3170 flag^.State := utils.readByte(st);
3171 // Íàïðàâëåíèå ôëàãà
3172 b := utils.readByte(st);
3173 if (b = 1) then flag^.Direction := D_LEFT else flag^.Direction := D_RIGHT; // b = 2
3174 // Îáúåêò ôëàãà
3175 Obj_LoadState(@flag^.Obj, st);
3176 end;
3178 begin
3179 if (st = nil) then exit;
3181 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
3182 loadPanels();
3183 ///// /////
3185 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó
3186 g_GFX_Init();
3187 //mapCreateGrid();
3189 ///// Çàãðóæàåì ìóçûêó: /////
3190 if not utils.checkSign(st, 'MUSI') then raise XStreamError.Create('invalid music signature');
3191 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid music version');
3192 // Íàçâàíèå ìóçûêè
3193 assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
3194 str := utils.readStr(st);
3195 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3196 dw := utils.readLongWord(st);
3197 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3198 boo := utils.readBool(st);
3199 // Çàïóñêàåì ýòó ìóçûêó
3200 gMusic.SetByName(str);
3201 gMusic.SpecPause := boo;
3202 gMusic.Play();
3203 gMusic.Pause(true);
3204 gMusic.SetPosition(dw);
3205 ///// /////
3207 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
3208 gTotalMonsters := utils.readLongInt(st);
3209 ///// /////
3211 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
3212 if (gGameSettings.GameMode = GM_CTF) then
3213 begin
3214 // Ôëàã Êðàñíîé êîìàíäû
3215 loadFlag(@gFlags[FLAG_RED]);
3216 // Ôëàã Ñèíåé êîìàíäû
3217 loadFlag(@gFlags[FLAG_BLUE]);
3218 end;
3219 ///// /////
3221 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3222 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3223 begin
3224 // Î÷êè Êðàñíîé êîìàíäû
3225 gTeamStat[TEAM_RED].Goals := utils.readSmallInt(st);
3226 // Î÷êè Ñèíåé êîìàíäû
3227 gTeamStat[TEAM_BLUE].Goals := utils.readSmallInt(st);
3228 end;
3229 ///// /////
3230 end;
3233 // trace liquid, stepping by `dx` and `dy`
3234 // return last seen liquid coords, and `false` if we're started outside of the liquid
3235 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
3236 const
3237 MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
3238 begin
3239 topx := x;
3240 topy := y;
3241 // started outside of the liquid?
3242 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end;
3243 if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check
3244 result := true;
3245 while true do
3246 begin
3247 Inc(x, dx);
3248 Inc(y, dy);
3249 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit
3250 topx := x;
3251 topy := y;
3252 end;
3253 end;
3256 end.