DEADSOFTWARE

parser and parser-dependent modules cosmetix
[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 // ////////////////////////////////////////////////////////////////////////// //
261 procedure mapWarningCB (const msg: AnsiString; line, col: Integer);
262 begin
263 if (line > 0) then
264 begin
265 e_LogWritefln('parse error at (%s,%s): %s', [line, col, msg], TMsgType.Warning);
266 end
267 else
268 begin
269 e_LogWritefln('parse error: %s', [msg], TMsgType.Warning);
270 end;
271 end;
274 // ////////////////////////////////////////////////////////////////////////// //
275 var
276 panByGUID: array of TPanel = nil;
279 // ////////////////////////////////////////////////////////////////////////// //
280 function g_Map_PanelByGUID (aguid: Integer): TPanel; inline;
281 begin
282 //if (panByGUID = nil) or (not panByGUID.get(aguid, result)) then result := nil;
283 if (aguid >= 0) and (aguid < Length(panByGUID)) then result := panByGUID[aguid] else result := nil;
284 end;
287 // return `true` from `cb` to stop
288 function g_Map_ForEachPanel (cb: TForEachPanelCB): TPanel;
289 var
290 pan: TPanel;
291 begin
292 result := nil;
293 if not assigned(cb) then exit;
294 for pan in panByGUID do
295 begin
296 if cb(pan) then begin result := pan; exit; end;
297 end;
298 end;
301 procedure g_Map_NetSendInterestingPanels ();
302 var
303 pan: TPanel;
304 begin
305 if g_Game_IsServer and g_Game_IsNet then
306 begin
307 for pan in panByGUID do
308 begin
309 if pan.gncNeedSend then MH_SEND_PanelState(pan.guid);
310 end;
311 end;
312 end;
315 // ////////////////////////////////////////////////////////////////////////// //
316 function g_Map_MinX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0 else result := 0; end;
317 function g_Map_MinY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0 else result := 0; end;
318 function g_Map_MaxX (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridX0+mapGrid.gridWidth-1 else result := 0; end;
319 function g_Map_MaxY (): Integer; inline; begin if (mapGrid <> nil) then result := mapGrid.gridY0+mapGrid.gridHeight-1 else result := 0; end;
322 // ////////////////////////////////////////////////////////////////////////// //
323 var
324 dfmapdef: TDynMapDef = nil;
327 procedure loadMapDefinition ();
328 var
329 pr: TTextParser = nil;
330 st: TStream = nil;
331 WAD: TWADFile = nil;
332 begin
333 if (dfmapdef <> nil) then exit;
335 try
336 e_LogWritefln('parsing "mapdef.txt"...', []);
337 st := openDiskFileRO(DataDir+'mapdef.txt');
338 e_LogWritefln('found local "%smapdef.txt"', [DataDir]);
339 except
340 st := nil;
341 end;
343 if (st = nil) then
344 begin
345 WAD := TWADFile.Create();
346 if not WAD.ReadFile(GameWAD) then
347 begin
348 //raise Exception.Create('cannot load "game.wad"');
349 st := nil;
350 end
351 else
352 begin
353 st := WAD.openFileStream('mapdef.txt');
354 end;
355 end;
357 try
358 if (st = nil) then
359 begin
360 //raise Exception.Create('cannot open "mapdef.txt"');
361 e_LogWriteln('using default "mapdef.txt"...');
362 pr := TStrTextParser.Create(defaultMapDef);
363 end
364 else
365 begin
366 pr := TFileTextParser.Create(st);
367 end;
368 except on e: Exception do
369 begin
370 e_LogWritefln('something is VERY wrong here! -- ', [e.message]);
371 raise;
372 end;
373 end;
375 try
376 dfmapdef := TDynMapDef.Create(pr);
377 except
378 on e: TDynParseException do
379 raise Exception.CreateFmt('ERROR in "mapdef.txt" at (%s,%s): %s', [e.tokLine, e.tokCol, e.message]);
380 on e: Exception do
381 raise Exception.CreateFmt('ERROR in "mapdef.txt" at (%s,%s): %s', [pr.tokLine, pr.tokCol, e.message]);
382 end;
384 st.Free();
385 WAD.Free();
386 end;
389 // ////////////////////////////////////////////////////////////////////////// //
390 function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord;
391 var
392 wst: TSFSMemoryChunkStream = nil;
393 begin
394 result := nil;
395 if (dataLen < 4) then exit;
397 if (dfmapdef = nil) then writeln('need to load mapdef');
398 loadMapDefinition();
399 if (dfmapdef = nil) then raise Exception.Create('internal map loader error');
401 wst := TSFSMemoryChunkStream.Create(data, dataLen);
402 try
403 result := dfmapdef.parseMap(wst);
404 except
405 on e: TDynParseException do
406 begin
407 e_LogWritefln('ERROR at (%s,%s): %s', [e.tokLine, e.tokCol, e.message]);
408 wst.Free();
409 result := nil;
410 exit;
411 end;
412 on e: Exception do
413 begin
414 e_LogWritefln('ERROR: %s', [e.message]);
415 wst.Free();
416 result := nil;
417 exit;
418 end;
419 end;
421 //e_LogWriteln('map parsed.');
422 end;
425 // ////////////////////////////////////////////////////////////////////////// //
426 var
427 NNF_PureName: String; // Èìÿ òåêñòóðû áåç öèôð â êîíöå
428 NNF_FirstNum: Integer; // ×èñëî ó íà÷àëüíîé òåêñòóðû
429 NNF_CurrentNum: Integer; // Ñëåäóþùåå ÷èñëî ó òåêñòóðû
432 function g_Texture_NumNameFindStart(name: String): Boolean;
433 var
434 i: Integer;
436 begin
437 Result := False;
438 NNF_PureName := '';
439 NNF_FirstNum := -1;
440 NNF_CurrentNum := -1;
442 for i := Length(name) downto 1 do
443 if (name[i] = '_') then // "_" - ñèìâîë íà÷àëà íîìåðíîãî ïîñòôèêñà
444 begin
445 if i = Length(name) then
446 begin // Íåò öèôð â êîíöå ñòðîêè
447 Exit;
448 end
449 else
450 begin
451 NNF_PureName := Copy(name, 1, i);
452 Delete(name, 1, i);
453 Break;
454 end;
455 end;
457 // Íå ïåðåâåñòè â ÷èñëî:
458 if not TryStrToInt(name, NNF_FirstNum) then
459 Exit;
461 NNF_CurrentNum := 0;
463 Result := True;
464 end;
467 function g_Texture_NumNameFindNext(var newName: String): Byte;
468 begin
469 if (NNF_PureName = '') or (NNF_CurrentNum < 0) then
470 begin
471 newName := '';
472 Result := NNF_NO_NAME;
473 Exit;
474 end;
476 newName := NNF_PureName + IntToStr(NNF_CurrentNum);
478 if NNF_CurrentNum < NNF_FirstNum then
479 Result := NNF_NAME_BEFORE
480 else
481 if NNF_CurrentNum > NNF_FirstNum then
482 Result := NNF_NAME_AFTER
483 else
484 Result := NNF_NAME_EQUALS;
486 Inc(NNF_CurrentNum);
487 end;
490 // ////////////////////////////////////////////////////////////////////////// //
491 function panelTypeToTag (panelType: Word): Integer;
492 begin
493 case panelType of
494 PANEL_WALL: result := GridTagWall; // gWalls
495 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
496 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
497 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
498 PANEL_WATER: result := GridTagWater; // gWater
499 PANEL_ACID1: result := GridTagAcid1; // gAcid1
500 PANEL_ACID2: result := GridTagAcid2; // gAcid2
501 PANEL_STEP: result := GridTagStep; // gSteps
502 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
503 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
504 else result := GridTagInvalid;
505 end;
506 end;
509 function dplLess (a, b: TObject): Boolean;
510 var
511 pa, pb: TPanel;
512 begin
513 pa := TPanel(a);
514 pb := TPanel(b);
515 if (pa.tag < pb.tag) then begin result := true; exit; end;
516 if (pa.tag > pb.tag) then begin result := false; exit; end;
517 result := (pa.arrIdx < pb.arrIdx);
518 end;
520 procedure dplClear ();
521 begin
522 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
523 end;
526 var
527 Textures: TLevelTextureArray = nil;
528 TextNameHash: THashStrInt = nil; // key: texture name; value: index in `Textures`
529 BadTextNameHash: THashStrInt = nil; // set; so we won't spam with non-existing texture messages
530 RespawnPoints: array of TRespawnPoint;
531 FlagPoints: array[FLAG_RED..FLAG_BLUE] of PFlagPoint;
532 //DOMFlagPoints: Array of TFlagPoint;
535 procedure g_Map_ProfilersBegin ();
536 begin
537 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
538 profMapCollision.mainBegin(g_profile_collision);
539 // create sections
540 if g_profile_collision then
541 begin
542 profMapCollision.sectionBegin('*solids');
543 profMapCollision.sectionEnd();
544 profMapCollision.sectionBegin('liquids');
545 profMapCollision.sectionEnd();
546 end;
547 end;
549 procedure g_Map_ProfilersEnd ();
550 begin
551 if (profMapCollision <> nil) then profMapCollision.mainEnd();
552 end;
555 // wall index in `gWalls` or -1
556 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
557 var
558 ex, ey: Integer;
559 begin
560 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, (GridTagWall or GridTagDoor));
561 if (result <> nil) then
562 begin
563 if (hitx <> nil) then hitx^ := ex;
564 if (hity <> nil) then hity^ := ey;
565 end
566 else
567 begin
568 if (hitx <> nil) then hitx^ := x1;
569 if (hity <> nil) then hity^ := y1;
570 end;
571 end;
573 // returns panel or nil
574 function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel;
575 var
576 ex, ey: Integer;
577 begin
578 result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, tag);
579 if (result <> nil) then
580 begin
581 if (hitx <> nil) then hitx^ := ex;
582 if (hity <> nil) then hity^ := ey;
583 end
584 else
585 begin
586 if (hitx <> nil) then hitx^ := x1;
587 if (hity <> nil) then hity^ := y1;
588 end;
589 end;
592 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
594 function checker (pan: TPanel; tag: Integer): Boolean;
595 begin
597 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
598 begin
599 result := pan.Enabled; // stop if wall is enabled
600 exit;
601 end;
604 if ((tag and GridTagLift) <> 0) then
605 begin
606 // stop if the lift of the right type
607 result :=
608 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
609 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
610 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
611 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
612 exit;
613 end;
615 result := true; // otherwise, stop anyway, 'cause `forEachAtPoint()` is guaranteed to call this only for correct panels
616 end;
618 var
619 tagmask: Integer = 0;
620 begin
621 result := false;
623 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
624 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
625 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
626 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
627 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
628 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
629 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
631 if (tagmask = 0) then exit;// just in case
632 if ((tagmask and GridTagLift) <> 0) then
633 begin
634 // slow
635 result := (mapGrid.forEachAtPoint(x, y, checker, tagmask) <> nil);
636 end
637 else
638 begin
639 // fast
640 result := (mapGrid.forEachAtPoint(x, y, nil, tagmask) <> nil);
641 end;
642 end;
645 function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
646 begin
647 result := nil;
648 if (tagmask = 0) then exit;
649 result := mapGrid.forEachAtPoint(x, y, nil, tagmask);
650 end;
653 function g_Map_IsSpecialTexture(Texture: String): Boolean;
654 begin
655 Result := (Texture = TEXTURE_NAME_WATER) or
656 (Texture = TEXTURE_NAME_ACID1) or
657 (Texture = TEXTURE_NAME_ACID2);
658 end;
660 procedure CreateDoorMap();
661 var
662 PanelArray: Array of record
663 X, Y: Integer;
664 Width, Height: Word;
665 Active: Boolean;
666 PanelID: DWORD;
667 end;
668 a, b, c, m, i, len: Integer;
669 ok: Boolean;
670 begin
671 if gWalls = nil then
672 Exit;
674 i := 0;
675 len := 128;
676 SetLength(PanelArray, len);
678 for a := 0 to High(gWalls) do
679 if gWalls[a].Door then
680 begin
681 PanelArray[i].X := gWalls[a].X;
682 PanelArray[i].Y := gWalls[a].Y;
683 PanelArray[i].Width := gWalls[a].Width;
684 PanelArray[i].Height := gWalls[a].Height;
685 PanelArray[i].Active := True;
686 PanelArray[i].PanelID := a;
688 i := i + 1;
689 if i = len then
690 begin
691 len := len + 128;
692 SetLength(PanelArray, len);
693 end;
694 end;
696 // Íåò äâåðåé:
697 if i = 0 then
698 begin
699 PanelArray := nil;
700 Exit;
701 end;
703 SetLength(gDoorMap, 0);
705 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
707 for a := 0 to i-1 do
708 if PanelArray[a].Active then
709 begin
710 PanelArray[a].Active := False;
711 m := Length(gDoorMap);
712 SetLength(gDoorMap, m+1);
713 SetLength(gDoorMap[m], 1);
714 gDoorMap[m, 0] := PanelArray[a].PanelID;
715 ok := True;
717 while ok do
718 begin
719 ok := False;
721 for b := 0 to i-1 do
722 if PanelArray[b].Active then
723 for c := 0 to High(gDoorMap[m]) do
724 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
725 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
726 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
727 PanelArray[b].Width, PanelArray[b].Height,
728 gWalls[gDoorMap[m, c]].X,
729 gWalls[gDoorMap[m, c]].Y,
730 gWalls[gDoorMap[m, c]].Width,
731 gWalls[gDoorMap[m, c]].Height) then
732 begin
733 PanelArray[b].Active := False;
734 SetLength(gDoorMap[m],
735 Length(gDoorMap[m])+1);
736 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
737 ok := True;
738 Break;
739 end;
740 end;
742 g_Game_StepLoading();
743 end;
745 PanelArray := nil;
746 end;
748 procedure CreateLiftMap();
749 var
750 PanelArray: Array of record
751 X, Y: Integer;
752 Width, Height: Word;
753 Active: Boolean;
754 end;
755 a, b, c, len, i, j: Integer;
756 ok: Boolean;
757 begin
758 if gLifts = nil then
759 Exit;
761 len := Length(gLifts);
762 SetLength(PanelArray, len);
764 for a := 0 to len-1 do
765 begin
766 PanelArray[a].X := gLifts[a].X;
767 PanelArray[a].Y := gLifts[a].Y;
768 PanelArray[a].Width := gLifts[a].Width;
769 PanelArray[a].Height := gLifts[a].Height;
770 PanelArray[a].Active := True;
771 end;
773 SetLength(gLiftMap, len);
774 i := 0;
776 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
778 for a := 0 to len-1 do
779 if PanelArray[a].Active then
780 begin
781 PanelArray[a].Active := False;
782 SetLength(gLiftMap[i], 32);
783 j := 0;
784 gLiftMap[i, j] := a;
785 ok := True;
787 while ok do
788 begin
789 ok := False;
790 for b := 0 to len-1 do
791 if PanelArray[b].Active then
792 for c := 0 to j do
793 if g_CollideAround(PanelArray[b].X,
794 PanelArray[b].Y,
795 PanelArray[b].Width,
796 PanelArray[b].Height,
797 PanelArray[gLiftMap[i, c]].X,
798 PanelArray[gLiftMap[i, c]].Y,
799 PanelArray[gLiftMap[i, c]].Width,
800 PanelArray[gLiftMap[i, c]].Height) then
801 begin
802 PanelArray[b].Active := False;
803 j := j+1;
804 if j > High(gLiftMap[i]) then
805 SetLength(gLiftMap[i],
806 Length(gLiftMap[i])+32);
808 gLiftMap[i, j] := b;
809 ok := True;
811 Break;
812 end;
813 end;
815 SetLength(gLiftMap[i], j+1);
816 i := i+1;
818 g_Game_StepLoading();
819 end;
821 SetLength(gLiftMap, i);
823 PanelArray := nil;
824 end;
826 function CreatePanel (PanelRec: TDynRecord; AddTextures: TAddTextureArray; CurTex: Integer): Integer;
827 var
828 len: Integer;
829 panels: ^TPanelArray;
830 pan: TPanel;
831 pguid: Integer;
832 begin
833 Result := -1;
835 case PanelRec.PanelType of
836 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: panels := @gWalls;
837 PANEL_BACK: panels := @gRenderBackgrounds;
838 PANEL_FORE: panels := @gRenderForegrounds;
839 PANEL_WATER: panels := @gWater;
840 PANEL_ACID1: panels := @gAcid1;
841 PANEL_ACID2: panels := @gAcid2;
842 PANEL_STEP: panels := @gSteps;
843 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: panels := @gLifts;
844 PANEL_BLOCKMON: panels := @gBlockMon;
845 else exit;
846 end;
848 len := Length(panels^);
849 SetLength(panels^, len+1);
851 pguid := Length(panByGUID);
852 SetLength(panByGUID, pguid+1); //FIXME!
853 pan := TPanel.Create(PanelRec, AddTextures, CurTex, Textures, pguid);
854 assert(pguid >= 0);
855 assert(pguid < Length(panByGUID));
856 panByGUID[pguid] := pan;
857 panels^[len] := pan;
858 pan.arrIdx := len;
859 pan.proxyId := -1;
860 pan.tag := panelTypeToTag(PanelRec.PanelType);
862 PanelRec.user['panel_guid'] := pguid;
864 //result := len;
865 result := pguid;
866 end;
869 function CreateNullTexture(RecName: String): Integer;
870 begin
871 RecName := toLowerCase1251(RecName);
872 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
873 if TextNameHash.get(RecName, result) then exit; // i found her!
875 SetLength(Textures, Length(Textures)+1);
876 result := High(Textures);
878 with Textures[High(Textures)] do
879 begin
880 TextureName := RecName;
881 Width := 1;
882 Height := 1;
883 Anim := False;
884 TextureID := LongWord(TEXTURE_NONE);
885 end;
887 TextNameHash.put(RecName, result);
888 end;
891 function CreateTexture(RecName: AnsiString; Map: string; log: Boolean): Integer;
892 var
893 WAD: TWADFile;
894 TextureData: Pointer;
895 WADName: String;
896 a, ResLength: Integer;
897 begin
898 RecName := toLowerCase1251(RecName);
899 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
900 if TextNameHash.get(RecName, result) then
901 begin
902 // i found her!
903 //e_LogWritefln('texture ''%s'' already loaded', [RecName]);
904 exit;
905 end;
907 Result := -1;
909 if (BadTextNameHash <> nil) and BadTextNameHash.has(RecName) then exit; // don't do it again and again
912 if Textures <> nil then
913 begin
914 for a := 0 to High(Textures) do
915 begin
916 if (Textures[a].TextureName = RecName) then
917 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
918 e_LogWritefln('texture ''%s'' already loaded', [RecName]);
919 Result := a;
920 Exit;
921 end;
922 end;
923 end;
926 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
927 if (RecName = TEXTURE_NAME_WATER) or
928 (RecName = TEXTURE_NAME_ACID1) or
929 (RecName = TEXTURE_NAME_ACID2) then
930 begin
931 SetLength(Textures, Length(Textures)+1);
933 with Textures[High(Textures)] do
934 begin
935 TextureName := RecName;
936 if (TextureName = TEXTURE_NAME_WATER) then TextureID := LongWord(TEXTURE_SPECIAL_WATER)
937 else if (TextureName = TEXTURE_NAME_ACID1) then TextureID := LongWord(TEXTURE_SPECIAL_ACID1)
938 else if (TextureName = TEXTURE_NAME_ACID2) then TextureID := LongWord(TEXTURE_SPECIAL_ACID2);
940 Anim := False;
941 end;
943 result := High(Textures);
944 TextNameHash.put(RecName, result);
945 Exit;
946 end;
948 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
949 WADName := g_ExtractWadName(RecName);
951 WAD := TWADFile.Create();
953 if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
955 WAD.ReadFile(WADName);
957 //txname := RecName;
959 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
960 begin
961 FreeMem(TextureData);
962 RecName := 'COMMON\ALIEN';
963 end;
966 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength, log) then
967 begin
968 SetLength(Textures, Length(Textures)+1);
969 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
970 begin
971 SetLength(Textures, Length(Textures)-1);
972 Exit;
973 end;
974 e_GetTextureSize(Textures[High(Textures)].TextureID, @Textures[High(Textures)].Width, @Textures[High(Textures)].Height);
975 FreeMem(TextureData);
976 Textures[High(Textures)].TextureName := RecName;
977 Textures[High(Textures)].Anim := False;
979 result := High(Textures);
980 TextNameHash.put(RecName, result);
981 end
982 else // Íåò òàêîãî ðåóñðñà â WAD'å
983 begin
984 //e_WriteLog(Format('SHIT! Error loading texture %s : %s', [RecName, g_ExtractFilePathName(RecName)]), MSG_WARNING);
985 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
986 if log and (not BadTextNameHash.get(RecName, a)) then
987 begin
988 e_WriteLog(Format('Error loading texture %s', [RecName]), TMsgType.Warning);
989 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
990 end;
991 BadTextNameHash.put(RecName, -1);
992 end;
994 WAD.Free();
995 end;
998 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
999 var
1000 WAD: TWADFile;
1001 TextureWAD: PChar = nil;
1002 TextData: Pointer = nil;
1003 TextureData: Pointer = nil;
1004 cfg: TConfig = nil;
1005 WADName: String;
1006 ResLength: Integer;
1007 TextureResource: String;
1008 _width, _height, _framecount, _speed: Integer;
1009 _backanimation: Boolean;
1010 //imgfmt: string;
1011 ia: TDynImageDataArray = nil;
1012 f, c, frdelay, frloop: Integer;
1013 begin
1014 RecName := toLowerCase1251(RecName);
1015 if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
1016 if TextNameHash.get(RecName, result) then
1017 begin
1018 // i found her!
1019 //e_LogWritefln('animated texture ''%s'' already loaded', [RecName]);
1020 exit;
1021 end;
1023 result := -1;
1025 //e_LogWritefln('*** Loading animated texture "%s"', [RecName]);
1027 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1028 if BadTextNameHash.get(RecName, f) then
1029 begin
1030 //e_WriteLog(Format('no animation texture %s (don''t worry)', [RecName]), MSG_NOTIFY);
1031 exit;
1032 end;
1034 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
1035 WADName := g_ExtractWadName(RecName);
1037 WAD := TWADFile.Create();
1038 try
1039 if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
1041 WAD.ReadFile(WADName);
1043 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength, log) then
1044 begin
1045 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1046 if log and (not BadTextNameHash.get(RecName, f)) then
1047 begin
1048 e_WriteLog(Format('Error loading animation texture %s', [RecName]), TMsgType.Warning);
1049 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
1050 end;
1051 BadTextNameHash.put(RecName, -1);
1052 exit;
1053 end;
1055 {TEST
1056 if WADName = Map then
1057 begin
1058 //FreeMem(TextureWAD);
1059 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
1060 end;
1063 WAD.FreeWAD();
1065 if ResLength < 6 then
1066 begin
1067 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), TMsgType.Warning);
1068 BadTextNameHash.put(RecName, -1);
1069 exit;
1070 end;
1072 // ýòî ïòèöà? ýòî ñàìîë¸ò?
1073 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
1074 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
1075 begin
1076 // íåò, ýòî ñóïåðìåí!
1077 if not WAD.ReadMemory(TextureWAD, ResLength) then
1078 begin
1079 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), TMsgType.Warning);
1080 BadTextNameHash.put(RecName, -1);
1081 exit;
1082 end;
1084 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
1085 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
1086 begin
1087 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), TMsgType.Warning);
1088 BadTextNameHash.put(RecName, -1);
1089 exit;
1090 end;
1092 cfg := TConfig.CreateMem(TextData, ResLength);
1094 TextureResource := cfg.ReadStr('', 'resource', '');
1095 if TextureResource = '' then
1096 begin
1097 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), TMsgType.Warning);
1098 BadTextNameHash.put(RecName, -1);
1099 exit;
1100 end;
1102 _width := cfg.ReadInt('', 'framewidth', 0);
1103 _height := cfg.ReadInt('', 'frameheight', 0);
1104 _framecount := cfg.ReadInt('', 'framecount', 0);
1105 _speed := cfg.ReadInt('', 'waitcount', 0);
1106 _backanimation := cfg.ReadBool('', 'backanimation', False);
1108 cfg.Free();
1109 cfg := nil;
1111 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
1112 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
1113 begin
1114 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), TMsgType.Warning);
1115 BadTextNameHash.put(RecName, -1);
1116 exit;
1117 end;
1119 WAD.Free();
1120 WAD := nil;
1122 SetLength(Textures, Length(Textures)+1);
1123 with Textures[High(Textures)] do
1124 begin
1125 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
1126 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
1127 begin
1128 TextureName := RecName;
1129 Width := _width;
1130 Height := _height;
1131 Anim := True;
1132 FramesCount := _framecount;
1133 Speed := _speed;
1134 result := High(Textures);
1135 TextNameHash.put(RecName, result);
1136 end
1137 else
1138 begin
1139 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1140 if log and (not BadTextNameHash.get(RecName, f)) then
1141 begin
1142 e_WriteLog(Format('Error loading animation texture %s', [RecName]), TMsgType.Warning);
1143 end;
1144 BadTextNameHash.put(RecName, -1);
1145 end;
1146 end;
1147 end
1148 else
1149 begin
1150 // try animated image
1152 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
1153 if length(imgfmt) = 0 then
1154 begin
1155 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
1156 exit;
1157 end;
1159 GlobalMetadata.ClearMetaItems();
1160 GlobalMetadata.ClearMetaItemsForSaving();
1161 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
1162 begin
1163 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), TMsgType.Warning);
1164 BadTextNameHash.put(RecName, -1);
1165 exit;
1166 end;
1167 if length(ia) = 0 then
1168 begin
1169 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), TMsgType.Warning);
1170 BadTextNameHash.put(RecName, -1);
1171 exit;
1172 end;
1174 WAD.Free();
1175 WAD := nil;
1177 _width := ia[0].width;
1178 _height := ia[0].height;
1179 _framecount := length(ia);
1180 _speed := 1;
1181 _backanimation := false;
1182 frdelay := -1;
1183 frloop := -666;
1184 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
1185 begin
1186 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
1187 try
1188 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
1189 frdelay := f;
1190 if f < 0 then f := 0;
1191 // rounding ;-)
1192 c := f mod 28;
1193 if c < 13 then c := 0 else c := 1;
1194 f := (f div 28)+c;
1195 if f < 1 then f := 1 else if f > 255 then f := 255;
1196 _speed := f;
1197 except
1198 end;
1199 end;
1200 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
1201 begin
1202 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
1203 try
1204 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
1205 frloop := f;
1206 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
1207 except
1208 end;
1209 end;
1210 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
1211 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
1212 f := ord(_backanimation);
1213 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]), TMsgType.Notify);
1215 SetLength(Textures, Length(Textures)+1);
1216 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
1217 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
1218 begin
1219 Textures[High(Textures)].TextureName := RecName;
1220 Textures[High(Textures)].Width := _width;
1221 Textures[High(Textures)].Height := _height;
1222 Textures[High(Textures)].Anim := True;
1223 Textures[High(Textures)].FramesCount := length(ia);
1224 Textures[High(Textures)].Speed := _speed;
1225 result := High(Textures);
1226 TextNameHash.put(RecName, result);
1227 //writeln(' CREATED!');
1228 end
1229 else
1230 begin
1231 if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
1232 if log and (not BadTextNameHash.get(RecName, f)) then
1233 begin
1234 e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), TMsgType.Warning);
1235 end;
1236 BadTextNameHash.put(RecName, -1);
1237 end;
1238 end;
1239 finally
1240 for f := 0 to High(ia) do FreeImage(ia[f]);
1241 WAD.Free();
1242 cfg.Free();
1243 if (TextureWAD <> nil) then FreeMem(TextureWAD);
1244 if (TextData <> nil) then FreeMem(TextData);
1245 if (TextureData <> nil) then FreeMem(TextureData);
1246 end;
1247 end;
1249 procedure CreateItem(Item: TDynRecord);
1250 begin
1251 if g_Game_IsClient then Exit;
1253 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
1254 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
1255 Exit;
1257 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
1258 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
1259 end;
1261 procedure CreateArea(Area: TDynRecord);
1262 var
1263 a: Integer;
1264 id: DWORD = 0;
1265 begin
1266 case Area.AreaType of
1267 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
1268 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
1269 begin
1270 SetLength(RespawnPoints, Length(RespawnPoints)+1);
1271 with RespawnPoints[High(RespawnPoints)] do
1272 begin
1273 X := Area.X;
1274 Y := Area.Y;
1275 Direction := TDirection(Area.Direction);
1277 case Area.AreaType of
1278 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
1279 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
1280 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
1281 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
1282 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
1283 end;
1284 end;
1285 end;
1287 AREA_REDFLAG, AREA_BLUEFLAG:
1288 begin
1289 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
1291 if FlagPoints[a] <> nil then Exit;
1293 New(FlagPoints[a]);
1295 with FlagPoints[a]^ do
1296 begin
1297 X := Area.X-FLAGRECT.X;
1298 Y := Area.Y-FLAGRECT.Y;
1299 Direction := TDirection(Area.Direction);
1300 end;
1302 with gFlags[a] do
1303 begin
1304 case a of
1305 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
1306 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
1307 end;
1309 Animation := TAnimation.Create(id, True, 8);
1310 Obj.Rect := FLAGRECT;
1312 g_Map_ResetFlag(a);
1313 end;
1314 end;
1316 AREA_DOMFLAG:
1317 begin
1318 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
1319 with DOMFlagPoints[High(DOMFlagPoints)] do
1320 begin
1321 X := Area.X;
1322 Y := Area.Y;
1323 Direction := TDirection(Area.Direction);
1324 end;
1326 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
1327 end;
1328 end;
1329 end;
1331 function CreateTrigger (amapIdx: Integer; Trigger: TDynRecord; atpanid, atrigpanid: Integer): Integer;
1332 var
1333 _trigger: TTrigger;
1334 begin
1335 result := -1;
1336 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
1338 with _trigger do
1339 begin
1340 mapId := Trigger.id;
1341 mapIndex := amapIdx;
1342 X := Trigger.X;
1343 Y := Trigger.Y;
1344 Width := Trigger.Width;
1345 Height := Trigger.Height;
1346 Enabled := Trigger.Enabled;
1347 TexturePanelGUID := atpanid;
1348 TriggerType := Trigger.TriggerType;
1349 ActivateType := Trigger.ActivateType;
1350 Keys := Trigger.Keys;
1351 trigPanelGUID := atrigpanid;
1352 end;
1354 result := Integer(g_Triggers_Create(_trigger, Trigger));
1355 end;
1357 procedure CreateMonster(monster: TDynRecord);
1358 var
1359 a: Integer;
1360 mon: TMonster;
1361 begin
1362 if g_Game_IsClient then Exit;
1364 if (gGameSettings.GameType = GT_SINGLE)
1365 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1366 begin
1367 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1369 if gTriggers <> nil then
1370 begin
1371 for a := 0 to High(gTriggers) do
1372 begin
1373 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1374 begin
1375 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1376 if (gTriggers[a].trigDataRec.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1377 end;
1378 end;
1379 end;
1381 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1382 end;
1383 end;
1385 procedure g_Map_ReAdd_DieTriggers();
1387 function monsDieTrig (mon: TMonster): Boolean;
1388 var
1389 a: Integer;
1390 //tw: TStrTextWriter;
1391 begin
1392 result := false; // don't stop
1393 mon.ClearTriggers();
1394 for a := 0 to High(gTriggers) do
1395 begin
1396 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1397 begin
1398 //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a);
1400 tw := TStrTextWriter.Create();
1401 try
1402 gTriggers[a].trigData.writeTo(tw);
1403 e_LogWritefln('=== trigger #%s ==='#10'%s'#10'---', [a, tw.str]);
1404 finally
1405 tw.Free();
1406 end;
1408 if (gTriggers[a].trigDataRec.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a);
1409 end;
1410 end;
1411 end;
1413 begin
1414 if g_Game_IsClient then Exit;
1416 g_Mons_ForEach(monsDieTrig);
1417 end;
1419 function extractWadName(resourceName: string): string;
1420 var
1421 posN: Integer;
1422 begin
1423 posN := Pos(':', resourceName);
1424 if posN > 0 then
1425 Result:= Copy(resourceName, 0, posN-1)
1426 else
1427 Result := '';
1428 end;
1430 procedure addResToExternalResList(res: string);
1431 begin
1432 res := extractWadName(res);
1433 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1434 gExternalResources.Add(res);
1435 end;
1437 procedure generateExternalResourcesList({mapReader: TMapReader_1}map: TDynRecord);
1438 //var
1439 //textures: TTexturesRec1Array;
1440 //textures: TDynField;
1441 //trec: TDynRecord;
1442 //mapHeader: TMapHeaderRec_1;
1443 //i: integer;
1444 //resFile: String = '';
1445 begin
1446 if gExternalResources = nil then
1447 gExternalResources := TStringList.Create;
1449 gExternalResources.Clear;
1451 (*
1453 textures := GetTextures(map);
1454 for i := 0 to High(textures) do
1455 begin
1456 addResToExternalResList(resFile);
1457 end;
1460 textures := map['texture'];
1461 if (textures <> nil) then
1462 begin
1463 for trec in textures do
1464 begin
1465 addResToExternalResList(resFile);
1466 end;
1467 end;
1469 textures := nil;
1470 *)
1472 //mapHeader := GetMapHeader(map);
1474 addResToExternalResList(map.MusicName);
1475 addResToExternalResList(map.SkyName);
1476 end;
1479 procedure mapCreateGrid ();
1480 var
1481 mapX0: Integer = $3fffffff;
1482 mapY0: Integer = $3fffffff;
1483 mapX1: Integer = -$3fffffff;
1484 mapY1: Integer = -$3fffffff;
1486 procedure calcBoundingBox (constref panels: TPanelArray);
1487 var
1488 idx: Integer;
1489 pan: TPanel;
1490 begin
1491 for idx := 0 to High(panels) do
1492 begin
1493 pan := panels[idx];
1494 if not pan.visvalid then continue;
1495 if (pan.Width < 1) or (pan.Height < 1) then continue;
1496 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1497 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1498 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1499 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1500 end;
1501 end;
1503 procedure addPanelsToGrid (constref panels: TPanelArray);
1504 var
1505 idx: Integer;
1506 pan: TPanel;
1507 newtag: Integer;
1508 begin
1509 //tag := panelTypeToTag(tag);
1510 for idx := 0 to High(panels) do
1511 begin
1512 pan := panels[idx];
1513 if not pan.visvalid then continue;
1514 if (pan.proxyId <> -1) then
1515 begin
1516 {$IF DEFINED(D2F_DEBUG)}
1517 e_WriteLog(Format('DUPLICATE wall #%d(%d) enabled (%d); type:%08x', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.PanelType]), TMsgType.Notify);
1518 {$ENDIF}
1519 continue;
1520 end;
1521 case pan.PanelType of
1522 PANEL_WALL: newtag := GridTagWall;
1523 PANEL_OPENDOOR, PANEL_CLOSEDOOR: newtag := GridTagDoor;
1524 PANEL_BACK: newtag := GridTagBack;
1525 PANEL_FORE: newtag := GridTagFore;
1526 PANEL_WATER: newtag := GridTagWater;
1527 PANEL_ACID1: newtag := GridTagAcid1;
1528 PANEL_ACID2: newtag := GridTagAcid2;
1529 PANEL_STEP: newtag := GridTagStep;
1530 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: newtag := GridTagLift;
1531 PANEL_BLOCKMON: newtag := GridTagBlockMon;
1532 else continue; // oops
1533 end;
1534 pan.tag := newtag;
1536 pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, newtag);
1537 // "enabled" flag has meaning only for doors and walls (engine assumes it); but meh...
1538 mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
1539 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
1541 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
1542 begin
1543 e_WriteLog(Format('INSERTED wall #%d(%d) enabled (%d)', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY);
1544 end;
1546 {$ENDIF}
1547 end;
1548 end;
1550 begin
1551 mapGrid.Free();
1552 mapGrid := nil;
1554 calcBoundingBox(gWalls);
1555 calcBoundingBox(gRenderBackgrounds);
1556 calcBoundingBox(gRenderForegrounds);
1557 calcBoundingBox(gWater);
1558 calcBoundingBox(gAcid1);
1559 calcBoundingBox(gAcid2);
1560 calcBoundingBox(gSteps);
1561 calcBoundingBox(gLifts);
1562 calcBoundingBox(gBlockMon);
1564 e_LogWritefln('map dimensions: (%d,%d)-(%d,%d); editor size:(0,0)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1, gMapInfo.Width, gMapInfo.Height]);
1566 if (mapX0 > 0) then mapX0 := 0;
1567 if (mapY0 > 0) then mapY0 := 0;
1569 if (mapX1 < gMapInfo.Width-1) then mapX1 := gMapInfo.Width-1;
1570 if (mapY1 < gMapInfo.Height-1) then mapY1 := gMapInfo.Height-1;
1572 mapGrid := TPanelGrid.Create(mapX0-128, mapY0-128, mapX1-mapX0+1+128*2, mapY1-mapY0+1+128*2);
1573 //mapGrid := TPanelGrid.Create(0, 0, gMapInfo.Width, gMapInfo.Height);
1575 addPanelsToGrid(gWalls);
1576 addPanelsToGrid(gRenderBackgrounds);
1577 addPanelsToGrid(gRenderForegrounds);
1578 addPanelsToGrid(gWater);
1579 addPanelsToGrid(gAcid1);
1580 addPanelsToGrid(gAcid2);
1581 addPanelsToGrid(gSteps);
1582 addPanelsToGrid(gLifts); // it doesn't matter which LIFT type is used here
1583 addPanelsToGrid(gBlockMon);
1585 mapGrid.dumpStats();
1587 g_Mons_InitTree(mapGrid.gridX0, mapGrid.gridY0, mapGrid.gridWidth, mapGrid.gridHeight);
1588 end;
1591 function g_Map_Load(Res: String): Boolean;
1592 const
1593 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1594 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1595 type
1596 PTRec = ^TTRec;
1597 TTRec = record
1598 //TexturePanel: Integer;
1599 tnum: Integer;
1600 id: Integer;
1601 trigrec: TDynRecord;
1602 // texture pane;
1603 texPanelIdx: Integer;
1604 texPanel: TDynRecord;
1605 // "action" panel
1606 actPanelIdx: Integer;
1607 actPanel: TDynRecord;
1608 end;
1609 var
1610 WAD: TWADFile;
1611 //mapReader: TDynRecord = nil;
1612 mapTextureList: TDynField = nil; //TTexturesRec1Array; tagInt: texture index
1613 panels: TDynField = nil; //TPanelsRec1Array;
1614 items: TDynField = nil; //TItemsRec1Array;
1615 monsters: TDynField = nil; //TMonsterRec1Array;
1616 areas: TDynField = nil; //TAreasRec1Array;
1617 triggers: TDynField = nil; //TTriggersRec1Array;
1618 b, c, k: Integer;
1619 PanelID: DWORD;
1620 AddTextures: TAddTextureArray;
1621 TriggersTable: array of TTRec;
1622 FileName, mapResName, s, TexName: String;
1623 Data: Pointer;
1624 Len: Integer;
1625 ok, isAnim: Boolean;
1626 CurTex, ntn: Integer;
1627 rec, texrec: TDynRecord;
1628 pttit: PTRec;
1629 pannum, trignum, cnt, tgpid: Integer;
1630 stt: UInt64;
1631 moveSpeed{, moveStart, moveEnd}: TDFPoint;
1632 //moveActive: Boolean;
1633 pan: TPanel;
1634 mapOk: Boolean = false;
1635 begin
1636 mapGrid.Free();
1637 mapGrid := nil;
1639 //gCurrentMap.Free();
1640 //gCurrentMap := nil;
1642 panByGUID := nil;
1644 Result := False;
1645 gMapInfo.Map := Res;
1646 TriggersTable := nil;
1647 //mapReader := nil;
1649 sfsGCDisable(); // temporary disable removing of temporary volumes
1650 try
1651 // Çàãðóçêà WAD (åñëè ó íàñ íåò óæå çàãðóæåíîé êàðòû)
1652 if (gCurrentMap = nil) then
1653 begin
1654 FileName := g_ExtractWadName(Res);
1655 e_WriteLog('Loading map WAD: '+FileName, TMsgType.Notify);
1656 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1658 WAD := TWADFile.Create();
1659 if not WAD.ReadFile(FileName) then
1660 begin
1661 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1662 WAD.Free();
1663 Exit;
1664 end;
1666 //k8: why loader ignores path here?
1667 mapResName := g_ExtractFileName(Res);
1668 if not WAD.GetMapResource(mapResName, Data, Len) then
1669 begin
1670 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1671 WAD.Free();
1672 Exit;
1673 end;
1675 WAD.Free();
1677 if (Len < 4) then
1678 begin
1679 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1680 FreeMem(Data);
1681 exit;
1682 end;
1684 // Çàãðóçêà êàðòû:
1685 e_LogWritefln('Loading map: %s', [mapResName], TMsgType.Notify);
1686 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1688 stt := getTimeMicro();
1690 try
1691 gCurrentMap := g_Map_ParseMap(Data, Len);
1692 except
1693 gCurrentMap.Free();
1694 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1695 FreeMem(Data);
1696 gCurrentMapFileName := '';
1697 Exit;
1698 end;
1700 FreeMem(Data);
1702 if (gCurrentMap = nil) then
1703 begin
1704 e_LogWritefln('invalid map file: ''%s''', [mapResName]);
1705 gCurrentMapFileName := '';
1706 exit;
1707 end;
1708 end
1709 else
1710 begin
1711 stt := getTimeMicro();
1712 end;
1714 //gCurrentMap := mapReader;
1716 generateExternalResourcesList(gCurrentMap);
1717 mapTextureList := gCurrentMap['texture'];
1718 // get all other lists here too
1719 panels := gCurrentMap['panel'];
1720 triggers := gCurrentMap['trigger'];
1721 items := gCurrentMap['item'];
1722 areas := gCurrentMap['area'];
1723 monsters := gCurrentMap['monster'];
1725 // Çàãðóçêà îïèñàíèÿ êàðòû:
1726 e_WriteLog(' Reading map info...', TMsgType.Notify);
1727 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1729 with gMapInfo do
1730 begin
1731 Name := gCurrentMap.MapName;
1732 Description := gCurrentMap.MapDesc;
1733 Author := gCurrentMap.MapAuthor;
1734 MusicName := gCurrentMap.MusicName;
1735 SkyName := gCurrentMap.SkyName;
1736 Height := gCurrentMap.Height;
1737 Width := gCurrentMap.Width;
1738 end;
1740 // Çàãðóçêà òåêñòóð:
1741 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1742 // Äîáàâëåíèå òåêñòóð â Textures[]:
1743 if (mapTextureList <> nil) and (mapTextureList.count > 0) then
1744 begin
1745 e_WriteLog(' Loading textures:', TMsgType.Notify);
1746 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], mapTextureList.count-1, False);
1748 cnt := -1;
1749 for rec in mapTextureList do
1750 begin
1751 Inc(cnt);
1752 s := rec.Resource;
1753 {$IF DEFINED(D2F_DEBUG_TXLOAD)}
1754 e_WriteLog(Format(' Loading texture #%d: %s', [cnt, s]), TMsgType.Notify);
1755 {$ENDIF}
1756 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1757 if rec.Anim then
1758 begin
1759 // Àíèìèðîâàííàÿ òåêñòóðà
1760 ntn := CreateAnimTexture(rec.Resource, FileName, True);
1761 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1762 end
1763 else
1764 begin
1765 // Îáû÷íàÿ òåêñòóðà
1766 ntn := CreateTexture(rec.Resource, FileName, True);
1767 if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1768 end;
1769 if (ntn < 0) then ntn := CreateNullTexture(rec.Resource);
1771 rec.tagInt := ntn; // remember texture number
1772 g_Game_StepLoading();
1773 end;
1775 // set panel tagInt to texture index
1776 if (panels <> nil) then
1777 begin
1778 for rec in panels do
1779 begin
1780 texrec := rec.TextureRec;
1781 if (texrec = nil) then rec.tagInt := -1 else rec.tagInt := texrec.tagInt;
1782 end;
1783 end;
1784 end;
1786 // Çàãðóçêà òðèããåðîâ
1787 gTriggerClientID := 0;
1788 e_WriteLog(' Loading triggers...', TMsgType.Notify);
1789 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1791 // Çàãðóçêà ïàíåëåé
1792 e_WriteLog(' Loading panels...', TMsgType.Notify);
1793 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1795 // check texture numbers for panels
1796 if (panels <> nil) and (panels.count > 0) then
1797 begin
1798 for rec in panels do
1799 begin
1800 if (rec.tagInt < 0) then
1801 begin
1802 e_WriteLog('error loading map: invalid texture index for panel', TMsgType.Fatal);
1803 result := false;
1804 gCurrentMap.Free();
1805 gCurrentMap := nil;
1806 gCurrentMapFileName := '';
1807 exit;
1808 end;
1809 end;
1810 end;
1812 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì)
1813 if (triggers <> nil) and (triggers.count > 0) then
1814 begin
1815 e_WriteLog(' Setting up trigger table...', TMsgType.Notify);
1816 //SetLength(TriggersTable, triggers.count);
1817 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], triggers.count-1, False);
1819 SetLength(TriggersTable, triggers.count);
1820 trignum := -1;
1821 for rec in triggers do
1822 begin
1823 Inc(trignum);
1824 pttit := @TriggersTable[trignum];
1825 pttit.trigrec := rec;
1826 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè)
1827 pttit.texPanelIdx := -1; // will be fixed later
1828 pttit.texPanel := rec.TexturePanelRec;
1829 // action panel
1830 pttit.actPanelIdx := -1;
1831 if (rec.trigRec <> nil) then pttit.actPanel := rec.trigRec.tgPanelRec else pttit.actPanel := nil;
1832 // set flag
1833 if (pttit.texPanel <> nil) then pttit.texPanel.userPanelTrigRef := true;
1834 if (pttit.actPanel <> nil) then pttit.actPanel.userPanelTrigRef := true;
1835 // update progress
1836 g_Game_StepLoading();
1837 end;
1838 end;
1840 // Ñîçäàåì ïàíåëè
1841 if (panels <> nil) and (panels.count > 0) then
1842 begin
1843 e_WriteLog(' Setting up trigger links...', TMsgType.Notify);
1844 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], panels.count-1, False);
1846 pannum := -1;
1847 for rec in panels do
1848 begin
1849 Inc(pannum);
1850 //e_LogWritefln('PANSTART: pannum=%s', [pannum]);
1851 texrec := nil;
1852 SetLength(AddTextures, 0);
1853 CurTex := -1;
1854 ok := false;
1856 if (mapTextureList <> nil) then
1857 begin
1858 texrec := rec.TextureRec;
1859 ok := (texrec <> nil);
1860 end;
1862 if ok then
1863 begin
1864 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1865 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð
1866 ok := false;
1867 if (TriggersTable <> nil) and (mapTextureList <> nil) then
1868 begin
1869 if rec.userPanelTrigRef then
1870 begin
1871 // e_LogWritefln('trigref for panel %s', [pannum]);
1872 ok := True;
1873 end;
1874 end;
1875 end;
1877 if ok then
1878 begin
1879 // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1880 s := texrec.Resource;
1882 // Ñïåö-òåêñòóðû çàïðåùåíû
1883 if g_Map_IsSpecialTexture(s) then
1884 begin
1885 ok := false
1886 end
1887 else
1888 begin
1889 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè
1890 ok := g_Texture_NumNameFindStart(s);
1891 end;
1893 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1894 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #
1895 if ok then
1896 begin
1897 k := NNF_NAME_BEFORE;
1898 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû
1899 while ok or (k = NNF_NAME_BEFORE) or (k = NNF_NAME_EQUALS) do
1900 begin
1901 k := g_Texture_NumNameFindNext(TexName);
1903 if (k = NNF_NAME_BEFORE) or (k = NNF_NAME_AFTER) then
1904 begin
1905 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó
1906 if texrec.Anim then
1907 begin
1908 // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1909 isAnim := True;
1910 //e_LogWritefln('000: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1911 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1912 //e_LogWritefln('001: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1913 if not ok then
1914 begin
1915 // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1916 isAnim := False;
1917 //e_LogWritefln('002: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1918 ok := CreateTexture(TexName, FileName, False) >= 0;
1919 //e_LogWritefln('003: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1920 end;
1921 end
1922 else
1923 begin
1924 // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1925 isAnim := False;
1926 //e_LogWritefln('004: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1927 ok := CreateTexture(TexName, FileName, False) >= 0;
1928 //e_LogWritefln('005: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1929 if not ok then
1930 begin
1931 // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1932 isAnim := True;
1933 //e_LogWritefln('006: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1934 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1935 //e_LogWritefln('007: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
1936 end;
1937 end;
1939 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè
1940 if ok then
1941 begin
1943 for c := 0 to High(Textures) do
1944 begin
1945 if (Textures[c].TextureName = TexName) then
1946 begin
1947 SetLength(AddTextures, Length(AddTextures)+1);
1948 AddTextures[High(AddTextures)].Texture := c;
1949 AddTextures[High(AddTextures)].Anim := isAnim;
1950 break;
1951 end;
1952 end;
1954 if (TextNameHash <> nil) and TextNameHash.get(toLowerCase1251(TexName), c) then
1955 begin
1956 SetLength(AddTextures, Length(AddTextures)+1);
1957 AddTextures[High(AddTextures)].Texture := c;
1958 AddTextures[High(AddTextures)].Anim := isAnim;
1959 end;
1960 end;
1961 end
1962 else
1963 begin
1964 if k = NNF_NAME_EQUALS then
1965 begin
1966 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî
1967 SetLength(AddTextures, Length(AddTextures)+1);
1968 AddTextures[High(AddTextures)].Texture := rec.tagInt; // internal texture number, not map index
1969 AddTextures[High(AddTextures)].Anim := texrec.Anim;
1970 CurTex := High(AddTextures);
1971 ok := true;
1972 end
1973 else // NNF_NO_NAME
1974 begin
1975 ok := false;
1976 end;
1977 end;
1978 end; // while ok...
1980 ok := true;
1981 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1982 end; // if ok - ññûëàþòñÿ òðèããåðû
1984 if not ok then
1985 begin
1986 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó
1987 SetLength(AddTextures, 1);
1988 AddTextures[0].Texture := rec.tagInt; // internal texture number, not map index
1989 AddTextures[0].Anim := false;
1990 if (texrec <> nil) then AddTextures[0].Anim := texrec.Anim;
1991 CurTex := 0;
1992 end;
1994 //e_WriteLog(Format('panel #%d: TextureNum=%d; ht=%d; ht1=%d; atl=%d', [a, panels[a].TextureNum, High(mapTextureList), High(Textures), High(AddTextures)]), MSG_NOTIFY);
1996 //e_LogWritefln('PANADD: pannum=%s', [pannum]);
1998 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå GUID
1999 PanelID := CreatePanel(rec, AddTextures, CurTex);
2000 //e_LogWritefln('panel #%s of type %s got guid #%s', [pannum, rec.PanelType, PanelID]);
2001 rec.userPanelId := PanelID; // remember game panel id, we'll fix triggers later
2003 // setup lifts
2004 moveSpeed := rec.moveSpeed;
2005 //moveStart := rec.moveStart;
2006 //moveEnd := rec.moveEnd;
2007 //moveActive := rec['move_active'].value;
2008 if not moveSpeed.isZero then
2009 begin
2010 SetLength(gMovingWallIds, Length(gMovingWallIds)+1);
2011 gMovingWallIds[High(gMovingWallIds)] := PanelID;
2012 //e_LogWritefln('found moving panel ''%s'' (idx=%s; id=%s)', [rec.id, pannum, PanelID]);
2013 end;
2015 //e_LogWritefln('PANEND: pannum=%s', [pannum]);
2017 g_Game_StepLoading();
2018 end;
2019 end;
2021 // ×èíèì ID'û ïàíåëåé, êîòîðûå èñïîëüçóþòñÿ â òðèããåðàõ
2022 for b := 0 to High(TriggersTable) do
2023 begin
2024 if (TriggersTable[b].texPanel <> nil) then TriggersTable[b].texPanelIdx := TriggersTable[b].texPanel.userPanelId;
2025 if (TriggersTable[b].actPanel <> nil) then TriggersTable[b].actPanelIdx := TriggersTable[b].actPanel.userPanelId;
2026 end;
2028 // create map grid, init other grids (for monsters, for example)
2029 e_WriteLog('Creating map grid', TMsgType.Notify);
2030 mapCreateGrid();
2032 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû
2033 if (triggers <> nil) and (panels <> nil) and (not gLoadGameMode) then
2034 begin
2035 e_LogWritefln(' Creating triggers (%d)...', [triggers.count]);
2036 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
2037 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü
2038 trignum := -1;
2039 for rec in triggers do
2040 begin
2041 Inc(trignum);
2042 tgpid := TriggersTable[trignum].actPanelIdx;
2043 //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]);
2044 TriggersTable[trignum].tnum := trignum;
2045 TriggersTable[trignum].id := CreateTrigger(trignum, rec, TriggersTable[trignum].texPanelIdx, tgpid);
2046 end;
2047 end;
2049 //FIXME: use hashtable!
2050 for pan in panByGUID do
2051 begin
2052 if (pan.endPosTrigId >= 0) and (pan.endPosTrigId < Length(TriggersTable)) then
2053 begin
2054 pan.endPosTrigId := TriggersTable[pan.endPosTrigId].id;
2055 end;
2056 if (pan.endSizeTrigId >= 0) and (pan.endSizeTrigId < Length(TriggersTable)) then
2057 begin
2058 pan.endSizeTrigId := TriggersTable[pan.endSizeTrigId].id;
2059 end;
2060 end;
2062 // Çàãðóçêà ïðåäìåòîâ
2063 e_WriteLog(' Loading items...', TMsgType.Notify);
2064 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
2066 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû
2067 if (items <> nil) and not gLoadGameMode then
2068 begin
2069 e_WriteLog(' Spawning items...', TMsgType.Notify);
2070 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
2071 for rec in items do CreateItem(rec);
2072 end;
2074 // Çàãðóçêà îáëàñòåé
2075 e_WriteLog(' Loading areas...', TMsgType.Notify);
2076 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
2078 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè
2079 if areas <> nil then
2080 begin
2081 e_WriteLog(' Creating areas...', TMsgType.Notify);
2082 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
2083 for rec in areas do CreateArea(rec);
2084 end;
2086 // Çàãðóçêà ìîíñòðîâ
2087 e_WriteLog(' Loading monsters...', TMsgType.Notify);
2088 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
2090 gTotalMonsters := 0;
2092 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ
2093 if (monsters <> nil) and not gLoadGameMode then
2094 begin
2095 e_WriteLog(' Spawning monsters...', TMsgType.Notify);
2096 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
2097 for rec in monsters do CreateMonster(rec);
2098 end;
2100 //gCurrentMap := mapReader; // this will be our current map now
2101 gCurrentMapFileName := Res;
2102 //mapReader := nil;
2104 // Çàãðóçêà íåáà
2105 if (gMapInfo.SkyName <> '') then
2106 begin
2107 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, TMsgType.Notify);
2108 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
2109 FileName := g_ExtractWadName(gMapInfo.SkyName);
2111 if (FileName <> '') then FileName := GameDir+'/wads/'+FileName else FileName := g_ExtractWadName(Res);
2113 if gTextureFilter then TEXTUREFILTER := GL_LINEAR else TEXTUREFILTER := GL_NEAREST;
2114 try
2115 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
2116 if g_Texture_CreateWAD(BackID, s) then
2117 begin
2118 g_Game_SetupScreenSize();
2119 end
2120 else
2121 begin
2122 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
2123 end;
2124 finally
2125 TEXTUREFILTER := GL_NEAREST;
2126 end;
2127 end;
2129 // Çàãðóçêà ìóçûêè
2130 ok := False;
2131 if gMapInfo.MusicName <> '' then
2132 begin
2133 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, TMsgType.Notify);
2134 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
2135 FileName := g_ExtractWadName(gMapInfo.MusicName);
2137 if FileName <> '' then
2138 FileName := GameDir+'/wads/'+FileName
2139 else
2140 begin
2141 FileName := g_ExtractWadName(Res);
2142 end;
2144 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
2145 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
2146 ok := True
2147 else
2148 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
2149 end;
2151 // Îñòàëüíûå óñòàíâêè
2152 CreateDoorMap();
2153 CreateLiftMap();
2155 g_Items_Init();
2156 g_Weapon_Init();
2157 g_Monsters_Init();
2159 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
2160 if not gLoadGameMode then g_GFX_Init();
2162 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
2163 mapTextureList := nil;
2164 panels := nil;
2165 items := nil;
2166 areas := nil;
2167 triggers := nil;
2168 TriggersTable := nil;
2169 AddTextures := nil;
2171 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
2172 if ok and (not gLoadGameMode) then
2173 begin
2174 gMusic.SetByName(gMapInfo.MusicName);
2175 gMusic.Play();
2176 end
2177 else
2178 begin
2179 gMusic.SetByName('');
2180 end;
2182 stt := getTimeMicro()-stt;
2183 e_LogWritefln('map loaded in %s.%s milliseconds', [Integer(stt div 1000), Integer(stt mod 1000)]);
2184 mapOk := true;
2185 finally
2186 sfsGCEnable(); // enable releasing unused volumes
2187 //mapReader.Free();
2188 e_ClearInputBuffer(); // why not?
2189 if not mapOk then
2190 begin
2191 gCurrentMap.Free();
2192 gCurrentMap := nil;
2193 gCurrentMapFileName := '';
2194 end;
2195 end;
2197 e_WriteLog('Done loading map.', TMsgType.Notify);
2198 Result := True;
2199 end;
2202 function g_Map_GetMapInfo(Res: String): TMapInfo;
2203 var
2204 WAD: TWADFile;
2205 mapReader: TDynRecord;
2206 //Header: TMapHeaderRec_1;
2207 FileName: String;
2208 Data: Pointer;
2209 Len: Integer;
2210 begin
2211 FillChar(Result, SizeOf(Result), 0);
2212 FileName := g_ExtractWadName(Res);
2214 WAD := TWADFile.Create();
2215 if not WAD.ReadFile(FileName) then
2216 begin
2217 WAD.Free();
2218 Exit;
2219 end;
2221 //k8: it ignores path again
2222 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
2223 begin
2224 WAD.Free();
2225 Exit;
2226 end;
2228 WAD.Free();
2230 try
2231 mapReader := g_Map_ParseMap(Data, Len);
2232 except
2233 mapReader := nil;
2234 FreeMem(Data);
2235 exit;
2236 end;
2238 FreeMem(Data);
2240 if (mapReader = nil) then exit;
2242 if (mapReader.Width > 0) and (mapReader.Height > 0) then
2243 begin
2244 Result.Name := mapReader.MapName;
2245 Result.Description := mapReader.MapDesc;
2246 Result.Map := Res;
2247 Result.Author := mapReader.MapAuthor;
2248 Result.Height := mapReader.Height;
2249 Result.Width := mapReader.Width;
2250 end
2251 else
2252 begin
2253 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
2254 //ZeroMemory(@Header, SizeOf(Header));
2255 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
2256 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
2257 Result.Map := Res;
2258 Result.Author := '';
2259 Result.Height := 0;
2260 Result.Width := 0;
2261 end;
2263 mapReader.Free();
2264 end;
2266 function g_Map_GetMapsList(WADName: string): SArray;
2267 var
2268 WAD: TWADFile;
2269 a: Integer;
2270 ResList: SArray;
2271 begin
2272 Result := nil;
2273 WAD := TWADFile.Create();
2274 if not WAD.ReadFile(WADName) then
2275 begin
2276 WAD.Free();
2277 Exit;
2278 end;
2279 ResList := WAD.GetMapResources();
2280 if ResList <> nil then
2281 begin
2282 for a := 0 to High(ResList) do
2283 begin
2284 SetLength(Result, Length(Result)+1);
2285 Result[High(Result)] := ResList[a];
2286 end;
2287 end;
2288 WAD.Free();
2289 end;
2291 function g_Map_Exist(Res: string): Boolean;
2292 var
2293 WAD: TWADFile;
2294 FileName, mnn: string;
2295 ResList: SArray;
2296 a: Integer;
2297 begin
2298 Result := False;
2300 FileName := addWadExtension(g_ExtractWadName(Res));
2302 WAD := TWADFile.Create;
2303 if not WAD.ReadFile(FileName) then
2304 begin
2305 WAD.Free();
2306 Exit;
2307 end;
2309 ResList := WAD.GetMapResources();
2310 WAD.Free();
2312 mnn := g_ExtractFileName(Res);
2313 if ResList <> nil then
2314 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
2315 begin
2316 Result := True;
2317 Exit;
2318 end;
2319 end;
2321 procedure g_Map_Free(freeTextures: Boolean=true);
2322 var
2323 a: Integer;
2325 procedure FreePanelArray(var panels: TPanelArray);
2326 var
2327 i: Integer;
2329 begin
2330 if panels <> nil then
2331 begin
2332 for i := 0 to High(panels) do
2333 panels[i].Free();
2334 panels := nil;
2335 end;
2336 end;
2338 begin
2339 g_GFX_Free();
2340 g_Weapon_Free();
2341 g_Items_Free();
2342 g_Triggers_Free();
2343 g_Monsters_Free();
2345 RespawnPoints := nil;
2346 if FlagPoints[FLAG_RED] <> nil then
2347 begin
2348 Dispose(FlagPoints[FLAG_RED]);
2349 FlagPoints[FLAG_RED] := nil;
2350 end;
2351 if FlagPoints[FLAG_BLUE] <> nil then
2352 begin
2353 Dispose(FlagPoints[FLAG_BLUE]);
2354 FlagPoints[FLAG_BLUE] := nil;
2355 end;
2356 //DOMFlagPoints := nil;
2358 //gDOMFlags := nil;
2360 if (Length(gCurrentMapFileName) <> 0) then
2361 begin
2362 e_LogWritefln('g_Map_Free: previous map was ''%s''...', [gCurrentMapFileName]);
2363 end
2364 else
2365 begin
2366 e_LogWritefln('g_Map_Free: no previous map.', []);
2367 end;
2369 if freeTextures then
2370 begin
2371 e_LogWritefln('g_Map_Free: clearing textures...', []);
2372 if (Textures <> nil) then
2373 begin
2374 for a := 0 to High(Textures) do
2375 begin
2376 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
2377 begin
2378 if Textures[a].Anim then
2379 begin
2380 g_Frames_DeleteByID(Textures[a].FramesID)
2381 end
2382 else
2383 begin
2384 if (Textures[a].TextureID <> LongWord(TEXTURE_NONE)) then
2385 begin
2386 e_DeleteTexture(Textures[a].TextureID);
2387 end;
2388 end;
2389 end;
2390 end;
2391 Textures := nil;
2392 end;
2393 TextNameHash.Free();
2394 TextNameHash := nil;
2395 BadTextNameHash.Free();
2396 BadTextNameHash := nil;
2397 gCurrentMapFileName := '';
2398 gCurrentMap.Free();
2399 gCurrentMap := nil;
2400 end;
2402 panByGUID := nil;
2404 FreePanelArray(gWalls);
2405 FreePanelArray(gRenderBackgrounds);
2406 FreePanelArray(gRenderForegrounds);
2407 FreePanelArray(gWater);
2408 FreePanelArray(gAcid1);
2409 FreePanelArray(gAcid2);
2410 FreePanelArray(gSteps);
2411 FreePanelArray(gLifts);
2412 FreePanelArray(gBlockMon);
2413 gMovingWallIds := nil;
2415 if BackID <> DWORD(-1) then
2416 begin
2417 gBackSize.X := 0;
2418 gBackSize.Y := 0;
2419 e_DeleteTexture(BackID);
2420 BackID := DWORD(-1);
2421 end;
2423 g_Game_StopAllSounds(False);
2424 gMusic.FreeSound();
2425 g_Sound_Delete(gMapInfo.MusicName);
2427 gMapInfo.Name := '';
2428 gMapInfo.Description := '';
2429 gMapInfo.MusicName := '';
2430 gMapInfo.Height := 0;
2431 gMapInfo.Width := 0;
2433 gDoorMap := nil;
2434 gLiftMap := nil;
2435 end;
2437 procedure g_Map_Update();
2438 var
2439 a, d, j: Integer;
2440 m: Word;
2441 s: String;
2443 procedure UpdatePanelArray(var panels: TPanelArray);
2444 var
2445 i: Integer;
2447 begin
2448 for i := 0 to High(panels) do panels[i].Update();
2449 end;
2451 begin
2452 if g_dbgpan_mplat_step then g_dbgpan_mplat_active := true;
2454 UpdatePanelArray(gWalls);
2455 UpdatePanelArray(gRenderBackgrounds);
2456 UpdatePanelArray(gRenderForegrounds);
2457 UpdatePanelArray(gWater);
2458 UpdatePanelArray(gAcid1);
2459 UpdatePanelArray(gAcid2);
2460 UpdatePanelArray(gSteps);
2462 if g_dbgpan_mplat_step then begin g_dbgpan_mplat_step := false; g_dbgpan_mplat_active := false; end;
2464 if gGameSettings.GameMode = GM_CTF then
2465 begin
2466 for a := FLAG_RED to FLAG_BLUE do
2467 begin
2468 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2469 begin
2470 with gFlags[a] do
2471 begin
2472 if gFlags[a].Animation <> nil then gFlags[a].Animation.Update();
2474 m := g_Obj_Move(@Obj, True, True);
2476 if gTime mod (GAME_TICK*2) <> 0 then Continue;
2478 // Ñîïðîòèâëåíèå âîçäóõà
2479 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2481 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó
2482 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2483 begin
2484 g_Map_ResetFlag(a);
2485 gFlags[a].CaptureTime := 0;
2486 if a = FLAG_RED then
2487 s := _lc[I_PLAYER_FLAG_RED]
2488 else
2489 s := _lc[I_PLAYER_FLAG_BLUE];
2490 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2492 if g_Game_IsNet then
2493 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2494 Continue;
2495 end;
2497 if Count > 0 then Count -= 1;
2499 // Èãðîê áåðåò ôëàã
2500 if gPlayers <> nil then
2501 begin
2502 j := Random(Length(gPlayers)) - 1;
2503 for d := 0 to High(gPlayers) do
2504 begin
2505 Inc(j);
2506 if j > High(gPlayers) then j := 0;
2507 if gPlayers[j] <> nil then
2508 begin
2509 if gPlayers[j].alive and g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2510 begin
2511 if gPlayers[j].GetFlag(a) then Break;
2512 end;
2513 end;
2514 end;
2515 end;
2516 end;
2517 end;
2518 end;
2519 end;
2520 end;
2523 // old algo
2524 procedure g_Map_DrawPanels (PanelType: Word; hasAmbient: Boolean; constref ambColor: TDFColor);
2526 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2527 var
2528 idx: Integer;
2529 begin
2530 if (panels <> nil) then
2531 begin
2532 // alas, no visible set
2533 for idx := 0 to High(panels) do
2534 begin
2535 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw(hasAmbient, ambColor);
2536 end;
2537 end;
2538 end;
2540 begin
2541 case PanelType of
2542 PANEL_WALL: DrawPanels(gWalls);
2543 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2544 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2545 PANEL_FORE: DrawPanels(gRenderForegrounds);
2546 PANEL_WATER: DrawPanels(gWater);
2547 PANEL_ACID1: DrawPanels(gAcid1);
2548 PANEL_ACID2: DrawPanels(gAcid2);
2549 PANEL_STEP: DrawPanels(gSteps);
2550 end;
2551 end;
2554 // new algo
2555 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2557 function checker (pan: TPanel; tag: Integer): Boolean;
2558 begin
2559 result := false; // don't stop, ever
2560 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2561 gDrawPanelList.insert(pan);
2562 end;
2564 begin
2565 dplClear();
2566 //tagmask := panelTypeToTag(PanelType);
2567 mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask);
2568 // list will be rendered in `g_game.DrawPlayer()`
2569 end;
2572 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2574 function checker (pan: TPanel; tag: Integer): Boolean;
2575 begin
2576 result := false; // don't stop, ever
2577 pan.DrawShadowVolume(lightX, lightY, radius);
2578 end;
2580 begin
2581 mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2582 end;
2585 procedure g_Map_DrawBack(dx, dy: Integer);
2586 begin
2587 if gDrawBackGround and (BackID <> DWORD(-1)) then
2588 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2589 else
2590 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2591 end;
2593 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2594 PanelType: Word; b1x3: Boolean=false): Boolean;
2595 var
2596 a, h: Integer;
2597 begin
2598 Result := False;
2600 if WordBool(PanelType and PANEL_WALL) then
2601 if gWalls <> nil then
2602 begin
2603 h := High(gWalls);
2605 for a := 0 to h do
2606 if gWalls[a].Enabled and
2607 g_Collide(X, Y, Width, Height,
2608 gWalls[a].X, gWalls[a].Y,
2609 gWalls[a].Width, gWalls[a].Height) then
2610 begin
2611 Result := True;
2612 Exit;
2613 end;
2614 end;
2616 if WordBool(PanelType and PANEL_WATER) then
2617 if gWater <> nil then
2618 begin
2619 h := High(gWater);
2621 for a := 0 to h do
2622 if g_Collide(X, Y, Width, Height,
2623 gWater[a].X, gWater[a].Y,
2624 gWater[a].Width, gWater[a].Height) then
2625 begin
2626 Result := True;
2627 Exit;
2628 end;
2629 end;
2631 if WordBool(PanelType and PANEL_ACID1) then
2632 if gAcid1 <> nil then
2633 begin
2634 h := High(gAcid1);
2636 for a := 0 to h do
2637 if g_Collide(X, Y, Width, Height,
2638 gAcid1[a].X, gAcid1[a].Y,
2639 gAcid1[a].Width, gAcid1[a].Height) then
2640 begin
2641 Result := True;
2642 Exit;
2643 end;
2644 end;
2646 if WordBool(PanelType and PANEL_ACID2) then
2647 if gAcid2 <> nil then
2648 begin
2649 h := High(gAcid2);
2651 for a := 0 to h do
2652 if g_Collide(X, Y, Width, Height,
2653 gAcid2[a].X, gAcid2[a].Y,
2654 gAcid2[a].Width, gAcid2[a].Height) then
2655 begin
2656 Result := True;
2657 Exit;
2658 end;
2659 end;
2661 if WordBool(PanelType and PANEL_STEP) then
2662 if gSteps <> nil then
2663 begin
2664 h := High(gSteps);
2666 for a := 0 to h do
2667 if g_Collide(X, Y, Width, Height,
2668 gSteps[a].X, gSteps[a].Y,
2669 gSteps[a].Width, gSteps[a].Height) then
2670 begin
2671 Result := True;
2672 Exit;
2673 end;
2674 end;
2676 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2677 if gLifts <> nil then
2678 begin
2679 h := High(gLifts);
2681 for a := 0 to h do
2682 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2683 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2684 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2685 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2686 g_Collide(X, Y, Width, Height,
2687 gLifts[a].X, gLifts[a].Y,
2688 gLifts[a].Width, gLifts[a].Height) then
2689 begin
2690 Result := True;
2691 Exit;
2692 end;
2693 end;
2695 if WordBool(PanelType and PANEL_BLOCKMON) then
2696 if gBlockMon <> nil then
2697 begin
2698 h := High(gBlockMon);
2700 for a := 0 to h do
2701 if ( (not b1x3) or
2702 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2703 g_Collide(X, Y, Width, Height,
2704 gBlockMon[a].X, gBlockMon[a].Y,
2705 gBlockMon[a].Width, gBlockMon[a].Height) then
2706 begin
2707 Result := True;
2708 Exit;
2709 end;
2710 end;
2711 end;
2713 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2714 var
2715 texid: DWORD;
2717 function checkPanels (constref panels: TPanelArray): Boolean;
2718 var
2719 a: Integer;
2720 begin
2721 result := false;
2722 if panels = nil then exit;
2723 for a := 0 to High(panels) do
2724 begin
2725 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2726 begin
2727 result := true;
2728 texid := panels[a].GetTextureID();
2729 exit;
2730 end;
2731 end;
2732 end;
2734 begin
2735 texid := LongWord(TEXTURE_NONE);
2736 result := texid;
2737 if not checkPanels(gWater) then
2738 if not checkPanels(gAcid1) then
2739 if not checkPanels(gAcid2) then exit;
2740 result := texid;
2741 end;
2744 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2745 const
2746 SlowMask = GridTagLift or GridTagBlockMon;
2747 function checker (pan: TPanel; tag: Integer): Boolean;
2748 begin
2750 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2751 begin
2752 result := pan.Enabled;
2753 exit;
2754 end;
2757 if ((tag and GridTagLift) <> 0) then
2758 begin
2759 result :=
2760 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2761 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2762 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2763 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
2764 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
2765 exit;
2766 end;
2768 if ((tag and GridTagBlockMon) <> 0) then
2769 begin
2770 result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2771 exit;
2772 end;
2774 // other shit
2775 //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2776 result := true; // i found her!
2777 end;
2779 var
2780 tagmask: Integer = 0;
2781 begin
2782 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2783 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2784 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2785 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2786 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2787 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2788 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2790 if (tagmask = 0) then begin result := false; exit; end; // just in case
2792 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2793 if gdbg_map_use_accel_coldet then
2794 begin
2795 if (Width = 1) and (Height = 1) then
2796 begin
2797 if ((tagmask and SlowMask) <> 0) then
2798 begin
2799 // slow
2800 result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil);
2801 end
2802 else
2803 begin
2804 // fast
2805 result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil);
2806 end;
2807 end
2808 else
2809 begin
2810 if ((tagmask and SlowMask) <> 0) then
2811 begin
2812 // slow
2813 result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil);
2814 end
2815 else
2816 begin
2817 // fast
2818 result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil);
2819 end;
2820 end;
2821 end
2822 else
2823 begin
2824 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2825 end;
2826 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2827 end;
2830 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2831 var
2832 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2833 texid: DWORD;
2835 // slightly different from the old code, but meh...
2836 function checker (pan: TPanel; tag: Integer): Boolean;
2837 begin
2838 result := false; // don't stop, ever
2839 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2840 // check priorities
2841 case cctype of
2842 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2843 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2844 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2845 end;
2846 // collision?
2847 //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2848 // yeah
2849 texid := pan.GetTextureID();
2850 // water? water has the highest priority, so stop right here
2851 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2852 // acid2?
2853 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2854 // acid1?
2855 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2856 end;
2858 begin
2859 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2860 if gdbg_map_use_accel_coldet then
2861 begin
2862 texid := LongWord(TEXTURE_NONE);
2863 if (Width = 1) and (Height = 1) then
2864 begin
2865 mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2866 end
2867 else
2868 begin
2869 mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2870 end;
2871 result := texid;
2872 end
2873 else
2874 begin
2875 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2876 end;
2877 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2878 end;
2881 procedure g_Map_EnableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_EnableWallGUID(gWalls[ID].guid); end;
2882 procedure g_Map_DisableWall_XXX (ID: DWORD); begin if (ID < Length(gWalls)) then g_Map_DisableWallGUID(gWalls[ID].guid); end;
2883 procedure g_Map_SetLift_XXX (ID: DWORD; t: Integer); begin if (ID < Length(gLifts)) then g_Map_SetLiftGUID(gLifts[ID].guid, t); end;
2886 procedure g_Map_EnableWallGUID (pguid: Integer);
2887 var
2888 pan: TPanel;
2889 begin
2890 //pan := gWalls[ID];
2891 pan := g_Map_PanelByGUID(pguid);
2892 if (pan = nil) then exit;
2893 if pan.Enabled and mapGrid.proxyEnabled[pan.proxyId] then exit;
2895 pan.Enabled := True;
2896 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, true);
2898 mapGrid.proxyEnabled[pan.proxyId] := true;
2899 //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true
2900 //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor);
2902 //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pguid);
2903 // mark platform as interesting
2904 pan.setDirty();
2906 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2907 //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);
2908 {$ENDIF}
2909 end;
2912 procedure g_Map_DisableWallGUID (pguid: Integer);
2913 var
2914 pan: TPanel;
2915 begin
2916 //pan := gWalls[ID];
2917 pan := g_Map_PanelByGUID(pguid);
2918 if (pan = nil) then exit;
2919 if (not pan.Enabled) and (not mapGrid.proxyEnabled[pan.proxyId]) then exit;
2921 pan.Enabled := False;
2922 g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, false);
2924 mapGrid.proxyEnabled[pan.proxyId] := false;
2925 //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end;
2927 //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pguid);
2928 // mark platform as interesting
2929 pan.setDirty();
2931 {$IFDEF MAP_DEBUG_ENABLED_FLAG}
2932 //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);
2933 {$ENDIF}
2934 end;
2937 procedure g_Map_SwitchTextureGUID (pguid: Integer; AnimLoop: Byte = 0);
2938 var
2939 tp: TPanel;
2940 begin
2941 tp := g_Map_PanelByGUID(pguid);
2942 if (tp = nil) then exit;
2943 tp.NextTexture(AnimLoop);
2944 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelTexture(pguid, AnimLoop);
2945 end;
2948 procedure g_Map_SetLiftGUID (pguid: Integer; t: Integer);
2949 var
2950 pan: TPanel;
2951 begin
2952 //pan := gLifts[ID];
2953 pan := g_Map_PanelByGUID(pguid);
2954 if (pan = nil) then exit;
2955 if not pan.isGLift then exit;
2957 if ({gLifts[ID]}pan.LiftType = t) then exit; //!FIXME!TRIGANY!
2959 with {gLifts[ID]} pan do
2960 begin
2961 LiftType := t;
2963 g_Mark(X, Y, Width, Height, MARK_LIFT, false);
2964 //TODO: make separate lift tags, and change tag here
2966 case LiftType of
2967 0: g_Mark(X, Y, Width, Height, MARK_LIFTUP);
2968 1: g_Mark(X, Y, Width, Height, MARK_LIFTDOWN);
2969 2: g_Mark(X, Y, Width, Height, MARK_LIFTLEFT);
2970 3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT);
2971 end;
2973 //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pguid);
2974 // mark platform as interesting
2975 pan.setDirty();
2976 end;
2977 end;
2980 function g_Map_GetPoint (PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2981 var
2982 a: Integer;
2983 PointsArray: Array of TRespawnPoint;
2984 begin
2985 Result := False;
2986 SetLength(PointsArray, 0);
2988 if RespawnPoints = nil then
2989 Exit;
2991 for a := 0 to High(RespawnPoints) do
2992 if RespawnPoints[a].PointType = PointType then
2993 begin
2994 SetLength(PointsArray, Length(PointsArray)+1);
2995 PointsArray[High(PointsArray)] := RespawnPoints[a];
2996 end;
2998 if PointsArray = nil then
2999 Exit;
3001 RespawnPoint := PointsArray[Random(Length(PointsArray))];
3002 Result := True;
3003 end;
3005 function g_Map_GetPointCount(PointType: Byte): Word;
3006 var
3007 a: Integer;
3008 begin
3009 Result := 0;
3011 if RespawnPoints = nil then
3012 Exit;
3014 for a := 0 to High(RespawnPoints) do
3015 if RespawnPoints[a].PointType = PointType then
3016 Result := Result + 1;
3017 end;
3019 function g_Map_HaveFlagPoints(): Boolean;
3020 begin
3021 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
3022 end;
3024 procedure g_Map_ResetFlag(Flag: Byte);
3025 begin
3026 with gFlags[Flag] do
3027 begin
3028 Obj.X := -1000;
3029 Obj.Y := -1000;
3030 Obj.Vel.X := 0;
3031 Obj.Vel.Y := 0;
3032 Direction := TDirection.D_LEFT;
3033 State := FLAG_STATE_NONE;
3034 if FlagPoints[Flag] <> nil then
3035 begin
3036 Obj.X := FlagPoints[Flag]^.X;
3037 Obj.Y := FlagPoints[Flag]^.Y;
3038 Direction := FlagPoints[Flag]^.Direction;
3039 State := FLAG_STATE_NORMAL;
3040 end;
3041 Count := -1;
3042 end;
3043 end;
3045 procedure g_Map_DrawFlags();
3046 var
3047 i, dx: Integer;
3048 Mirror: TMirrorType;
3049 begin
3050 if gGameSettings.GameMode <> GM_CTF then
3051 Exit;
3053 for i := FLAG_RED to FLAG_BLUE do
3054 with gFlags[i] do
3055 if State <> FLAG_STATE_CAPTURED then
3056 begin
3057 if State = FLAG_STATE_NONE then
3058 continue;
3060 if Direction = TDirection.D_LEFT then
3061 begin
3062 Mirror := TMirrorType.Horizontal;
3063 dx := -1;
3064 end
3065 else
3066 begin
3067 Mirror := TMirrorType.None;
3068 dx := 1;
3069 end;
3071 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
3073 if g_debug_Frames then
3074 begin
3075 e_DrawQuad(Obj.X+Obj.Rect.X,
3076 Obj.Y+Obj.Rect.Y,
3077 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
3078 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
3079 0, 255, 0);
3080 end;
3081 end;
3082 end;
3085 procedure g_Map_SaveState (st: TStream);
3086 var
3087 str: String;
3089 procedure savePanels ();
3090 var
3091 pan: TPanel;
3092 begin
3093 // Ñîõðàíÿåì ïàíåëè
3094 utils.writeInt(st, LongInt(Length(panByGUID)));
3095 for pan in panByGUID do pan.SaveState(st);
3096 end;
3098 procedure saveFlag (flag: PFlag);
3099 var
3100 b: Byte;
3101 begin
3102 utils.writeSign(st, 'FLAG');
3103 utils.writeInt(st, Byte(0)); // version
3104 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3105 utils.writeInt(st, Byte(flag^.RespawnType));
3106 // Ñîñòîÿíèå ôëàãà
3107 utils.writeInt(st, Byte(flag^.State));
3108 // Íàïðàâëåíèå ôëàãà
3109 if flag^.Direction = TDirection.D_LEFT then b := 1 else b := 2; // D_RIGHT
3110 utils.writeInt(st, Byte(b));
3111 // Îáúåêò ôëàãà
3112 Obj_SaveState(st, @flag^.Obj);
3113 end;
3115 begin
3116 savePanels();
3118 // Ñîõðàíÿåì ìóçûêó
3119 utils.writeSign(st, 'MUSI');
3120 utils.writeInt(st, Byte(0));
3121 // Íàçâàíèå ìóçûêè
3122 assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
3123 if gMusic.NoMusic then str := '' else str := gMusic.Name;
3124 utils.writeStr(st, str);
3125 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3126 utils.writeInt(st, LongWord(gMusic.GetPosition()));
3127 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3128 utils.writeBool(st, gMusic.SpecPause);
3130 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
3131 utils.writeInt(st, LongInt(gTotalMonsters));
3132 ///// /////
3134 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
3135 if (gGameSettings.GameMode = GM_CTF) then
3136 begin
3137 // Ôëàã Êðàñíîé êîìàíäû
3138 saveFlag(@gFlags[FLAG_RED]);
3139 // Ôëàã Ñèíåé êîìàíäû
3140 saveFlag(@gFlags[FLAG_BLUE]);
3141 end;
3142 ///// /////
3144 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3145 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3146 begin
3147 // Î÷êè Êðàñíîé êîìàíäû
3148 utils.writeInt(st, SmallInt(gTeamStat[TEAM_RED].Goals));
3149 // Î÷êè Ñèíåé êîìàíäû
3150 utils.writeInt(st, SmallInt(gTeamStat[TEAM_BLUE].Goals));
3151 end;
3152 ///// /////
3153 end;
3156 procedure g_Map_LoadState (st: TStream);
3157 var
3158 dw: DWORD;
3159 str: String;
3160 boo: Boolean;
3162 procedure loadPanels ();
3163 var
3164 pan: TPanel;
3165 begin
3166 // Çàãðóæàåì ïàíåëè
3167 if (Length(panByGUID) <> utils.readLongInt(st)) then raise XStreamError.Create('invalid number of saved panels');
3168 for pan in panByGUID do
3169 begin
3170 pan.LoadState(st);
3171 if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
3172 end;
3173 end;
3175 procedure loadFlag (flag: PFlag);
3176 var
3177 b: Byte;
3178 begin
3179 // Ñèãíàòóðà ôëàãà
3180 if not utils.checkSign(st, 'FLAG') then raise XStreamError.Create('invalid flag signature');
3181 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid flag version');
3182 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà
3183 flag^.RespawnType := utils.readByte(st);
3184 // Ñîñòîÿíèå ôëàãà
3185 flag^.State := utils.readByte(st);
3186 // Íàïðàâëåíèå ôëàãà
3187 b := utils.readByte(st);
3188 if (b = 1) then flag^.Direction := TDirection.D_LEFT else flag^.Direction := TDirection.D_RIGHT; // b = 2
3189 // Îáúåêò ôëàãà
3190 Obj_LoadState(@flag^.Obj, st);
3191 end;
3193 begin
3194 if (st = nil) then exit;
3196 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
3197 loadPanels();
3198 ///// /////
3200 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó
3201 g_GFX_Init();
3202 //mapCreateGrid();
3204 ///// Çàãðóæàåì ìóçûêó: /////
3205 if not utils.checkSign(st, 'MUSI') then raise XStreamError.Create('invalid music signature');
3206 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid music version');
3207 // Íàçâàíèå ìóçûêè
3208 assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
3209 str := utils.readStr(st);
3210 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè
3211 dw := utils.readLongWord(st);
3212 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå
3213 boo := utils.readBool(st);
3214 // Çàïóñêàåì ýòó ìóçûêó
3215 gMusic.SetByName(str);
3216 gMusic.SpecPause := boo;
3217 gMusic.Play();
3218 gMusic.Pause(true);
3219 gMusic.SetPosition(dw);
3220 ///// /////
3222 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
3223 gTotalMonsters := utils.readLongInt(st);
3224 ///// /////
3226 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
3227 if (gGameSettings.GameMode = GM_CTF) then
3228 begin
3229 // Ôëàã Êðàñíîé êîìàíäû
3230 loadFlag(@gFlags[FLAG_RED]);
3231 // Ôëàã Ñèíåé êîìàíäû
3232 loadFlag(@gFlags[FLAG_BLUE]);
3233 end;
3234 ///// /////
3236 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
3237 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
3238 begin
3239 // Î÷êè Êðàñíîé êîìàíäû
3240 gTeamStat[TEAM_RED].Goals := utils.readSmallInt(st);
3241 // Î÷êè Ñèíåé êîìàíäû
3242 gTeamStat[TEAM_BLUE].Goals := utils.readSmallInt(st);
3243 end;
3244 ///// /////
3245 end;
3248 // trace liquid, stepping by `dx` and `dy`
3249 // return last seen liquid coords, and `false` if we're started outside of the liquid
3250 function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
3251 const
3252 MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
3253 begin
3254 topx := x;
3255 topy := y;
3256 // started outside of the liquid?
3257 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end;
3258 if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check
3259 result := true;
3260 while true do
3261 begin
3262 Inc(x, dx);
3263 Inc(y, dy);
3264 if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit
3265 topx := x;
3266 topy := y;
3267 end;
3268 end;
3271 begin
3272 DynWarningCB := mapWarningCB;
3273 end.