DEADSOFTWARE

removed some unused code
[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 unit g_map;
19 interface
21 uses
22 e_graphics, g_basic, MAPSTRUCT, g_textures, Classes,
23 g_phys, wadreader, BinEditor, g_panel, g_grid, z_aabbtree, md5, binheap, xprofiler;
25 type
26 TMapInfo = record
27 Map: String;
28 Name: String;
29 Description: String;
30 Author: String;
31 MusicName: String;
32 SkyName: String;
33 Height: Word;
34 Width: Word;
35 end;
37 PRespawnPoint = ^TRespawnPoint;
38 TRespawnPoint = record
39 X, Y: Integer;
40 Direction: TDirection;
41 PointType: Byte;
42 end;
44 PFlagPoint = ^TFlagPoint;
45 TFlagPoint = TRespawnPoint;
47 PFlag = ^TFlag;
48 TFlag = record
49 Obj: TObj;
50 RespawnType: Byte;
51 State: Byte;
52 Count: Integer;
53 CaptureTime: LongWord;
54 Animation: TAnimation;
55 Direction: TDirection;
56 end;
58 function g_Map_Load(Res: String): Boolean;
59 function g_Map_GetMapInfo(Res: String): TMapInfo;
60 function g_Map_GetMapsList(WADName: String): SArray;
61 function g_Map_Exist(Res: String): Boolean;
62 procedure g_Map_Free();
63 procedure g_Map_Update();
65 procedure g_Map_DrawPanels (PanelType: Word); // unaccelerated
66 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
68 procedure g_Map_DrawBack(dx, dy: Integer);
69 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
70 PanelType: Word; b1x3: Boolean=false): Boolean;
71 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
72 procedure g_Map_EnableWall(ID: DWORD);
73 procedure g_Map_DisableWall(ID: DWORD);
74 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
75 procedure g_Map_SetLift(ID: DWORD; t: Integer);
76 procedure g_Map_ReAdd_DieTriggers();
77 function g_Map_IsSpecialTexture(Texture: String): Boolean;
79 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
80 function g_Map_GetPointCount(PointType: Byte): Word;
82 function g_Map_HaveFlagPoints(): Boolean;
84 procedure g_Map_ResetFlag(Flag: Byte);
85 procedure g_Map_DrawFlags();
87 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
89 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
90 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
92 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
94 // returns wall index in `gWalls` or -1
95 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Integer;
97 type
98 TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop
100 function g_Map_ForEachPanelAt (x, y: Integer; cb: TForEachPanelCB; panelType: Word): Boolean;
103 procedure g_Map_ProfilersBegin ();
104 procedure g_Map_ProfilersEnd ();
106 const
107 RESPAWNPOINT_PLAYER1 = 1;
108 RESPAWNPOINT_PLAYER2 = 2;
109 RESPAWNPOINT_DM = 3;
110 RESPAWNPOINT_RED = 4;
111 RESPAWNPOINT_BLUE = 5;
113 FLAG_NONE = 0;
114 FLAG_RED = 1;
115 FLAG_BLUE = 2;
116 FLAG_DOM = 3;
118 FLAG_STATE_NONE = 0;
119 FLAG_STATE_NORMAL = 1;
120 FLAG_STATE_DROPPED = 2;
121 FLAG_STATE_CAPTURED = 3;
122 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
123 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
125 FLAG_TIME = 720; // 20 seconds
127 SKY_STRETCH: Single = 1.5;
129 const
130 GridTagInvalid = 0;
132 (* draw order:
133 PANEL_BACK
134 PANEL_STEP
135 PANEL_WALL
136 PANEL_CLOSEDOOR
137 PANEL_ACID1
138 PANEL_ACID2
139 PANEL_WATER
140 PANEL_FORE
141 *)
142 // sorted by draw priority
143 GridTagBack = 1 shl 0;
144 GridTagStep = 1 shl 1;
145 GridTagWall = 1 shl 2;
146 GridTagDoor = 1 shl 3;
147 GridTagAcid1 = 1 shl 4;
148 GridTagAcid2 = 1 shl 5;
149 GridTagWater = 1 shl 6;
150 GridTagFore = 1 shl 7;
151 // the following are invisible
152 GridTagLift = 1 shl 8;
153 GridTagBlockMon = 1 shl 9;
156 var
157 gWalls: TPanelArray;
158 gRenderBackgrounds: TPanelArray;
159 gRenderForegrounds: TPanelArray;
160 gWater, gAcid1, gAcid2: TPanelArray;
161 gSteps: TPanelArray;
162 gLifts: TPanelArray;
163 gBlockMon: TPanelArray;
164 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
165 //gDOMFlags: array of TFlag;
166 gMapInfo: TMapInfo;
167 gBackSize: TPoint;
168 gDoorMap: array of array of DWORD;
169 gLiftMap: array of array of DWORD;
170 gWADHash: TMD5Digest;
171 BackID: DWORD = DWORD(-1);
172 gExternalResources: TStringList;
174 gdbg_map_use_accel_render: Boolean = true;
175 gdbg_map_use_accel_coldet: Boolean = true;
176 gdbg_map_use_tree_draw: Boolean = false;
177 gdbg_map_use_tree_coldet: Boolean = false;
178 gdbg_map_dump_coldet_tree_queries: Boolean = false;
179 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
180 gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
182 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
185 implementation
187 uses
188 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
189 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
190 g_options, MAPREADER, g_triggers, g_player, MAPDEF,
191 Math, g_monsters, g_saveload, g_language, g_netmsg,
192 utils, sfs,
193 ImagingTypes, Imaging, ImagingUtility,
194 ImagingGif, ImagingNetworkGraphics;
196 const
197 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
198 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
199 FLAG_SIGNATURE = $47414C46; // 'FLAG'
202 type
203 TPanelGrid = specialize TBodyGridBase<TPanel>;
205 TDynAABBTreePanelBase = specialize TDynAABBTreeBase<TPanel>;
207 TDynAABBTreeMap = class(TDynAABBTreePanelBase)
208 function getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean; override;
209 end;
211 function TDynAABBTreeMap.getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean;
212 begin
213 result := false;
214 if (pan = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end;
215 aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width-1, pan.Y+pan.Height-1);
216 if (pan.Width < 1) or (pan.Height < 1) then exit;
217 if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
218 result := true;
219 end;
222 function panelTypeToTag (panelType: Word): Integer;
223 begin
224 case panelType of
225 PANEL_WALL: result := GridTagWall; // gWalls
226 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
227 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
228 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
229 PANEL_WATER: result := GridTagWater; // gWater
230 PANEL_ACID1: result := GridTagAcid1; // gAcid1
231 PANEL_ACID2: result := GridTagAcid2; // gAcid2
232 PANEL_STEP: result := GridTagStep; // gSteps
233 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
234 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
235 else result := GridTagInvalid;
236 end;
237 end;
240 function dplLess (a, b: TObject): Boolean;
241 var
242 pa, pb: TPanel;
243 begin
244 //result := ((a as TPanel).ArrIdx < (b as TPanel).ArrIdx);
245 pa := TPanel(a);
246 pb := TPanel(b);
247 if (pa.tag < pb.tag) then begin result := true; exit; end;
248 if (pa.tag > pb.tag) then begin result := false; exit; end;
249 result := (pa.ArrIdx < pb.ArrIdx);
250 end;
252 procedure dplClear ();
253 begin
254 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
255 end;
258 type
259 TPanelID = record
260 PWhere: ^TPanelArray;
261 PArrID: Integer;
262 end;
264 var
265 PanelById: array of TPanelID;
266 Textures: TLevelTextureArray;
267 RespawnPoints: Array of TRespawnPoint;
268 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
269 //DOMFlagPoints: Array of TFlagPoint;
270 gMapGrid: TPanelGrid = nil;
271 mapTree: TDynAABBTreeMap = nil;
274 procedure g_Map_ProfilersBegin ();
275 begin
276 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
277 profMapCollision.mainBegin(g_profile_collision);
278 // create sections
279 if g_profile_collision then
280 begin
281 profMapCollision.sectionBegin('*solids');
282 profMapCollision.sectionEnd();
283 profMapCollision.sectionBegin('liquids');
284 profMapCollision.sectionEnd();
285 end;
286 end;
288 procedure g_Map_ProfilersEnd ();
289 begin
290 if (profMapCollision <> nil) then profMapCollision.mainEnd();
291 end;
294 // wall index in `gWalls` or -1
295 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Integer;
297 function sqchecker (pan: TPanel; var ray: Ray2D): Single;
298 var
299 aabb: AABB2D;
300 tmin: Single;
301 begin
302 result := -666.0; // invalid
303 if not pan.Enabled then exit;
304 aabb := AABB2D.CreateWH(pan.X, pan.Y, pan.Width, pan.Height);
305 if not aabb.valid then exit;
306 if aabb.intersects(ray, @tmin) then
307 begin
308 //if (tmin*tmin > maxDistSq) then exit;
309 if (tmin >= 0.0) then
310 begin
311 //e_WriteLog(Format('sqchecker(%d,%d,%d,%d): panel #%d (%d,%d)-(%d,%d); tmin=%f', [x0, y0, x1, y1, pan.arrIdx, pan.X, pan.Y, pan.Width, pan.Height, tmin]), MSG_NOTIFY);
312 //if (tmin < 0.0) then tmin := 0.0;
313 result := tmin;
314 end;
315 end;
316 end;
318 var
319 qr: TDynAABBTreeMap.TSegmentQueryResult;
320 ray: Ray2D;
321 hxf, hyf: Single;
322 hx, hy: Integer;
323 maxDistSq: Single;
324 begin
325 result := -1;
326 if (mapTree = nil) then exit;
327 if mapTree.segmentQuery(qr, x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor)) then
328 begin
329 maxDistSq := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0);
330 if (qr.flesh <> nil) and (qr.time*qr.time <= maxDistSq) then
331 begin
332 result := qr.flesh.arrIdx;
333 if (hitx <> nil) or (hity <> nil) then
334 begin
335 ray := Ray2D.Create(x0, y0, x1, y1);
336 hxf := ray.origX+ray.dirX*qr.time;
337 hyf := ray.origY+ray.dirY*qr.time;
338 while true do
339 begin
340 hx := trunc(hxf);
341 hy := trunc(hyf);
342 if (hx >= qr.flesh.X) and (hy >= qr.flesh.Y) and (hx < qr.flesh.X+qr.flesh.Width) and (hy < qr.flesh.Y+qr.flesh.Height) then
343 begin
344 // go back a little
345 hxf -= ray.dirX;
346 hyf -= ray.dirY;
347 end
348 else
349 begin
350 break;
351 end;
352 end;
353 if (hitx <> nil) then hitx^ := hx;
354 if (hity <> nil) then hity^ := hy;
355 end;
356 end;
357 end;
358 end;
361 function g_Map_ForEachPanelAt (x, y: Integer; cb: TForEachPanelCB; panelType: Word): Boolean;
363 function checker (pan: TPanel; tag: Integer): Boolean;
364 begin
365 result := false; // don't stop, ever
367 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
368 begin
369 if not pan.Enabled then exit;
370 end;
372 if ((tag and GridTagLift) <> 0) then
373 begin
374 result :=
375 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
376 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
377 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
378 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) and
379 (x >= pan.X) and (y >= pan.Y) and (x < pan.X+pan.Width) and (y < pan.Y+pan.Height);
380 if result then result := cb(pan);;
381 exit;
382 end;
384 // other shit
385 result := (x >= pan.X) and (y >= pan.Y) and (x < pan.X+pan.Width) and (y < pan.Y+pan.Height);
386 if result then result := cb(pan);
387 end;
389 var
390 tagmask: Integer = 0;
391 begin
392 result := false;
393 if not assigned(cb) then exit;
394 //if (mapTree = nil) then exit;
395 //function TDynAABBTreeBase.pointQuery (ax, ay: TreeNumber; cb: TQueryOverlapCB; tagmask: Integer=-1): TTreeFlesh;
397 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
398 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
399 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
400 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
401 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
402 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
403 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
405 if (tagmask = 0) then exit;// just in case
406 result := gMapGrid.forEachInAABB(x, y, 1, 1, checker, tagmask);
407 end;
410 function g_Map_IsSpecialTexture(Texture: String): Boolean;
411 begin
412 Result := (Texture = TEXTURE_NAME_WATER) or
413 (Texture = TEXTURE_NAME_ACID1) or
414 (Texture = TEXTURE_NAME_ACID2);
415 end;
417 procedure CreateDoorMap();
418 var
419 PanelArray: Array of record
420 X, Y: Integer;
421 Width, Height: Word;
422 Active: Boolean;
423 PanelID: DWORD;
424 end;
425 a, b, c, m, i, len: Integer;
426 ok: Boolean;
427 begin
428 if gWalls = nil then
429 Exit;
431 i := 0;
432 len := 128;
433 SetLength(PanelArray, len);
435 for a := 0 to High(gWalls) do
436 if gWalls[a].Door then
437 begin
438 PanelArray[i].X := gWalls[a].X;
439 PanelArray[i].Y := gWalls[a].Y;
440 PanelArray[i].Width := gWalls[a].Width;
441 PanelArray[i].Height := gWalls[a].Height;
442 PanelArray[i].Active := True;
443 PanelArray[i].PanelID := a;
445 i := i + 1;
446 if i = len then
447 begin
448 len := len + 128;
449 SetLength(PanelArray, len);
450 end;
451 end;
453 // Íåò äâåðåé:
454 if i = 0 then
455 begin
456 PanelArray := nil;
457 Exit;
458 end;
460 SetLength(gDoorMap, 0);
462 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
464 for a := 0 to i-1 do
465 if PanelArray[a].Active then
466 begin
467 PanelArray[a].Active := False;
468 m := Length(gDoorMap);
469 SetLength(gDoorMap, m+1);
470 SetLength(gDoorMap[m], 1);
471 gDoorMap[m, 0] := PanelArray[a].PanelID;
472 ok := True;
474 while ok do
475 begin
476 ok := False;
478 for b := 0 to i-1 do
479 if PanelArray[b].Active then
480 for c := 0 to High(gDoorMap[m]) do
481 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
482 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
483 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
484 PanelArray[b].Width, PanelArray[b].Height,
485 gWalls[gDoorMap[m, c]].X,
486 gWalls[gDoorMap[m, c]].Y,
487 gWalls[gDoorMap[m, c]].Width,
488 gWalls[gDoorMap[m, c]].Height) then
489 begin
490 PanelArray[b].Active := False;
491 SetLength(gDoorMap[m],
492 Length(gDoorMap[m])+1);
493 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
494 ok := True;
495 Break;
496 end;
497 end;
499 g_Game_StepLoading();
500 end;
502 PanelArray := nil;
503 end;
505 procedure CreateLiftMap();
506 var
507 PanelArray: Array of record
508 X, Y: Integer;
509 Width, Height: Word;
510 Active: Boolean;
511 end;
512 a, b, c, len, i, j: Integer;
513 ok: Boolean;
514 begin
515 if gLifts = nil then
516 Exit;
518 len := Length(gLifts);
519 SetLength(PanelArray, len);
521 for a := 0 to len-1 do
522 begin
523 PanelArray[a].X := gLifts[a].X;
524 PanelArray[a].Y := gLifts[a].Y;
525 PanelArray[a].Width := gLifts[a].Width;
526 PanelArray[a].Height := gLifts[a].Height;
527 PanelArray[a].Active := True;
528 end;
530 SetLength(gLiftMap, len);
531 i := 0;
533 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
535 for a := 0 to len-1 do
536 if PanelArray[a].Active then
537 begin
538 PanelArray[a].Active := False;
539 SetLength(gLiftMap[i], 32);
540 j := 0;
541 gLiftMap[i, j] := a;
542 ok := True;
544 while ok do
545 begin
546 ok := False;
547 for b := 0 to len-1 do
548 if PanelArray[b].Active then
549 for c := 0 to j do
550 if g_CollideAround(PanelArray[b].X,
551 PanelArray[b].Y,
552 PanelArray[b].Width,
553 PanelArray[b].Height,
554 PanelArray[gLiftMap[i, c]].X,
555 PanelArray[gLiftMap[i, c]].Y,
556 PanelArray[gLiftMap[i, c]].Width,
557 PanelArray[gLiftMap[i, c]].Height) then
558 begin
559 PanelArray[b].Active := False;
560 j := j+1;
561 if j > High(gLiftMap[i]) then
562 SetLength(gLiftMap[i],
563 Length(gLiftMap[i])+32);
565 gLiftMap[i, j] := b;
566 ok := True;
568 Break;
569 end;
570 end;
572 SetLength(gLiftMap[i], j+1);
573 i := i+1;
575 g_Game_StepLoading();
576 end;
578 SetLength(gLiftMap, i);
580 PanelArray := nil;
581 end;
583 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
584 CurTex: Integer; sav: Boolean): Integer;
585 var
586 len: Integer;
587 panels: ^TPanelArray;
588 begin
589 Result := -1;
591 case PanelRec.PanelType of
592 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
593 panels := @gWalls;
594 PANEL_BACK:
595 panels := @gRenderBackgrounds;
596 PANEL_FORE:
597 panels := @gRenderForegrounds;
598 PANEL_WATER:
599 panels := @gWater;
600 PANEL_ACID1:
601 panels := @gAcid1;
602 PANEL_ACID2:
603 panels := @gAcid2;
604 PANEL_STEP:
605 panels := @gSteps;
606 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
607 panels := @gLifts;
608 PANEL_BLOCKMON:
609 panels := @gBlockMon;
610 else
611 Exit;
612 end;
614 len := Length(panels^);
615 SetLength(panels^, len + 1);
617 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
618 panels^[len].ArrIdx := len;
619 panels^[len].tag := panelTypeToTag(PanelRec.PanelType);
620 if sav then
621 panels^[len].SaveIt := True;
623 Result := len;
625 len := Length(PanelByID);
626 SetLength(PanelByID, len + 1);
627 PanelByID[len].PWhere := panels;
628 PanelByID[len].PArrID := Result;
629 end;
631 function CreateNullTexture(RecName: String): Integer;
632 begin
633 SetLength(Textures, Length(Textures)+1);
634 result := High(Textures);
636 with Textures[High(Textures)] do
637 begin
638 TextureName := RecName;
639 Width := 1;
640 Height := 1;
641 Anim := False;
642 TextureID := TEXTURE_NONE;
643 end;
644 end;
646 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
647 var
648 WAD: TWADFile;
649 TextureData: Pointer;
650 WADName, txname: String;
651 a, ResLength: Integer;
652 begin
653 Result := -1;
655 if Textures <> nil then
656 for a := 0 to High(Textures) do
657 if Textures[a].TextureName = RecName then
658 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
659 Result := a;
660 Exit;
661 end;
663 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
664 if (RecName = TEXTURE_NAME_WATER) or
665 (RecName = TEXTURE_NAME_ACID1) or
666 (RecName = TEXTURE_NAME_ACID2) then
667 begin
668 SetLength(Textures, Length(Textures)+1);
670 with Textures[High(Textures)] do
671 begin
672 TextureName := RecName;
674 if TextureName = TEXTURE_NAME_WATER then
675 TextureID := TEXTURE_SPECIAL_WATER
676 else
677 if TextureName = TEXTURE_NAME_ACID1 then
678 TextureID := TEXTURE_SPECIAL_ACID1
679 else
680 if TextureName = TEXTURE_NAME_ACID2 then
681 TextureID := TEXTURE_SPECIAL_ACID2;
683 Anim := False;
684 end;
686 result := High(Textures);
687 Exit;
688 end;
690 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
691 WADName := g_ExtractWadName(RecName);
693 WAD := TWADFile.Create();
695 if WADName <> '' then
696 WADName := GameDir+'/wads/'+WADName
697 else
698 WADName := Map;
700 WAD.ReadFile(WADName);
702 txname := RecName;
704 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
705 begin
706 FreeMem(TextureData);
707 RecName := 'COMMON\ALIEN';
708 end;
711 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
712 begin
713 SetLength(Textures, Length(Textures)+1);
714 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
715 Exit;
716 e_GetTextureSize(Textures[High(Textures)].TextureID,
717 @Textures[High(Textures)].Width,
718 @Textures[High(Textures)].Height);
719 FreeMem(TextureData);
720 Textures[High(Textures)].TextureName := {RecName}txname;
721 Textures[High(Textures)].Anim := False;
723 result := High(Textures);
724 end
725 else // Íåò òàêîãî ðåóñðñà â WAD'å
726 begin
727 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
728 if log then
729 begin
730 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
731 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
732 end;
733 end;
735 WAD.Free();
736 end;
738 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
739 var
740 WAD: TWADFile;
741 TextureWAD: PChar = nil;
742 TextData: Pointer = nil;
743 TextureData: Pointer = nil;
744 cfg: TConfig = nil;
745 WADName: String;
746 ResLength: Integer;
747 TextureResource: String;
748 _width, _height, _framecount, _speed: Integer;
749 _backanimation: Boolean;
750 //imgfmt: string;
751 ia: TDynImageDataArray = nil;
752 f, c, frdelay, frloop: Integer;
753 begin
754 result := -1;
756 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
758 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
759 WADName := g_ExtractWadName(RecName);
761 WAD := TWADFile.Create();
762 try
763 if WADName <> '' then
764 WADName := GameDir+'/wads/'+WADName
765 else
766 WADName := Map;
768 WAD.ReadFile(WADName);
770 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
771 begin
772 if log then
773 begin
774 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
775 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
776 end;
777 exit;
778 end;
780 {TEST
781 if WADName = Map then
782 begin
783 //FreeMem(TextureWAD);
784 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
785 end;
788 WAD.FreeWAD();
790 if ResLength < 6 then
791 begin
792 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
793 exit;
794 end;
796 // ýòî ïòèöà? ýòî ñàìîë¸ò?
797 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
798 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
799 begin
800 // íåò, ýòî ñóïåðìåí!
801 if not WAD.ReadMemory(TextureWAD, ResLength) then
802 begin
803 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
804 exit;
805 end;
807 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
808 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
809 begin
810 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
811 exit;
812 end;
814 cfg := TConfig.CreateMem(TextData, ResLength);
816 TextureResource := cfg.ReadStr('', 'resource', '');
817 if TextureResource = '' then
818 begin
819 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
820 exit;
821 end;
823 _width := cfg.ReadInt('', 'framewidth', 0);
824 _height := cfg.ReadInt('', 'frameheight', 0);
825 _framecount := cfg.ReadInt('', 'framecount', 0);
826 _speed := cfg.ReadInt('', 'waitcount', 0);
827 _backanimation := cfg.ReadBool('', 'backanimation', False);
829 cfg.Free();
830 cfg := nil;
832 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
833 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
834 begin
835 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
836 exit;
837 end;
839 WAD.Free();
840 WAD := nil;
842 SetLength(Textures, Length(Textures)+1);
843 with Textures[High(Textures)] do
844 begin
845 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
846 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
847 begin
848 TextureName := RecName;
849 Width := _width;
850 Height := _height;
851 Anim := True;
852 FramesCount := _framecount;
853 Speed := _speed;
854 result := High(Textures);
855 end
856 else
857 begin
858 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
859 end;
860 end;
861 end
862 else
863 begin
864 // try animated image
866 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
867 if length(imgfmt) = 0 then
868 begin
869 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
870 exit;
871 end;
873 GlobalMetadata.ClearMetaItems();
874 GlobalMetadata.ClearMetaItemsForSaving();
875 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
876 begin
877 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
878 exit;
879 end;
880 if length(ia) = 0 then
881 begin
882 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
883 exit;
884 end;
886 WAD.Free();
887 WAD := nil;
889 _width := ia[0].width;
890 _height := ia[0].height;
891 _framecount := length(ia);
892 _speed := 1;
893 _backanimation := false;
894 frdelay := -1;
895 frloop := -666;
896 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
897 begin
898 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
899 try
900 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
901 frdelay := f;
902 if f < 0 then f := 0;
903 // rounding ;-)
904 c := f mod 28;
905 if c < 13 then c := 0 else c := 1;
906 f := (f div 28)+c;
907 if f < 1 then f := 1 else if f > 255 then f := 255;
908 _speed := f;
909 except
910 end;
911 end;
912 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
913 begin
914 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
915 try
916 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
917 frloop := f;
918 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
919 except
920 end;
921 end;
922 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
923 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
924 f := ord(_backanimation);
925 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);
927 SetLength(Textures, Length(Textures)+1);
928 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
929 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
930 begin
931 Textures[High(Textures)].TextureName := RecName;
932 Textures[High(Textures)].Width := _width;
933 Textures[High(Textures)].Height := _height;
934 Textures[High(Textures)].Anim := True;
935 Textures[High(Textures)].FramesCount := length(ia);
936 Textures[High(Textures)].Speed := _speed;
937 result := High(Textures);
938 //writeln(' CREATED!');
939 end
940 else
941 begin
942 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
943 end;
944 end;
945 finally
946 for f := 0 to High(ia) do FreeImage(ia[f]);
947 WAD.Free();
948 cfg.Free();
949 if TextureWAD <> nil then FreeMem(TextureWAD);
950 if TextData <> nil then FreeMem(TextData);
951 if TextureData <> nil then FreeMem(TextureData);
952 end;
953 end;
955 procedure CreateItem(Item: TItemRec_1);
956 begin
957 if g_Game_IsClient then Exit;
959 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
960 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
961 Exit;
963 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
964 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
965 end;
967 procedure CreateArea(Area: TAreaRec_1);
968 var
969 a: Integer;
970 id: DWORD;
971 begin
972 case Area.AreaType of
973 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
974 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
975 begin
976 SetLength(RespawnPoints, Length(RespawnPoints)+1);
977 with RespawnPoints[High(RespawnPoints)] do
978 begin
979 X := Area.X;
980 Y := Area.Y;
981 Direction := TDirection(Area.Direction);
983 case Area.AreaType of
984 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
985 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
986 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
987 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
988 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
989 end;
990 end;
991 end;
993 AREA_REDFLAG, AREA_BLUEFLAG:
994 begin
995 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
997 if FlagPoints[a] <> nil then Exit;
999 New(FlagPoints[a]);
1001 with FlagPoints[a]^ do
1002 begin
1003 X := Area.X-FLAGRECT.X;
1004 Y := Area.Y-FLAGRECT.Y;
1005 Direction := TDirection(Area.Direction);
1006 end;
1008 with gFlags[a] do
1009 begin
1010 case a of
1011 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
1012 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
1013 end;
1015 Animation := TAnimation.Create(id, True, 8);
1016 Obj.Rect := FLAGRECT;
1018 g_Map_ResetFlag(a);
1019 end;
1020 end;
1022 AREA_DOMFLAG:
1023 begin
1024 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
1025 with DOMFlagPoints[High(DOMFlagPoints)] do
1026 begin
1027 X := Area.X;
1028 Y := Area.Y;
1029 Direction := TDirection(Area.Direction);
1030 end;
1032 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
1033 end;
1034 end;
1035 end;
1037 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
1038 var
1039 _trigger: TTrigger;
1040 begin
1041 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
1043 with _trigger do
1044 begin
1045 X := Trigger.X;
1046 Y := Trigger.Y;
1047 Width := Trigger.Width;
1048 Height := Trigger.Height;
1049 Enabled := ByteBool(Trigger.Enabled);
1050 TexturePanel := Trigger.TexturePanel;
1051 TexturePanelType := fTexturePanel1Type;
1052 ShotPanelType := fTexturePanel2Type;
1053 TriggerType := Trigger.TriggerType;
1054 ActivateType := Trigger.ActivateType;
1055 Keys := Trigger.Keys;
1056 Data.Default := Trigger.DATA;
1057 end;
1059 g_Triggers_Create(_trigger);
1060 end;
1062 procedure CreateMonster(monster: TMonsterRec_1);
1063 var
1064 a: Integer;
1065 mon: TMonster;
1066 begin
1067 if g_Game_IsClient then Exit;
1069 if (gGameSettings.GameType = GT_SINGLE)
1070 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1071 begin
1072 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1074 if gTriggers <> nil then
1075 begin
1076 for a := 0 to High(gTriggers) do
1077 begin
1078 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1079 begin
1080 if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
1081 end;
1082 end;
1083 end;
1085 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1086 end;
1087 end;
1089 procedure g_Map_ReAdd_DieTriggers();
1091 function monsDieTrig (mon: TMonster): Boolean;
1092 var
1093 a: Integer;
1094 begin
1095 result := false; // don't stop
1096 mon.ClearTriggers();
1097 for a := 0 to High(gTriggers) do
1098 begin
1099 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1100 begin
1101 if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
1102 end;
1103 end;
1104 end;
1106 begin
1107 if g_Game_IsClient then Exit;
1109 g_Mons_ForEach(monsDieTrig);
1110 end;
1112 function extractWadName(resourceName: string): string;
1113 var
1114 posN: Integer;
1115 begin
1116 posN := Pos(':', resourceName);
1117 if posN > 0 then
1118 Result:= Copy(resourceName, 0, posN-1)
1119 else
1120 Result := '';
1121 end;
1123 procedure addResToExternalResList(res: string);
1124 begin
1125 res := extractWadName(res);
1126 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1127 gExternalResources.Add(res);
1128 end;
1130 procedure generateExternalResourcesList(mapReader: TMapReader_1);
1131 var
1132 textures: TTexturesRec1Array;
1133 mapHeader: TMapHeaderRec_1;
1134 i: integer;
1135 resFile: String = '';
1136 begin
1137 if gExternalResources = nil then
1138 gExternalResources := TStringList.Create;
1140 gExternalResources.Clear;
1141 textures := mapReader.GetTextures();
1142 for i := 0 to High(textures) do
1143 begin
1144 addResToExternalResList(resFile);
1145 end;
1147 textures := nil;
1149 mapHeader := mapReader.GetMapHeader;
1151 addResToExternalResList(mapHeader.MusicName);
1152 addResToExternalResList(mapHeader.SkyName);
1153 end;
1155 procedure mapCreateGrid ();
1156 var
1157 mapX0: Integer = $3fffffff;
1158 mapY0: Integer = $3fffffff;
1159 mapX1: Integer = -$3fffffff;
1160 mapY1: Integer = -$3fffffff;
1162 procedure calcBoundingBox (constref panels: TPanelArray);
1163 var
1164 idx: Integer;
1165 pan: TPanel;
1166 begin
1167 for idx := 0 to High(panels) do
1168 begin
1169 pan := panels[idx];
1170 if not pan.visvalid then continue;
1171 if (pan.Width < 1) or (pan.Height < 1) then continue;
1172 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1173 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1174 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1175 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1176 end;
1177 end;
1179 procedure addPanelsToGrid (constref panels: TPanelArray; tag: Integer);
1180 var
1181 idx: Integer;
1182 pan: TPanel;
1183 begin
1184 tag := panelTypeToTag(tag);
1185 for idx := High(panels) downto 0 do
1186 begin
1187 pan := panels[idx];
1188 pan.tag := tag;
1189 if not pan.visvalid then continue;
1190 gMapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, tag);
1191 mapTree.insertObject(pan, tag, true); // as static object
1192 end;
1193 end;
1195 begin
1196 gMapGrid.Free();
1197 gMapGrid := nil;
1198 mapTree.Free();
1199 mapTree := nil;
1201 calcBoundingBox(gWalls);
1202 calcBoundingBox(gRenderBackgrounds);
1203 calcBoundingBox(gRenderForegrounds);
1204 calcBoundingBox(gWater);
1205 calcBoundingBox(gAcid1);
1206 calcBoundingBox(gAcid2);
1207 calcBoundingBox(gSteps);
1208 calcBoundingBox(gLifts);
1209 calcBoundingBox(gBlockMon);
1211 e_WriteLog(Format('map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING);
1213 gMapGrid := TPanelGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
1214 mapTree := TDynAABBTreeMap.Create();
1216 addPanelsToGrid(gWalls, PANEL_WALL);
1217 addPanelsToGrid(gWalls, PANEL_CLOSEDOOR);
1218 addPanelsToGrid(gWalls, PANEL_OPENDOOR);
1219 addPanelsToGrid(gRenderBackgrounds, PANEL_BACK);
1220 addPanelsToGrid(gRenderForegrounds, PANEL_FORE);
1221 addPanelsToGrid(gWater, PANEL_WATER);
1222 addPanelsToGrid(gAcid1, PANEL_ACID1);
1223 addPanelsToGrid(gAcid2, PANEL_ACID2);
1224 addPanelsToGrid(gSteps, PANEL_STEP);
1225 addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here
1226 addPanelsToGrid(gBlockMon, PANEL_BLOCKMON);
1228 gMapGrid.dumpStats();
1229 e_WriteLog(Format('tree depth: %d; %d nodes used, %d nodes allocated', [mapTree.computeTreeHeight, mapTree.nodeCount, mapTree.nodeAlloced]), MSG_NOTIFY);
1230 mapTree.forEachLeaf(nil);
1231 end;
1233 function g_Map_Load(Res: String): Boolean;
1234 const
1235 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1236 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1237 var
1238 WAD: TWADFile;
1239 MapReader: TMapReader_1;
1240 Header: TMapHeaderRec_1;
1241 _textures: TTexturesRec1Array;
1242 _texnummap: array of Integer; // `_textures` -> `Textures`
1243 panels: TPanelsRec1Array;
1244 items: TItemsRec1Array;
1245 monsters: TMonsterRec1Array;
1246 areas: TAreasRec1Array;
1247 triggers: TTriggersRec1Array;
1248 a, b, c, k: Integer;
1249 PanelID: DWORD;
1250 AddTextures: TAddTextureArray;
1251 texture: TTextureRec_1;
1252 TriggersTable: Array of record
1253 TexturePanel: Integer;
1254 LiftPanel: Integer;
1255 DoorPanel: Integer;
1256 ShotPanel: Integer;
1257 end;
1258 FileName, mapResName, s, TexName: String;
1259 Data: Pointer;
1260 Len: Integer;
1261 ok, isAnim, trigRef: Boolean;
1262 CurTex, ntn: Integer;
1264 begin
1265 gMapGrid.Free();
1266 gMapGrid := nil;
1267 mapTree.Free();
1268 mapTree := nil;
1270 Result := False;
1271 gMapInfo.Map := Res;
1272 TriggersTable := nil;
1273 FillChar(texture, SizeOf(texture), 0);
1275 sfsGCDisable(); // temporary disable removing of temporary volumes
1276 try
1277 // Çàãðóçêà WAD:
1278 FileName := g_ExtractWadName(Res);
1279 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1280 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1282 WAD := TWADFile.Create();
1283 if not WAD.ReadFile(FileName) then
1284 begin
1285 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1286 WAD.Free();
1287 Exit;
1288 end;
1289 //k8: why loader ignores path here?
1290 mapResName := g_ExtractFileName(Res);
1291 if not WAD.GetMapResource(mapResName, Data, Len) then
1292 begin
1293 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1294 WAD.Free();
1295 Exit;
1296 end;
1298 WAD.Free();
1300 // Çàãðóçêà êàðòû:
1301 e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
1302 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1303 MapReader := TMapReader_1.Create();
1305 if not MapReader.LoadMap(Data) then
1306 begin
1307 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1308 FreeMem(Data);
1309 MapReader.Free();
1310 Exit;
1311 end;
1313 FreeMem(Data);
1314 generateExternalResourcesList(MapReader);
1315 // Çàãðóçêà òåêñòóð:
1316 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1317 _textures := MapReader.GetTextures();
1318 _texnummap := nil;
1320 // Äîáàâëåíèå òåêñòóð â Textures[]:
1321 if _textures <> nil then
1322 begin
1323 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1324 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
1325 SetLength(_texnummap, length(_textures));
1327 for a := 0 to High(_textures) do
1328 begin
1329 SetLength(s, 64);
1330 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
1331 for b := 1 to Length(s) do
1332 if s[b] = #0 then
1333 begin
1334 SetLength(s, b-1);
1335 Break;
1336 end;
1337 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1338 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1339 // Àíèìèðîâàííàÿ òåêñòóðà:
1340 if ByteBool(_textures[a].Anim) then
1341 begin
1342 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1343 if ntn < 0 then
1344 begin
1345 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1346 ntn := CreateNullTexture(_textures[a].Resource);
1347 end;
1348 end
1349 else // Îáû÷íàÿ òåêñòóðà:
1350 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1351 if ntn < 0 then
1352 begin
1353 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1354 ntn := CreateNullTexture(_textures[a].Resource);
1355 end;
1357 _texnummap[a] := ntn; // fix texture number
1358 g_Game_StepLoading();
1359 end;
1360 end;
1362 // Çàãðóçêà òðèããåðîâ:
1363 gTriggerClientID := 0;
1364 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1365 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1366 triggers := MapReader.GetTriggers();
1368 // Çàãðóçêà ïàíåëåé:
1369 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1370 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1371 panels := MapReader.GetPanels();
1373 // check texture numbers for panels
1374 for a := 0 to High(panels) do
1375 begin
1376 if panels[a].TextureNum > High(_textures) then
1377 begin
1378 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1379 result := false;
1380 exit;
1381 end;
1382 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1383 end;
1385 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1386 if triggers <> nil then
1387 begin
1388 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1389 SetLength(TriggersTable, Length(triggers));
1390 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1392 for a := 0 to High(TriggersTable) do
1393 begin
1394 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1395 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1396 // Ëèôòû:
1397 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1398 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1399 else
1400 TriggersTable[a].LiftPanel := -1;
1401 // Äâåðè:
1402 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1403 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1404 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1405 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1406 else
1407 TriggersTable[a].DoorPanel := -1;
1408 // Òóðåëü:
1409 if triggers[a].TriggerType = TRIGGER_SHOT then
1410 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1411 else
1412 TriggersTable[a].ShotPanel := -1;
1414 g_Game_StepLoading();
1415 end;
1416 end;
1418 // Ñîçäàåì ïàíåëè:
1419 if panels <> nil then
1420 begin
1421 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1422 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1424 for a := 0 to High(panels) do
1425 begin
1426 SetLength(AddTextures, 0);
1427 trigRef := False;
1428 CurTex := -1;
1429 if _textures <> nil then
1430 begin
1431 texture := _textures[panels[a].TextureNum];
1432 ok := True;
1433 end
1434 else
1435 ok := False;
1437 if ok then
1438 begin
1439 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1440 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1441 ok := False;
1442 if (TriggersTable <> nil) and (_textures <> nil) then
1443 for b := 0 to High(TriggersTable) do
1444 if (TriggersTable[b].TexturePanel = a)
1445 or (TriggersTable[b].ShotPanel = a) then
1446 begin
1447 trigRef := True;
1448 ok := True;
1449 Break;
1450 end;
1451 end;
1453 if ok then
1454 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1455 SetLength(s, 64);
1456 CopyMemory(@s[1], @texture.Resource[0], 64);
1457 // Èçìåðÿåì äëèíó:
1458 Len := Length(s);
1459 for c := Len downto 1 do
1460 if s[c] <> #0 then
1461 begin
1462 Len := c;
1463 Break;
1464 end;
1465 SetLength(s, Len);
1467 // Ñïåö-òåêñòóðû çàïðåùåíû:
1468 if g_Map_IsSpecialTexture(s) then
1469 ok := False
1470 else
1471 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1472 ok := g_Texture_NumNameFindStart(s);
1474 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1475 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1476 if ok then
1477 begin
1478 k := NNF_NAME_BEFORE;
1479 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1480 while ok or (k = NNF_NAME_BEFORE) or
1481 (k = NNF_NAME_EQUALS) do
1482 begin
1483 k := g_Texture_NumNameFindNext(TexName);
1485 if (k = NNF_NAME_BEFORE) or
1486 (k = NNF_NAME_AFTER) then
1487 begin
1488 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1489 if ByteBool(texture.Anim) then
1490 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1491 isAnim := True;
1492 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1493 if not ok then
1494 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1495 isAnim := False;
1496 ok := CreateTexture(TexName, FileName, False) >= 0;
1497 end;
1498 end
1499 else
1500 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1501 isAnim := False;
1502 ok := CreateTexture(TexName, FileName, False) >= 0;
1503 if not ok then
1504 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1505 isAnim := True;
1506 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1507 end;
1508 end;
1510 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1511 if ok then
1512 begin
1513 for c := 0 to High(Textures) do
1514 if Textures[c].TextureName = TexName then
1515 begin
1516 SetLength(AddTextures, Length(AddTextures)+1);
1517 AddTextures[High(AddTextures)].Texture := c;
1518 AddTextures[High(AddTextures)].Anim := isAnim;
1519 Break;
1520 end;
1521 end;
1522 end
1523 else
1524 if k = NNF_NAME_EQUALS then
1525 begin
1526 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1527 SetLength(AddTextures, Length(AddTextures)+1);
1528 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1529 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1530 CurTex := High(AddTextures);
1531 ok := True;
1532 end
1533 else // NNF_NO_NAME
1534 ok := False;
1535 end; // while ok...
1537 ok := True;
1538 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1539 end; // if ok - ññûëàþòñÿ òðèããåðû
1541 if not ok then
1542 begin
1543 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1544 SetLength(AddTextures, 1);
1545 AddTextures[0].Texture := panels[a].TextureNum;
1546 AddTextures[0].Anim := ByteBool(texture.Anim);
1547 CurTex := 0;
1548 end;
1550 //e_WriteLog(Format('panel #%d: TextureNum=%d; ht=%d; ht1=%d; atl=%d', [a, panels[a].TextureNum, High(_textures), High(Textures), High(AddTextures)]), MSG_NOTIFY);
1552 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1553 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1555 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1556 if TriggersTable <> nil then
1557 for b := 0 to High(TriggersTable) do
1558 begin
1559 // Òðèããåð äâåðè/ëèôòà:
1560 if (TriggersTable[b].LiftPanel = a) or
1561 (TriggersTable[b].DoorPanel = a) then
1562 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1563 // Òðèããåð ñìåíû òåêñòóðû:
1564 if TriggersTable[b].TexturePanel = a then
1565 triggers[b].TexturePanel := PanelID;
1566 // Òðèããåð "Òóðåëü":
1567 if TriggersTable[b].ShotPanel = a then
1568 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1569 end;
1571 g_Game_StepLoading();
1572 end;
1573 end;
1575 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1576 if (triggers <> nil) and not gLoadGameMode then
1577 begin
1578 e_WriteLog(' Creating triggers...', MSG_NOTIFY);
1579 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1580 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1581 for a := 0 to High(triggers) do
1582 begin
1583 if triggers[a].TexturePanel <> -1 then
1584 b := panels[TriggersTable[a].TexturePanel].PanelType
1585 else
1586 b := 0;
1587 if (triggers[a].TriggerType = TRIGGER_SHOT) and
1588 (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1589 c := panels[TriggersTable[a].ShotPanel].PanelType
1590 else
1591 c := 0;
1592 CreateTrigger(triggers[a], b, c);
1593 end;
1594 end;
1596 // Çàãðóçêà ïðåäìåòîâ:
1597 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1598 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1599 items := MapReader.GetItems();
1601 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1602 if (items <> nil) and not gLoadGameMode then
1603 begin
1604 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1605 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1606 for a := 0 to High(items) do
1607 CreateItem(Items[a]);
1608 end;
1610 // Çàãðóçêà îáëàñòåé:
1611 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1612 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1613 areas := MapReader.GetAreas();
1615 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1616 if areas <> nil then
1617 begin
1618 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1619 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1620 for a := 0 to High(areas) do
1621 CreateArea(areas[a]);
1622 end;
1624 // Çàãðóçêà ìîíñòðîâ:
1625 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1626 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1627 monsters := MapReader.GetMonsters();
1629 gTotalMonsters := 0;
1631 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1632 if (monsters <> nil) and not gLoadGameMode then
1633 begin
1634 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1635 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1636 for a := 0 to High(monsters) do
1637 CreateMonster(monsters[a]);
1638 end;
1640 // Çàãðóçêà îïèñàíèÿ êàðòû:
1641 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1642 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1643 Header := MapReader.GetMapHeader();
1645 MapReader.Free();
1647 with gMapInfo do
1648 begin
1649 Name := Header.MapName;
1650 Description := Header.MapDescription;
1651 Author := Header.MapAuthor;
1652 MusicName := Header.MusicName;
1653 SkyName := Header.SkyName;
1654 Height := Header.Height;
1655 Width := Header.Width;
1656 end;
1658 // Çàãðóçêà íåáà:
1659 if gMapInfo.SkyName <> '' then
1660 begin
1661 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1662 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1663 FileName := g_ExtractWadName(gMapInfo.SkyName);
1665 if FileName <> '' then
1666 FileName := GameDir+'/wads/'+FileName
1667 else
1668 begin
1669 FileName := g_ExtractWadName(Res);
1670 end;
1672 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1673 if g_Texture_CreateWAD(BackID, s) then
1674 begin
1675 g_Game_SetupScreenSize();
1676 end
1677 else
1678 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1679 end;
1681 // Çàãðóçêà ìóçûêè:
1682 ok := False;
1683 if gMapInfo.MusicName <> '' then
1684 begin
1685 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1686 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1687 FileName := g_ExtractWadName(gMapInfo.MusicName);
1689 if FileName <> '' then
1690 FileName := GameDir+'/wads/'+FileName
1691 else
1692 begin
1693 FileName := g_ExtractWadName(Res);
1694 end;
1696 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1697 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1698 ok := True
1699 else
1700 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1701 end;
1703 // Îñòàëüíûå óñòàíâêè:
1704 CreateDoorMap();
1705 CreateLiftMap();
1707 g_Items_Init();
1708 g_Weapon_Init();
1709 g_Monsters_Init();
1711 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1712 if not gLoadGameMode then
1713 g_GFX_Init();
1715 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1716 _textures := nil;
1717 panels := nil;
1718 items := nil;
1719 areas := nil;
1720 triggers := nil;
1721 TriggersTable := nil;
1722 AddTextures := nil;
1724 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
1725 if ok and (not gLoadGameMode) then
1726 begin
1727 gMusic.SetByName(gMapInfo.MusicName);
1728 gMusic.Play();
1729 end
1730 else
1731 gMusic.SetByName('');
1732 finally
1733 sfsGCEnable(); // enable releasing unused volumes
1734 end;
1736 e_WriteLog('Creating map grid', MSG_NOTIFY);
1737 mapCreateGrid();
1739 e_WriteLog('Done loading map.', MSG_NOTIFY);
1740 Result := True;
1741 end;
1743 function g_Map_GetMapInfo(Res: String): TMapInfo;
1744 var
1745 WAD: TWADFile;
1746 MapReader: TMapReader_1;
1747 Header: TMapHeaderRec_1;
1748 FileName: String;
1749 Data: Pointer;
1750 Len: Integer;
1751 begin
1752 FillChar(Result, SizeOf(Result), 0);
1753 FileName := g_ExtractWadName(Res);
1755 WAD := TWADFile.Create();
1756 if not WAD.ReadFile(FileName) then
1757 begin
1758 WAD.Free();
1759 Exit;
1760 end;
1762 //k8: it ignores path again
1763 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
1764 begin
1765 WAD.Free();
1766 Exit;
1767 end;
1769 WAD.Free();
1771 MapReader := TMapReader_1.Create();
1773 if not MapReader.LoadMap(Data) then
1774 begin
1775 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
1776 ZeroMemory(@Header, SizeOf(Header));
1777 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
1778 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
1779 end
1780 else
1781 begin
1782 Header := MapReader.GetMapHeader();
1783 Result.Name := Header.MapName;
1784 Result.Description := Header.MapDescription;
1785 end;
1787 FreeMem(Data);
1788 MapReader.Free();
1790 Result.Map := Res;
1791 Result.Author := Header.MapAuthor;
1792 Result.Height := Header.Height;
1793 Result.Width := Header.Width;
1794 end;
1796 function g_Map_GetMapsList(WADName: string): SArray;
1797 var
1798 WAD: TWADFile;
1799 a: Integer;
1800 ResList: SArray;
1801 begin
1802 Result := nil;
1803 WAD := TWADFile.Create();
1804 if not WAD.ReadFile(WADName) then
1805 begin
1806 WAD.Free();
1807 Exit;
1808 end;
1809 ResList := WAD.GetMapResources();
1810 if ResList <> nil then
1811 begin
1812 for a := 0 to High(ResList) do
1813 begin
1814 SetLength(Result, Length(Result)+1);
1815 Result[High(Result)] := ResList[a];
1816 end;
1817 end;
1818 WAD.Free();
1819 end;
1821 function g_Map_Exist(Res: string): Boolean;
1822 var
1823 WAD: TWADFile;
1824 FileName, mnn: string;
1825 ResList: SArray;
1826 a: Integer;
1827 begin
1828 Result := False;
1830 FileName := addWadExtension(g_ExtractWadName(Res));
1832 WAD := TWADFile.Create;
1833 if not WAD.ReadFile(FileName) then
1834 begin
1835 WAD.Free();
1836 Exit;
1837 end;
1839 ResList := WAD.GetMapResources();
1840 WAD.Free();
1842 mnn := g_ExtractFileName(Res);
1843 if ResList <> nil then
1844 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
1845 begin
1846 Result := True;
1847 Exit;
1848 end;
1849 end;
1851 procedure g_Map_Free();
1852 var
1853 a: Integer;
1855 procedure FreePanelArray(var panels: TPanelArray);
1856 var
1857 i: Integer;
1859 begin
1860 if panels <> nil then
1861 begin
1862 for i := 0 to High(panels) do
1863 panels[i].Free();
1864 panels := nil;
1865 end;
1866 end;
1868 begin
1869 g_GFX_Free();
1870 g_Weapon_Free();
1871 g_Items_Free();
1872 g_Triggers_Free();
1873 g_Monsters_Free();
1875 RespawnPoints := nil;
1876 if FlagPoints[FLAG_RED] <> nil then
1877 begin
1878 Dispose(FlagPoints[FLAG_RED]);
1879 FlagPoints[FLAG_RED] := nil;
1880 end;
1881 if FlagPoints[FLAG_BLUE] <> nil then
1882 begin
1883 Dispose(FlagPoints[FLAG_BLUE]);
1884 FlagPoints[FLAG_BLUE] := nil;
1885 end;
1886 //DOMFlagPoints := nil;
1888 //gDOMFlags := nil;
1890 if Textures <> nil then
1891 begin
1892 for a := 0 to High(Textures) do
1893 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
1894 if Textures[a].Anim then
1895 g_Frames_DeleteByID(Textures[a].FramesID)
1896 else
1897 if Textures[a].TextureID <> TEXTURE_NONE then
1898 e_DeleteTexture(Textures[a].TextureID);
1900 Textures := nil;
1901 end;
1903 FreePanelArray(gWalls);
1904 FreePanelArray(gRenderBackgrounds);
1905 FreePanelArray(gRenderForegrounds);
1906 FreePanelArray(gWater);
1907 FreePanelArray(gAcid1);
1908 FreePanelArray(gAcid2);
1909 FreePanelArray(gSteps);
1910 FreePanelArray(gLifts);
1911 FreePanelArray(gBlockMon);
1913 if BackID <> DWORD(-1) then
1914 begin
1915 gBackSize.X := 0;
1916 gBackSize.Y := 0;
1917 e_DeleteTexture(BackID);
1918 BackID := DWORD(-1);
1919 end;
1921 g_Game_StopAllSounds(False);
1922 gMusic.FreeSound();
1923 g_Sound_Delete(gMapInfo.MusicName);
1925 gMapInfo.Name := '';
1926 gMapInfo.Description := '';
1927 gMapInfo.MusicName := '';
1928 gMapInfo.Height := 0;
1929 gMapInfo.Width := 0;
1931 gDoorMap := nil;
1932 gLiftMap := nil;
1934 PanelByID := nil;
1935 end;
1937 procedure g_Map_Update();
1938 var
1939 a, d, j: Integer;
1940 m: Word;
1941 s: String;
1943 procedure UpdatePanelArray(var panels: TPanelArray);
1944 var
1945 i: Integer;
1947 begin
1948 if panels <> nil then
1949 for i := 0 to High(panels) do
1950 panels[i].Update();
1951 end;
1953 begin
1954 UpdatePanelArray(gWalls);
1955 UpdatePanelArray(gRenderBackgrounds);
1956 UpdatePanelArray(gRenderForegrounds);
1957 UpdatePanelArray(gWater);
1958 UpdatePanelArray(gAcid1);
1959 UpdatePanelArray(gAcid2);
1960 UpdatePanelArray(gSteps);
1962 if gGameSettings.GameMode = GM_CTF then
1963 begin
1964 for a := FLAG_RED to FLAG_BLUE do
1965 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
1966 with gFlags[a] do
1967 begin
1968 if gFlags[a].Animation <> nil then
1969 gFlags[a].Animation.Update();
1971 m := g_Obj_Move(@Obj, True, True);
1973 if gTime mod (GAME_TICK*2) <> 0 then
1974 Continue;
1976 // Ñîïðîòèâëåíèå âîçäóõà:
1977 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
1979 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
1980 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
1981 begin
1982 g_Map_ResetFlag(a);
1983 gFlags[a].CaptureTime := 0;
1984 if a = FLAG_RED then
1985 s := _lc[I_PLAYER_FLAG_RED]
1986 else
1987 s := _lc[I_PLAYER_FLAG_BLUE];
1988 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1990 if g_Game_IsNet then
1991 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
1992 Continue;
1993 end;
1995 if Count > 0 then
1996 Count := Count - 1;
1998 // Èãðîê áåðåò ôëàã:
1999 if gPlayers <> nil then
2000 begin
2001 j := Random(Length(gPlayers)) - 1;
2003 for d := 0 to High(gPlayers) do
2004 begin
2005 Inc(j);
2006 if j > High(gPlayers) then
2007 j := 0;
2009 if gPlayers[j] <> nil then
2010 if gPlayers[j].Live and
2011 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2012 begin
2013 if gPlayers[j].GetFlag(a) then
2014 Break;
2015 end;
2016 end;
2017 end;
2018 end;
2019 end;
2020 end;
2023 // old algo
2024 procedure g_Map_DrawPanels (PanelType: Word);
2026 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2027 var
2028 idx: Integer;
2029 begin
2030 if (panels <> nil) then
2031 begin
2032 // alas, no visible set
2033 for idx := 0 to High(panels) do
2034 begin
2035 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
2036 end;
2037 end;
2038 end;
2040 begin
2041 case PanelType of
2042 PANEL_WALL: DrawPanels(gWalls);
2043 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2044 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2045 PANEL_FORE: DrawPanels(gRenderForegrounds);
2046 PANEL_WATER: DrawPanels(gWater);
2047 PANEL_ACID1: DrawPanels(gAcid1);
2048 PANEL_ACID2: DrawPanels(gAcid2);
2049 PANEL_STEP: DrawPanels(gSteps);
2050 end;
2051 end;
2054 // new algo
2055 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2056 function checker (pan: TPanel; tag: Integer): Boolean;
2057 begin
2058 result := false; // don't stop, ever
2059 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2060 gDrawPanelList.insert(pan);
2061 end;
2063 begin
2064 dplClear();
2065 //tagmask := panelTypeToTag(PanelType);
2067 if gdbg_map_use_tree_draw then
2068 begin
2069 mapTree.aabbQuery(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore));
2070 end
2071 else
2072 begin
2073 gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore));
2074 end;
2075 // list will be rendered in `g_game.DrawPlayer()`
2076 end;
2079 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2080 function checker (pan: TPanel; tag: Integer): Boolean;
2081 begin
2082 result := false; // don't stop, ever
2083 pan.DrawShadowVolume(lightX, lightY, radius);
2084 end;
2086 begin
2087 if gdbg_map_use_tree_draw then
2088 begin
2089 mapTree.aabbQuery(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2090 end
2091 else
2092 begin
2093 gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2094 end;
2095 end;
2098 procedure g_Map_DrawBack(dx, dy: Integer);
2099 begin
2100 if gDrawBackGround and (BackID <> DWORD(-1)) then
2101 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2102 else
2103 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2104 end;
2106 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2107 PanelType: Word; b1x3: Boolean=false): Boolean;
2108 var
2109 a, h: Integer;
2110 begin
2111 Result := False;
2113 if WordBool(PanelType and PANEL_WALL) then
2114 if gWalls <> nil then
2115 begin
2116 h := High(gWalls);
2118 for a := 0 to h do
2119 if gWalls[a].Enabled and
2120 g_Collide(X, Y, Width, Height,
2121 gWalls[a].X, gWalls[a].Y,
2122 gWalls[a].Width, gWalls[a].Height) then
2123 begin
2124 Result := True;
2125 Exit;
2126 end;
2127 end;
2129 if WordBool(PanelType and PANEL_WATER) then
2130 if gWater <> nil then
2131 begin
2132 h := High(gWater);
2134 for a := 0 to h do
2135 if g_Collide(X, Y, Width, Height,
2136 gWater[a].X, gWater[a].Y,
2137 gWater[a].Width, gWater[a].Height) then
2138 begin
2139 Result := True;
2140 Exit;
2141 end;
2142 end;
2144 if WordBool(PanelType and PANEL_ACID1) then
2145 if gAcid1 <> nil then
2146 begin
2147 h := High(gAcid1);
2149 for a := 0 to h do
2150 if g_Collide(X, Y, Width, Height,
2151 gAcid1[a].X, gAcid1[a].Y,
2152 gAcid1[a].Width, gAcid1[a].Height) then
2153 begin
2154 Result := True;
2155 Exit;
2156 end;
2157 end;
2159 if WordBool(PanelType and PANEL_ACID2) then
2160 if gAcid2 <> nil then
2161 begin
2162 h := High(gAcid2);
2164 for a := 0 to h do
2165 if g_Collide(X, Y, Width, Height,
2166 gAcid2[a].X, gAcid2[a].Y,
2167 gAcid2[a].Width, gAcid2[a].Height) then
2168 begin
2169 Result := True;
2170 Exit;
2171 end;
2172 end;
2174 if WordBool(PanelType and PANEL_STEP) then
2175 if gSteps <> nil then
2176 begin
2177 h := High(gSteps);
2179 for a := 0 to h do
2180 if g_Collide(X, Y, Width, Height,
2181 gSteps[a].X, gSteps[a].Y,
2182 gSteps[a].Width, gSteps[a].Height) then
2183 begin
2184 Result := True;
2185 Exit;
2186 end;
2187 end;
2189 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2190 if gLifts <> nil then
2191 begin
2192 h := High(gLifts);
2194 for a := 0 to h do
2195 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2196 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2197 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2198 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2199 g_Collide(X, Y, Width, Height,
2200 gLifts[a].X, gLifts[a].Y,
2201 gLifts[a].Width, gLifts[a].Height) then
2202 begin
2203 Result := True;
2204 Exit;
2205 end;
2206 end;
2208 if WordBool(PanelType and PANEL_BLOCKMON) then
2209 if gBlockMon <> nil then
2210 begin
2211 h := High(gBlockMon);
2213 for a := 0 to h do
2214 if ( (not b1x3) or
2215 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2216 g_Collide(X, Y, Width, Height,
2217 gBlockMon[a].X, gBlockMon[a].Y,
2218 gBlockMon[a].Width, gBlockMon[a].Height) then
2219 begin
2220 Result := True;
2221 Exit;
2222 end;
2223 end;
2224 end;
2226 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2227 var
2228 texid: DWORD;
2230 function checkPanels (constref panels: TPanelArray): Boolean;
2231 var
2232 a: Integer;
2233 begin
2234 result := false;
2235 if panels = nil then exit;
2236 for a := 0 to High(panels) do
2237 begin
2238 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2239 begin
2240 result := true;
2241 texid := panels[a].GetTextureID();
2242 exit;
2243 end;
2244 end;
2245 end;
2247 begin
2248 texid := TEXTURE_NONE;
2249 result := texid;
2250 if not checkPanels(gWater) then
2251 if not checkPanels(gAcid1) then
2252 if not checkPanels(gAcid2) then exit;
2253 result := texid;
2254 end;
2257 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2258 function checker (pan: TPanel; tag: Integer): Boolean;
2259 begin
2260 result := false; // don't stop, ever
2262 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2263 begin
2264 if not pan.Enabled then exit;
2265 end;
2267 if ((tag and GridTagLift) <> 0) then
2268 begin
2269 result :=
2270 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2271 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2272 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2273 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) and
2274 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2275 exit;
2276 end;
2278 if ((tag and GridTagBlockMon) <> 0) then
2279 begin
2280 result := ((not b1x3) or (pan.Width+pan.Height >= 64)) and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2281 exit;
2282 end;
2284 // other shit
2285 result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2286 end;
2288 var
2289 tagmask: Integer = 0;
2290 begin
2291 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2292 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2293 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2294 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2295 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2296 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2297 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2299 if (tagmask = 0) then begin result := false; exit; end; // just in case
2301 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2302 if gdbg_map_use_accel_coldet then
2303 begin
2304 if gdbg_map_use_tree_coldet then
2305 begin
2306 //e_WriteLog(Format('coldet query: x=%d; y=%d; w=%d; h=%d', [X, Y, Width, Height]), MSG_NOTIFY);
2307 result := (mapTree.aabbQuery(X, Y, Width, Height, checker, tagmask) <> nil);
2308 if (gdbg_map_dump_coldet_tree_queries) and (mapTree.nodesVisited <> 0) then
2309 begin
2310 //e_WriteLog(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]), MSG_NOTIFY);
2311 g_Console_Add(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]));
2312 end;
2313 end
2314 else
2315 begin
2316 result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask);
2317 end;
2318 end
2319 else
2320 begin
2321 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2322 end;
2323 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2324 end;
2327 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2328 var
2329 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2330 texid: DWORD;
2332 // slightly different from the old code, but meh...
2333 function checker (pan: TPanel; tag: Integer): Boolean;
2334 begin
2335 result := false; // don't stop, ever
2336 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2337 // check priorities
2338 case cctype of
2339 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2340 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2341 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2342 end;
2343 // collision?
2344 if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2345 // yeah
2346 texid := pan.GetTextureID();
2347 // water? water has the highest priority, so stop right here
2348 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2349 // acid2?
2350 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2351 // acid1?
2352 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2353 end;
2355 begin
2356 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2357 if gdbg_map_use_accel_coldet then
2358 begin
2359 texid := TEXTURE_NONE;
2360 if gdbg_map_use_tree_coldet then
2361 begin
2362 mapTree.aabbQuery(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2363 end
2364 else
2365 begin
2366 gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2367 end;
2368 result := texid;
2369 end
2370 else
2371 begin
2372 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2373 end;
2374 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2375 end;
2377 procedure g_Map_EnableWall(ID: DWORD);
2378 begin
2379 with gWalls[ID] do
2380 begin
2381 Enabled := True;
2382 //g_Mark(X, Y, Width, Height, MARK_DOOR, True);
2384 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2385 end;
2386 end;
2388 procedure g_Map_DisableWall(ID: DWORD);
2389 begin
2390 with gWalls[ID] do
2391 begin
2392 Enabled := False;
2393 //g_Mark(X, Y, Width, Height, MARK_DOOR, False);
2395 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2396 end;
2397 end;
2399 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2400 var
2401 tp: TPanel;
2402 begin
2403 case PanelType of
2404 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2405 tp := gWalls[ID];
2406 PANEL_FORE:
2407 tp := gRenderForegrounds[ID];
2408 PANEL_BACK:
2409 tp := gRenderBackgrounds[ID];
2410 PANEL_WATER:
2411 tp := gWater[ID];
2412 PANEL_ACID1:
2413 tp := gAcid1[ID];
2414 PANEL_ACID2:
2415 tp := gAcid2[ID];
2416 PANEL_STEP:
2417 tp := gSteps[ID];
2418 else
2419 Exit;
2420 end;
2422 tp.NextTexture(AnimLoop);
2423 if g_Game_IsServer and g_Game_IsNet then
2424 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2425 end;
2427 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2428 begin
2429 if gLifts[ID].LiftType = t then
2430 Exit;
2432 with gLifts[ID] do
2433 begin
2434 LiftType := t;
2437 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2439 if LiftType = 0 then
2440 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2441 else if LiftType = 1 then
2442 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2443 else if LiftType = 2 then
2444 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2445 else if LiftType = 3 then
2446 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2449 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2450 end;
2451 end;
2453 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2454 var
2455 a: Integer;
2456 PointsArray: Array of TRespawnPoint;
2457 begin
2458 Result := False;
2459 SetLength(PointsArray, 0);
2461 if RespawnPoints = nil then
2462 Exit;
2464 for a := 0 to High(RespawnPoints) do
2465 if RespawnPoints[a].PointType = PointType then
2466 begin
2467 SetLength(PointsArray, Length(PointsArray)+1);
2468 PointsArray[High(PointsArray)] := RespawnPoints[a];
2469 end;
2471 if PointsArray = nil then
2472 Exit;
2474 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2475 Result := True;
2476 end;
2478 function g_Map_GetPointCount(PointType: Byte): Word;
2479 var
2480 a: Integer;
2481 begin
2482 Result := 0;
2484 if RespawnPoints = nil then
2485 Exit;
2487 for a := 0 to High(RespawnPoints) do
2488 if RespawnPoints[a].PointType = PointType then
2489 Result := Result + 1;
2490 end;
2492 function g_Map_HaveFlagPoints(): Boolean;
2493 begin
2494 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2495 end;
2497 procedure g_Map_ResetFlag(Flag: Byte);
2498 begin
2499 with gFlags[Flag] do
2500 begin
2501 Obj.X := -1000;
2502 Obj.Y := -1000;
2503 Obj.Vel.X := 0;
2504 Obj.Vel.Y := 0;
2505 Direction := D_LEFT;
2506 State := FLAG_STATE_NONE;
2507 if FlagPoints[Flag] <> nil then
2508 begin
2509 Obj.X := FlagPoints[Flag]^.X;
2510 Obj.Y := FlagPoints[Flag]^.Y;
2511 Direction := FlagPoints[Flag]^.Direction;
2512 State := FLAG_STATE_NORMAL;
2513 end;
2514 Count := -1;
2515 end;
2516 end;
2518 procedure g_Map_DrawFlags();
2519 var
2520 i, dx: Integer;
2521 Mirror: TMirrorType;
2522 begin
2523 if gGameSettings.GameMode <> GM_CTF then
2524 Exit;
2526 for i := FLAG_RED to FLAG_BLUE do
2527 with gFlags[i] do
2528 if State <> FLAG_STATE_CAPTURED then
2529 begin
2530 if State = FLAG_STATE_NONE then
2531 continue;
2533 if Direction = D_LEFT then
2534 begin
2535 Mirror := M_HORIZONTAL;
2536 dx := -1;
2537 end
2538 else
2539 begin
2540 Mirror := M_NONE;
2541 dx := 1;
2542 end;
2544 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2546 if g_debug_Frames then
2547 begin
2548 e_DrawQuad(Obj.X+Obj.Rect.X,
2549 Obj.Y+Obj.Rect.Y,
2550 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2551 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2552 0, 255, 0);
2553 end;
2554 end;
2555 end;
2557 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2558 var
2559 dw: DWORD;
2560 b: Byte;
2561 str: String;
2562 boo: Boolean;
2564 procedure SavePanelArray(var panels: TPanelArray);
2565 var
2566 PAMem: TBinMemoryWriter;
2567 i: Integer;
2568 begin
2569 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2570 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2572 i := 0;
2573 while i < Length(panels) do
2574 begin
2575 if panels[i].SaveIt then
2576 begin
2577 // ID ïàíåëè:
2578 PAMem.WriteInt(i);
2579 // Ñîõðàíÿåì ïàíåëü:
2580 panels[i].SaveState(PAMem);
2581 end;
2582 Inc(i);
2583 end;
2585 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2586 PAMem.SaveToMemory(Mem);
2587 PAMem.Free();
2588 end;
2590 procedure SaveFlag(flag: PFlag);
2591 begin
2592 // Ñèãíàòóðà ôëàãà:
2593 dw := FLAG_SIGNATURE; // 'FLAG'
2594 Mem.WriteDWORD(dw);
2595 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2596 Mem.WriteByte(flag^.RespawnType);
2597 // Ñîñòîÿíèå ôëàãà:
2598 Mem.WriteByte(flag^.State);
2599 // Íàïðàâëåíèå ôëàãà:
2600 if flag^.Direction = D_LEFT then
2601 b := 1
2602 else // D_RIGHT
2603 b := 2;
2604 Mem.WriteByte(b);
2605 // Îáúåêò ôëàãà:
2606 Obj_SaveState(@flag^.Obj, Mem);
2607 end;
2609 begin
2610 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2612 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2613 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2614 SavePanelArray(gWalls);
2615 // Ñîõðàíÿåì ïàíåëè ôîíà:
2616 SavePanelArray(gRenderBackgrounds);
2617 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2618 SavePanelArray(gRenderForegrounds);
2619 // Ñîõðàíÿåì ïàíåëè âîäû:
2620 SavePanelArray(gWater);
2621 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2622 SavePanelArray(gAcid1);
2623 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2624 SavePanelArray(gAcid2);
2625 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2626 SavePanelArray(gSteps);
2627 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2628 SavePanelArray(gLifts);
2629 ///// /////
2631 ///// Ñîõðàíÿåì ìóçûêó: /////
2632 // Ñèãíàòóðà ìóçûêè:
2633 dw := MUSIC_SIGNATURE; // 'MUSI'
2634 Mem.WriteDWORD(dw);
2635 // Íàçâàíèå ìóçûêè:
2636 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2637 if gMusic.NoMusic then
2638 str := ''
2639 else
2640 str := gMusic.Name;
2641 Mem.WriteString(str, 64);
2642 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2643 dw := gMusic.GetPosition();
2644 Mem.WriteDWORD(dw);
2645 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2646 boo := gMusic.SpecPause;
2647 Mem.WriteBoolean(boo);
2648 ///// /////
2650 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2651 Mem.WriteInt(gTotalMonsters);
2652 ///// /////
2654 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2655 if gGameSettings.GameMode = GM_CTF then
2656 begin
2657 // Ôëàã Êðàñíîé êîìàíäû:
2658 SaveFlag(@gFlags[FLAG_RED]);
2659 // Ôëàã Ñèíåé êîìàíäû:
2660 SaveFlag(@gFlags[FLAG_BLUE]);
2661 end;
2662 ///// /////
2664 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2665 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2666 begin
2667 // Î÷êè Êðàñíîé êîìàíäû:
2668 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2669 // Î÷êè Ñèíåé êîìàíäû:
2670 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2671 end;
2672 ///// /////
2673 end;
2675 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2676 var
2677 dw: DWORD;
2678 b: Byte;
2679 str: String;
2680 boo: Boolean;
2682 procedure LoadPanelArray(var panels: TPanelArray);
2683 var
2684 PAMem: TBinMemoryReader;
2685 i, id: Integer;
2686 begin
2687 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
2688 PAMem := TBinMemoryReader.Create();
2689 PAMem.LoadFromMemory(Mem);
2691 for i := 0 to Length(panels)-1 do
2692 if panels[i].SaveIt then
2693 begin
2694 // ID ïàíåëè:
2695 PAMem.ReadInt(id);
2696 if id <> i then
2697 begin
2698 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
2699 end;
2700 // Çàãðóæàåì ïàíåëü:
2701 panels[i].LoadState(PAMem);
2702 end;
2704 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
2705 PAMem.Free();
2706 end;
2708 procedure LoadFlag(flag: PFlag);
2709 begin
2710 // Ñèãíàòóðà ôëàãà:
2711 Mem.ReadDWORD(dw);
2712 if dw <> FLAG_SIGNATURE then // 'FLAG'
2713 begin
2714 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
2715 end;
2716 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2717 Mem.ReadByte(flag^.RespawnType);
2718 // Ñîñòîÿíèå ôëàãà:
2719 Mem.ReadByte(flag^.State);
2720 // Íàïðàâëåíèå ôëàãà:
2721 Mem.ReadByte(b);
2722 if b = 1 then
2723 flag^.Direction := D_LEFT
2724 else // b = 2
2725 flag^.Direction := D_RIGHT;
2726 // Îáúåêò ôëàãà:
2727 Obj_LoadState(@flag^.Obj, Mem);
2728 end;
2730 begin
2731 if Mem = nil then
2732 Exit;
2734 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
2735 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
2736 LoadPanelArray(gWalls);
2737 // Çàãðóæàåì ïàíåëè ôîíà:
2738 LoadPanelArray(gRenderBackgrounds);
2739 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
2740 LoadPanelArray(gRenderForegrounds);
2741 // Çàãðóæàåì ïàíåëè âîäû:
2742 LoadPanelArray(gWater);
2743 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
2744 LoadPanelArray(gAcid1);
2745 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
2746 LoadPanelArray(gAcid2);
2747 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
2748 LoadPanelArray(gSteps);
2749 // Çàãðóæàåì ïàíåëè ëèôòîâ:
2750 LoadPanelArray(gLifts);
2751 ///// /////
2753 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
2754 g_GFX_Init();
2755 mapCreateGrid();
2757 ///// Çàãðóæàåì ìóçûêó: /////
2758 // Ñèãíàòóðà ìóçûêè:
2759 Mem.ReadDWORD(dw);
2760 if dw <> MUSIC_SIGNATURE then // 'MUSI'
2761 begin
2762 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
2763 end;
2764 // Íàçâàíèå ìóçûêè:
2765 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
2766 Mem.ReadString(str);
2767 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2768 Mem.ReadDWORD(dw);
2769 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2770 Mem.ReadBoolean(boo);
2771 // Çàïóñêàåì ýòó ìóçûêó:
2772 gMusic.SetByName(str);
2773 gMusic.SpecPause := boo;
2774 gMusic.Play();
2775 gMusic.Pause(True);
2776 gMusic.SetPosition(dw);
2777 ///// /////
2779 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
2780 Mem.ReadInt(gTotalMonsters);
2781 ///// /////
2783 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
2784 if gGameSettings.GameMode = GM_CTF then
2785 begin
2786 // Ôëàã Êðàñíîé êîìàíäû:
2787 LoadFlag(@gFlags[FLAG_RED]);
2788 // Ôëàã Ñèíåé êîìàíäû:
2789 LoadFlag(@gFlags[FLAG_BLUE]);
2790 end;
2791 ///// /////
2793 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2794 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2795 begin
2796 // Î÷êè Êðàñíîé êîìàíäû:
2797 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
2798 // Î÷êè Ñèíåé êîìàíäû:
2799 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
2800 end;
2801 ///// /////
2802 end;
2804 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
2805 var
2806 Arr: TPanelArray;
2807 begin
2808 Result := nil;
2809 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
2810 Arr := PanelByID[PanelID].PWhere^;
2811 PanelArrayID := PanelByID[PanelID].PArrID;
2812 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
2813 end;
2815 end.