DEADSOFTWARE

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