DEADSOFTWARE

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