DEADSOFTWARE

grid code uglification; particles are great again (i hope)
[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): Boolean;
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;
102 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
105 procedure g_Map_ProfilersBegin ();
106 procedure g_Map_ProfilersEnd ();
108 const
109 RESPAWNPOINT_PLAYER1 = 1;
110 RESPAWNPOINT_PLAYER2 = 2;
111 RESPAWNPOINT_DM = 3;
112 RESPAWNPOINT_RED = 4;
113 RESPAWNPOINT_BLUE = 5;
115 FLAG_NONE = 0;
116 FLAG_RED = 1;
117 FLAG_BLUE = 2;
118 FLAG_DOM = 3;
120 FLAG_STATE_NONE = 0;
121 FLAG_STATE_NORMAL = 1;
122 FLAG_STATE_DROPPED = 2;
123 FLAG_STATE_CAPTURED = 3;
124 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
125 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
127 FLAG_TIME = 720; // 20 seconds
129 SKY_STRETCH: Single = 1.5;
131 const
132 GridTagInvalid = 0;
134 (* draw order:
135 PANEL_BACK
136 PANEL_STEP
137 PANEL_WALL
138 PANEL_CLOSEDOOR
139 PANEL_ACID1
140 PANEL_ACID2
141 PANEL_WATER
142 PANEL_FORE
143 *)
144 // sorted by draw priority
145 GridTagBack = 1 shl 0;
146 GridTagStep = 1 shl 1;
147 GridTagWall = 1 shl 2;
148 GridTagDoor = 1 shl 3;
149 GridTagAcid1 = 1 shl 4;
150 GridTagAcid2 = 1 shl 5;
151 GridTagWater = 1 shl 6;
152 GridTagFore = 1 shl 7;
153 // the following are invisible
154 GridTagLift = 1 shl 8;
155 GridTagBlockMon = 1 shl 9;
158 var
159 gWalls: TPanelArray;
160 gRenderBackgrounds: TPanelArray;
161 gRenderForegrounds: TPanelArray;
162 gWater, gAcid1, gAcid2: TPanelArray;
163 gSteps: TPanelArray;
164 gLifts: TPanelArray;
165 gBlockMon: TPanelArray;
166 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
167 //gDOMFlags: array of TFlag;
168 gMapInfo: TMapInfo;
169 gBackSize: TPoint;
170 gDoorMap: array of array of DWORD;
171 gLiftMap: array of array of DWORD;
172 gWADHash: TMD5Digest;
173 BackID: DWORD = DWORD(-1);
174 gExternalResources: TStringList;
176 gdbg_map_use_accel_render: Boolean = true;
177 gdbg_map_use_accel_coldet: Boolean = true;
178 //gdbg_map_use_tree_draw: Boolean = false;
179 //gdbg_map_use_tree_coldet: Boolean = false;
180 //gdbg_map_dump_coldet_tree_queries: Boolean = false;
181 profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY!
182 gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
184 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
187 implementation
189 uses
190 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
191 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
192 g_options, MAPREADER, g_triggers, g_player, MAPDEF,
193 Math, g_monsters, g_saveload, g_language, g_netmsg,
194 utils, sfs,
195 ImagingTypes, Imaging, ImagingUtility,
196 ImagingGif, ImagingNetworkGraphics;
198 const
199 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
200 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
201 FLAG_SIGNATURE = $47414C46; // 'FLAG'
204 type
205 TPanelGrid = specialize TBodyGridBase<TPanel>;
208 TDynAABBTreePanelBase = specialize TDynAABBTreeBase<TPanel>;
210 TDynAABBTreeMap = class(TDynAABBTreePanelBase)
211 function getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean; override;
212 end;
216 function TDynAABBTreeMap.getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean;
217 begin
218 result := false;
219 if (pan = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end;
220 aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width-1, pan.Y+pan.Height-1);
221 if (pan.Width < 1) or (pan.Height < 1) then exit;
222 if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!');
223 result := true;
224 end;
228 function panelTypeToTag (panelType: Word): Integer;
229 begin
230 case panelType of
231 PANEL_WALL: result := GridTagWall; // gWalls
232 PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls
233 PANEL_BACK: result := GridTagBack; // gRenderBackgrounds
234 PANEL_FORE: result := GridTagFore; // gRenderForegrounds
235 PANEL_WATER: result := GridTagWater; // gWater
236 PANEL_ACID1: result := GridTagAcid1; // gAcid1
237 PANEL_ACID2: result := GridTagAcid2; // gAcid2
238 PANEL_STEP: result := GridTagStep; // gSteps
239 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts
240 PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons
241 else result := GridTagInvalid;
242 end;
243 end;
246 function dplLess (a, b: TObject): Boolean;
247 var
248 pa, pb: TPanel;
249 begin
250 //result := ((a as TPanel).ArrIdx < (b as TPanel).ArrIdx);
251 pa := TPanel(a);
252 pb := TPanel(b);
253 if (pa.tag < pb.tag) then begin result := true; exit; end;
254 if (pa.tag > pb.tag) then begin result := false; exit; end;
255 result := (pa.ArrIdx < pb.ArrIdx);
256 end;
258 procedure dplClear ();
259 begin
260 if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
261 end;
264 type
265 TPanelID = record
266 PWhere: ^TPanelArray;
267 PArrID: Integer;
268 end;
270 var
271 PanelById: array of TPanelID;
272 Textures: TLevelTextureArray;
273 RespawnPoints: Array of TRespawnPoint;
274 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
275 //DOMFlagPoints: Array of TFlagPoint;
276 gMapGrid: TPanelGrid = nil;
277 //mapTree: TDynAABBTreeMap = nil;
280 procedure g_Map_ProfilersBegin ();
281 begin
282 if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
283 profMapCollision.mainBegin(g_profile_collision);
284 // create sections
285 if g_profile_collision then
286 begin
287 profMapCollision.sectionBegin('*solids');
288 profMapCollision.sectionEnd();
289 profMapCollision.sectionBegin('liquids');
290 profMapCollision.sectionEnd();
291 end;
292 end;
294 procedure g_Map_ProfilersEnd ();
295 begin
296 if (profMapCollision <> nil) then profMapCollision.mainEnd();
297 end;
300 // wall index in `gWalls` or -1
301 (*
302 function g_Map_traceToNearestWallOld (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Integer;
304 function sqchecker (pan: TPanel; var ray: Ray2D): Single;
305 var
306 aabb: AABB2D;
307 tmin: Single;
308 begin
309 result := -666.0; // invalid
310 if not pan.Enabled then exit;
311 aabb := AABB2D.CreateWH(pan.X, pan.Y, pan.Width, pan.Height);
312 if not aabb.valid then exit;
313 if aabb.intersects(ray, @tmin) then
314 begin
315 //if (tmin*tmin > maxDistSq) then exit;
316 if (tmin >= 0.0) then
317 begin
318 //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);
319 //if (tmin < 0.0) then tmin := 0.0;
320 result := tmin;
321 end;
322 end;
323 end;
325 var
326 qr: TDynAABBTreeMap.TSegmentQueryResult;
327 ray: Ray2D;
328 hxf, hyf: Single;
329 hx, hy: Integer;
330 maxDistSq: Single;
331 begin
332 result := -1;
333 if (mapTree = nil) then exit;
334 if mapTree.segmentQuery(qr, x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor)) then
335 begin
336 maxDistSq := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0);
337 if (qr.flesh <> nil) and (qr.time*qr.time <= maxDistSq) then
338 begin
339 result := qr.flesh.arrIdx;
340 if (hitx <> nil) or (hity <> nil) then
341 begin
342 ray := Ray2D.Create(x0, y0, x1, y1);
343 hxf := ray.origX+ray.dirX*qr.time;
344 hyf := ray.origY+ray.dirY*qr.time;
345 while true do
346 begin
347 hx := trunc(hxf);
348 hy := trunc(hyf);
349 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
350 begin
351 // go back a little
352 hxf -= ray.dirX;
353 hyf -= ray.dirY;
354 end
355 else
356 begin
357 break;
358 end;
359 end;
360 if (hitx <> nil) then hitx^ := hx;
361 if (hity <> nil) then hity^ := hy;
362 end;
363 end;
364 end;
365 end;
366 *)
369 // wall index in `gWalls` or -1
370 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Boolean;
371 var
372 lastX, lastY, lastDist: Integer;
373 wasHit: Boolean = false;
375 // pan=nil: before processing new tile
376 function sqchecker (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean;
377 var
378 dist: Integer;
379 begin
380 if (pan = nil) then
381 begin
382 // stop if something was hit at the previous tile
383 result := wasHit;
384 end
385 else
386 begin
387 result := false;
388 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
389 begin
390 if not pan.Enabled then exit;
391 end;
392 dist := (prevx-x0)*(prevx-x0)+(prevy-y0)*(prevy-y0);
393 if (dist < lastDist) then
394 begin
395 wasHit := true;
396 lastDist := dist;
397 lastX := prevx;
398 lastY := prevy;
399 end;
400 end;
401 end;
403 begin
404 result := false;
405 if (gMapGrid = nil) then exit;
406 lastDist := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)+1;
407 lastX := 0;
408 lastY := 0;
409 result := gMapGrid.traceRay(x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor));
410 if (hitx <> nil) then hitx^ := lastX;
411 if (hity <> nil) then hity^ := lastY;
412 end;
415 function g_Map_ForEachPanelAt (x, y: Integer; cb: TForEachPanelCB; panelType: Word): Boolean;
417 function checker (pan: TPanel; tag: Integer): Boolean;
418 begin
419 result := false; // don't stop, ever
421 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
422 begin
423 if not pan.Enabled then exit;
424 end;
426 result := (x >= pan.X) and (y >= pan.Y) and (x < pan.X+pan.Width) and (y < pan.Y+pan.Height);
427 if not result then exit;
429 if ((tag and GridTagLift) <> 0) then
430 begin
431 result :=
432 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
433 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
434 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
435 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
436 end;
438 // other shit
439 if result then result := cb(pan);
440 end;
442 var
443 tagmask: Integer = 0;
444 begin
445 result := false;
446 if not assigned(cb) then exit;
448 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
449 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
450 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
451 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
452 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
453 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
454 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
456 if (tagmask = 0) then exit;// just in case
457 result := gMapGrid.forEachInAABB(x, y, 1, 1, checker, tagmask);
458 end;
461 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
463 function checker (pan: TPanel; tag: Integer): Boolean;
464 begin
465 result := false; // don't stop, ever
467 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
468 begin
469 if not pan.Enabled then exit;
470 end;
472 result := (x >= pan.X) and (y >= pan.Y) and (x < pan.X+pan.Width) and (y < pan.Y+pan.Height);
473 if not result then exit;
475 if ((tag and GridTagLift) <> 0) then
476 begin
477 result :=
478 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
479 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
480 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
481 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
482 end;
483 end;
485 var
486 tagmask: Integer = 0;
487 begin
488 result := false;
490 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
491 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
492 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
493 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
494 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
495 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
496 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
498 if (tagmask = 0) then exit;// just in case
499 result := gMapGrid.forEachAtPoint(x, y, checker, tagmask);
500 end;
503 function g_Map_IsSpecialTexture(Texture: String): Boolean;
504 begin
505 Result := (Texture = TEXTURE_NAME_WATER) or
506 (Texture = TEXTURE_NAME_ACID1) or
507 (Texture = TEXTURE_NAME_ACID2);
508 end;
510 procedure CreateDoorMap();
511 var
512 PanelArray: Array of record
513 X, Y: Integer;
514 Width, Height: Word;
515 Active: Boolean;
516 PanelID: DWORD;
517 end;
518 a, b, c, m, i, len: Integer;
519 ok: Boolean;
520 begin
521 if gWalls = nil then
522 Exit;
524 i := 0;
525 len := 128;
526 SetLength(PanelArray, len);
528 for a := 0 to High(gWalls) do
529 if gWalls[a].Door then
530 begin
531 PanelArray[i].X := gWalls[a].X;
532 PanelArray[i].Y := gWalls[a].Y;
533 PanelArray[i].Width := gWalls[a].Width;
534 PanelArray[i].Height := gWalls[a].Height;
535 PanelArray[i].Active := True;
536 PanelArray[i].PanelID := a;
538 i := i + 1;
539 if i = len then
540 begin
541 len := len + 128;
542 SetLength(PanelArray, len);
543 end;
544 end;
546 // Íåò äâåðåé:
547 if i = 0 then
548 begin
549 PanelArray := nil;
550 Exit;
551 end;
553 SetLength(gDoorMap, 0);
555 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
557 for a := 0 to i-1 do
558 if PanelArray[a].Active then
559 begin
560 PanelArray[a].Active := False;
561 m := Length(gDoorMap);
562 SetLength(gDoorMap, m+1);
563 SetLength(gDoorMap[m], 1);
564 gDoorMap[m, 0] := PanelArray[a].PanelID;
565 ok := True;
567 while ok do
568 begin
569 ok := False;
571 for b := 0 to i-1 do
572 if PanelArray[b].Active then
573 for c := 0 to High(gDoorMap[m]) do
574 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
575 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
576 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
577 PanelArray[b].Width, PanelArray[b].Height,
578 gWalls[gDoorMap[m, c]].X,
579 gWalls[gDoorMap[m, c]].Y,
580 gWalls[gDoorMap[m, c]].Width,
581 gWalls[gDoorMap[m, c]].Height) then
582 begin
583 PanelArray[b].Active := False;
584 SetLength(gDoorMap[m],
585 Length(gDoorMap[m])+1);
586 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
587 ok := True;
588 Break;
589 end;
590 end;
592 g_Game_StepLoading();
593 end;
595 PanelArray := nil;
596 end;
598 procedure CreateLiftMap();
599 var
600 PanelArray: Array of record
601 X, Y: Integer;
602 Width, Height: Word;
603 Active: Boolean;
604 end;
605 a, b, c, len, i, j: Integer;
606 ok: Boolean;
607 begin
608 if gLifts = nil then
609 Exit;
611 len := Length(gLifts);
612 SetLength(PanelArray, len);
614 for a := 0 to len-1 do
615 begin
616 PanelArray[a].X := gLifts[a].X;
617 PanelArray[a].Y := gLifts[a].Y;
618 PanelArray[a].Width := gLifts[a].Width;
619 PanelArray[a].Height := gLifts[a].Height;
620 PanelArray[a].Active := True;
621 end;
623 SetLength(gLiftMap, len);
624 i := 0;
626 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
628 for a := 0 to len-1 do
629 if PanelArray[a].Active then
630 begin
631 PanelArray[a].Active := False;
632 SetLength(gLiftMap[i], 32);
633 j := 0;
634 gLiftMap[i, j] := a;
635 ok := True;
637 while ok do
638 begin
639 ok := False;
640 for b := 0 to len-1 do
641 if PanelArray[b].Active then
642 for c := 0 to j do
643 if g_CollideAround(PanelArray[b].X,
644 PanelArray[b].Y,
645 PanelArray[b].Width,
646 PanelArray[b].Height,
647 PanelArray[gLiftMap[i, c]].X,
648 PanelArray[gLiftMap[i, c]].Y,
649 PanelArray[gLiftMap[i, c]].Width,
650 PanelArray[gLiftMap[i, c]].Height) then
651 begin
652 PanelArray[b].Active := False;
653 j := j+1;
654 if j > High(gLiftMap[i]) then
655 SetLength(gLiftMap[i],
656 Length(gLiftMap[i])+32);
658 gLiftMap[i, j] := b;
659 ok := True;
661 Break;
662 end;
663 end;
665 SetLength(gLiftMap[i], j+1);
666 i := i+1;
668 g_Game_StepLoading();
669 end;
671 SetLength(gLiftMap, i);
673 PanelArray := nil;
674 end;
676 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
677 CurTex: Integer; sav: Boolean): Integer;
678 var
679 len: Integer;
680 panels: ^TPanelArray;
681 begin
682 Result := -1;
684 case PanelRec.PanelType of
685 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
686 panels := @gWalls;
687 PANEL_BACK:
688 panels := @gRenderBackgrounds;
689 PANEL_FORE:
690 panels := @gRenderForegrounds;
691 PANEL_WATER:
692 panels := @gWater;
693 PANEL_ACID1:
694 panels := @gAcid1;
695 PANEL_ACID2:
696 panels := @gAcid2;
697 PANEL_STEP:
698 panels := @gSteps;
699 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
700 panels := @gLifts;
701 PANEL_BLOCKMON:
702 panels := @gBlockMon;
703 else
704 Exit;
705 end;
707 len := Length(panels^);
708 SetLength(panels^, len + 1);
710 panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
711 panels^[len].ArrIdx := len;
712 panels^[len].tag := panelTypeToTag(PanelRec.PanelType);
713 if sav then
714 panels^[len].SaveIt := True;
716 Result := len;
718 len := Length(PanelByID);
719 SetLength(PanelByID, len + 1);
720 PanelByID[len].PWhere := panels;
721 PanelByID[len].PArrID := Result;
722 end;
724 function CreateNullTexture(RecName: String): Integer;
725 begin
726 SetLength(Textures, Length(Textures)+1);
727 result := High(Textures);
729 with Textures[High(Textures)] do
730 begin
731 TextureName := RecName;
732 Width := 1;
733 Height := 1;
734 Anim := False;
735 TextureID := TEXTURE_NONE;
736 end;
737 end;
739 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
740 var
741 WAD: TWADFile;
742 TextureData: Pointer;
743 WADName, txname: String;
744 a, ResLength: Integer;
745 begin
746 Result := -1;
748 if Textures <> nil then
749 for a := 0 to High(Textures) do
750 if Textures[a].TextureName = RecName then
751 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
752 Result := a;
753 Exit;
754 end;
756 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
757 if (RecName = TEXTURE_NAME_WATER) or
758 (RecName = TEXTURE_NAME_ACID1) or
759 (RecName = TEXTURE_NAME_ACID2) then
760 begin
761 SetLength(Textures, Length(Textures)+1);
763 with Textures[High(Textures)] do
764 begin
765 TextureName := RecName;
767 if TextureName = TEXTURE_NAME_WATER then
768 TextureID := TEXTURE_SPECIAL_WATER
769 else
770 if TextureName = TEXTURE_NAME_ACID1 then
771 TextureID := TEXTURE_SPECIAL_ACID1
772 else
773 if TextureName = TEXTURE_NAME_ACID2 then
774 TextureID := TEXTURE_SPECIAL_ACID2;
776 Anim := False;
777 end;
779 result := High(Textures);
780 Exit;
781 end;
783 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
784 WADName := g_ExtractWadName(RecName);
786 WAD := TWADFile.Create();
788 if WADName <> '' then
789 WADName := GameDir+'/wads/'+WADName
790 else
791 WADName := Map;
793 WAD.ReadFile(WADName);
795 txname := RecName;
797 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
798 begin
799 FreeMem(TextureData);
800 RecName := 'COMMON\ALIEN';
801 end;
804 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
805 begin
806 SetLength(Textures, Length(Textures)+1);
807 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
808 Exit;
809 e_GetTextureSize(Textures[High(Textures)].TextureID,
810 @Textures[High(Textures)].Width,
811 @Textures[High(Textures)].Height);
812 FreeMem(TextureData);
813 Textures[High(Textures)].TextureName := {RecName}txname;
814 Textures[High(Textures)].Anim := False;
816 result := High(Textures);
817 end
818 else // Íåò òàêîãî ðåóñðñà â WAD'å
819 begin
820 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
821 if log then
822 begin
823 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
824 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
825 end;
826 end;
828 WAD.Free();
829 end;
831 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
832 var
833 WAD: TWADFile;
834 TextureWAD: PChar = nil;
835 TextData: Pointer = nil;
836 TextureData: Pointer = nil;
837 cfg: TConfig = nil;
838 WADName: String;
839 ResLength: Integer;
840 TextureResource: String;
841 _width, _height, _framecount, _speed: Integer;
842 _backanimation: Boolean;
843 //imgfmt: string;
844 ia: TDynImageDataArray = nil;
845 f, c, frdelay, frloop: Integer;
846 begin
847 result := -1;
849 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
851 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
852 WADName := g_ExtractWadName(RecName);
854 WAD := TWADFile.Create();
855 try
856 if WADName <> '' then
857 WADName := GameDir+'/wads/'+WADName
858 else
859 WADName := Map;
861 WAD.ReadFile(WADName);
863 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
864 begin
865 if log then
866 begin
867 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
868 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
869 end;
870 exit;
871 end;
873 {TEST
874 if WADName = Map then
875 begin
876 //FreeMem(TextureWAD);
877 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
878 end;
881 WAD.FreeWAD();
883 if ResLength < 6 then
884 begin
885 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
886 exit;
887 end;
889 // ýòî ïòèöà? ýòî ñàìîë¸ò?
890 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
891 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
892 begin
893 // íåò, ýòî ñóïåðìåí!
894 if not WAD.ReadMemory(TextureWAD, ResLength) then
895 begin
896 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
897 exit;
898 end;
900 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
901 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
902 begin
903 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
904 exit;
905 end;
907 cfg := TConfig.CreateMem(TextData, ResLength);
909 TextureResource := cfg.ReadStr('', 'resource', '');
910 if TextureResource = '' then
911 begin
912 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
913 exit;
914 end;
916 _width := cfg.ReadInt('', 'framewidth', 0);
917 _height := cfg.ReadInt('', 'frameheight', 0);
918 _framecount := cfg.ReadInt('', 'framecount', 0);
919 _speed := cfg.ReadInt('', 'waitcount', 0);
920 _backanimation := cfg.ReadBool('', 'backanimation', False);
922 cfg.Free();
923 cfg := nil;
925 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
926 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
927 begin
928 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
929 exit;
930 end;
932 WAD.Free();
933 WAD := nil;
935 SetLength(Textures, Length(Textures)+1);
936 with Textures[High(Textures)] do
937 begin
938 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
939 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
940 begin
941 TextureName := RecName;
942 Width := _width;
943 Height := _height;
944 Anim := True;
945 FramesCount := _framecount;
946 Speed := _speed;
947 result := High(Textures);
948 end
949 else
950 begin
951 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
952 end;
953 end;
954 end
955 else
956 begin
957 // try animated image
959 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
960 if length(imgfmt) = 0 then
961 begin
962 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
963 exit;
964 end;
966 GlobalMetadata.ClearMetaItems();
967 GlobalMetadata.ClearMetaItemsForSaving();
968 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
969 begin
970 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
971 exit;
972 end;
973 if length(ia) = 0 then
974 begin
975 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
976 exit;
977 end;
979 WAD.Free();
980 WAD := nil;
982 _width := ia[0].width;
983 _height := ia[0].height;
984 _framecount := length(ia);
985 _speed := 1;
986 _backanimation := false;
987 frdelay := -1;
988 frloop := -666;
989 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
990 begin
991 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
992 try
993 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
994 frdelay := f;
995 if f < 0 then f := 0;
996 // rounding ;-)
997 c := f mod 28;
998 if c < 13 then c := 0 else c := 1;
999 f := (f div 28)+c;
1000 if f < 1 then f := 1 else if f > 255 then f := 255;
1001 _speed := f;
1002 except
1003 end;
1004 end;
1005 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
1006 begin
1007 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
1008 try
1009 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
1010 frloop := f;
1011 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
1012 except
1013 end;
1014 end;
1015 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
1016 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
1017 f := ord(_backanimation);
1018 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);
1020 SetLength(Textures, Length(Textures)+1);
1021 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
1022 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
1023 begin
1024 Textures[High(Textures)].TextureName := RecName;
1025 Textures[High(Textures)].Width := _width;
1026 Textures[High(Textures)].Height := _height;
1027 Textures[High(Textures)].Anim := True;
1028 Textures[High(Textures)].FramesCount := length(ia);
1029 Textures[High(Textures)].Speed := _speed;
1030 result := High(Textures);
1031 //writeln(' CREATED!');
1032 end
1033 else
1034 begin
1035 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
1036 end;
1037 end;
1038 finally
1039 for f := 0 to High(ia) do FreeImage(ia[f]);
1040 WAD.Free();
1041 cfg.Free();
1042 if TextureWAD <> nil then FreeMem(TextureWAD);
1043 if TextData <> nil then FreeMem(TextData);
1044 if TextureData <> nil then FreeMem(TextureData);
1045 end;
1046 end;
1048 procedure CreateItem(Item: TItemRec_1);
1049 begin
1050 if g_Game_IsClient then Exit;
1052 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
1053 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
1054 Exit;
1056 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
1057 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
1058 end;
1060 procedure CreateArea(Area: TAreaRec_1);
1061 var
1062 a: Integer;
1063 id: DWORD;
1064 begin
1065 case Area.AreaType of
1066 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
1067 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
1068 begin
1069 SetLength(RespawnPoints, Length(RespawnPoints)+1);
1070 with RespawnPoints[High(RespawnPoints)] do
1071 begin
1072 X := Area.X;
1073 Y := Area.Y;
1074 Direction := TDirection(Area.Direction);
1076 case Area.AreaType of
1077 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
1078 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
1079 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
1080 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
1081 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
1082 end;
1083 end;
1084 end;
1086 AREA_REDFLAG, AREA_BLUEFLAG:
1087 begin
1088 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
1090 if FlagPoints[a] <> nil then Exit;
1092 New(FlagPoints[a]);
1094 with FlagPoints[a]^ do
1095 begin
1096 X := Area.X-FLAGRECT.X;
1097 Y := Area.Y-FLAGRECT.Y;
1098 Direction := TDirection(Area.Direction);
1099 end;
1101 with gFlags[a] do
1102 begin
1103 case a of
1104 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
1105 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
1106 end;
1108 Animation := TAnimation.Create(id, True, 8);
1109 Obj.Rect := FLAGRECT;
1111 g_Map_ResetFlag(a);
1112 end;
1113 end;
1115 AREA_DOMFLAG:
1116 begin
1117 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
1118 with DOMFlagPoints[High(DOMFlagPoints)] do
1119 begin
1120 X := Area.X;
1121 Y := Area.Y;
1122 Direction := TDirection(Area.Direction);
1123 end;
1125 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
1126 end;
1127 end;
1128 end;
1130 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
1131 var
1132 _trigger: TTrigger;
1133 begin
1134 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
1136 with _trigger do
1137 begin
1138 X := Trigger.X;
1139 Y := Trigger.Y;
1140 Width := Trigger.Width;
1141 Height := Trigger.Height;
1142 Enabled := ByteBool(Trigger.Enabled);
1143 TexturePanel := Trigger.TexturePanel;
1144 TexturePanelType := fTexturePanel1Type;
1145 ShotPanelType := fTexturePanel2Type;
1146 TriggerType := Trigger.TriggerType;
1147 ActivateType := Trigger.ActivateType;
1148 Keys := Trigger.Keys;
1149 Data.Default := Trigger.DATA;
1150 end;
1152 g_Triggers_Create(_trigger);
1153 end;
1155 procedure CreateMonster(monster: TMonsterRec_1);
1156 var
1157 a: Integer;
1158 mon: TMonster;
1159 begin
1160 if g_Game_IsClient then Exit;
1162 if (gGameSettings.GameType = GT_SINGLE)
1163 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
1164 begin
1165 mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction));
1167 if gTriggers <> nil then
1168 begin
1169 for a := 0 to High(gTriggers) do
1170 begin
1171 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1172 begin
1173 if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
1174 end;
1175 end;
1176 end;
1178 if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters);
1179 end;
1180 end;
1182 procedure g_Map_ReAdd_DieTriggers();
1184 function monsDieTrig (mon: TMonster): Boolean;
1185 var
1186 a: Integer;
1187 begin
1188 result := false; // don't stop
1189 mon.ClearTriggers();
1190 for a := 0 to High(gTriggers) do
1191 begin
1192 if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
1193 begin
1194 if (gTriggers[a].Data.MonsterID-1) = mon.StartID then mon.AddTrigger(a);
1195 end;
1196 end;
1197 end;
1199 begin
1200 if g_Game_IsClient then Exit;
1202 g_Mons_ForEach(monsDieTrig);
1203 end;
1205 function extractWadName(resourceName: string): string;
1206 var
1207 posN: Integer;
1208 begin
1209 posN := Pos(':', resourceName);
1210 if posN > 0 then
1211 Result:= Copy(resourceName, 0, posN-1)
1212 else
1213 Result := '';
1214 end;
1216 procedure addResToExternalResList(res: string);
1217 begin
1218 res := extractWadName(res);
1219 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
1220 gExternalResources.Add(res);
1221 end;
1223 procedure generateExternalResourcesList(mapReader: TMapReader_1);
1224 var
1225 textures: TTexturesRec1Array;
1226 mapHeader: TMapHeaderRec_1;
1227 i: integer;
1228 resFile: String = '';
1229 begin
1230 if gExternalResources = nil then
1231 gExternalResources := TStringList.Create;
1233 gExternalResources.Clear;
1234 textures := mapReader.GetTextures();
1235 for i := 0 to High(textures) do
1236 begin
1237 addResToExternalResList(resFile);
1238 end;
1240 textures := nil;
1242 mapHeader := mapReader.GetMapHeader;
1244 addResToExternalResList(mapHeader.MusicName);
1245 addResToExternalResList(mapHeader.SkyName);
1246 end;
1248 procedure mapCreateGrid ();
1249 var
1250 mapX0: Integer = $3fffffff;
1251 mapY0: Integer = $3fffffff;
1252 mapX1: Integer = -$3fffffff;
1253 mapY1: Integer = -$3fffffff;
1255 procedure calcBoundingBox (constref panels: TPanelArray);
1256 var
1257 idx: Integer;
1258 pan: TPanel;
1259 begin
1260 for idx := 0 to High(panels) do
1261 begin
1262 pan := panels[idx];
1263 if not pan.visvalid then continue;
1264 if (pan.Width < 1) or (pan.Height < 1) then continue;
1265 if (mapX0 > pan.x0) then mapX0 := pan.x0;
1266 if (mapY0 > pan.y0) then mapY0 := pan.y0;
1267 if (mapX1 < pan.x1) then mapX1 := pan.x1;
1268 if (mapY1 < pan.y1) then mapY1 := pan.y1;
1269 end;
1270 end;
1272 procedure addPanelsToGrid (constref panels: TPanelArray; tag: Integer);
1273 var
1274 idx: Integer;
1275 pan: TPanel;
1276 begin
1277 tag := panelTypeToTag(tag);
1278 for idx := High(panels) downto 0 do
1279 begin
1280 pan := panels[idx];
1281 pan.tag := tag;
1282 if not pan.visvalid then continue;
1283 gMapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, tag);
1284 //mapTree.insertObject(pan, tag, true); // as static object
1285 end;
1286 end;
1288 begin
1289 gMapGrid.Free();
1290 gMapGrid := nil;
1291 //mapTree.Free();
1292 //mapTree := nil;
1294 calcBoundingBox(gWalls);
1295 calcBoundingBox(gRenderBackgrounds);
1296 calcBoundingBox(gRenderForegrounds);
1297 calcBoundingBox(gWater);
1298 calcBoundingBox(gAcid1);
1299 calcBoundingBox(gAcid2);
1300 calcBoundingBox(gSteps);
1301 calcBoundingBox(gLifts);
1302 calcBoundingBox(gBlockMon);
1304 e_WriteLog(Format('map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING);
1306 gMapGrid := TPanelGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
1307 //mapTree := TDynAABBTreeMap.Create();
1309 addPanelsToGrid(gWalls, PANEL_WALL);
1310 addPanelsToGrid(gWalls, PANEL_CLOSEDOOR);
1311 addPanelsToGrid(gWalls, PANEL_OPENDOOR);
1312 addPanelsToGrid(gRenderBackgrounds, PANEL_BACK);
1313 addPanelsToGrid(gRenderForegrounds, PANEL_FORE);
1314 addPanelsToGrid(gWater, PANEL_WATER);
1315 addPanelsToGrid(gAcid1, PANEL_ACID1);
1316 addPanelsToGrid(gAcid2, PANEL_ACID2);
1317 addPanelsToGrid(gSteps, PANEL_STEP);
1318 addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here
1319 addPanelsToGrid(gBlockMon, PANEL_BLOCKMON);
1321 gMapGrid.dumpStats();
1322 //e_WriteLog(Format('tree depth: %d; %d nodes used, %d nodes allocated', [mapTree.computeTreeHeight, mapTree.nodeCount, mapTree.nodeAlloced]), MSG_NOTIFY);
1323 //mapTree.forEachLeaf(nil);
1324 end;
1326 function g_Map_Load(Res: String): Boolean;
1327 const
1328 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
1329 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
1330 var
1331 WAD: TWADFile;
1332 MapReader: TMapReader_1;
1333 Header: TMapHeaderRec_1;
1334 _textures: TTexturesRec1Array;
1335 _texnummap: array of Integer; // `_textures` -> `Textures`
1336 panels: TPanelsRec1Array;
1337 items: TItemsRec1Array;
1338 monsters: TMonsterRec1Array;
1339 areas: TAreasRec1Array;
1340 triggers: TTriggersRec1Array;
1341 a, b, c, k: Integer;
1342 PanelID: DWORD;
1343 AddTextures: TAddTextureArray;
1344 texture: TTextureRec_1;
1345 TriggersTable: Array of record
1346 TexturePanel: Integer;
1347 LiftPanel: Integer;
1348 DoorPanel: Integer;
1349 ShotPanel: Integer;
1350 end;
1351 FileName, mapResName, s, TexName: String;
1352 Data: Pointer;
1353 Len: Integer;
1354 ok, isAnim, trigRef: Boolean;
1355 CurTex, ntn: Integer;
1357 begin
1358 gMapGrid.Free();
1359 gMapGrid := nil;
1360 //mapTree.Free();
1361 //mapTree := nil;
1363 Result := False;
1364 gMapInfo.Map := Res;
1365 TriggersTable := nil;
1366 FillChar(texture, SizeOf(texture), 0);
1368 sfsGCDisable(); // temporary disable removing of temporary volumes
1369 try
1370 // Çàãðóçêà WAD:
1371 FileName := g_ExtractWadName(Res);
1372 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
1373 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
1375 WAD := TWADFile.Create();
1376 if not WAD.ReadFile(FileName) then
1377 begin
1378 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
1379 WAD.Free();
1380 Exit;
1381 end;
1382 //k8: why loader ignores path here?
1383 mapResName := g_ExtractFileName(Res);
1384 if not WAD.GetMapResource(mapResName, Data, Len) then
1385 begin
1386 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
1387 WAD.Free();
1388 Exit;
1389 end;
1391 WAD.Free();
1393 // Çàãðóçêà êàðòû:
1394 e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
1395 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
1396 MapReader := TMapReader_1.Create();
1398 if not MapReader.LoadMap(Data) then
1399 begin
1400 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
1401 FreeMem(Data);
1402 MapReader.Free();
1403 Exit;
1404 end;
1406 FreeMem(Data);
1407 generateExternalResourcesList(MapReader);
1408 // Çàãðóçêà òåêñòóð:
1409 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
1410 _textures := MapReader.GetTextures();
1411 _texnummap := nil;
1413 // Äîáàâëåíèå òåêñòóð â Textures[]:
1414 if _textures <> nil then
1415 begin
1416 e_WriteLog(' Loading textures:', MSG_NOTIFY);
1417 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
1418 SetLength(_texnummap, length(_textures));
1420 for a := 0 to High(_textures) do
1421 begin
1422 SetLength(s, 64);
1423 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
1424 for b := 1 to Length(s) do
1425 if s[b] = #0 then
1426 begin
1427 SetLength(s, b-1);
1428 Break;
1429 end;
1430 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1431 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1432 // Àíèìèðîâàííàÿ òåêñòóðà:
1433 if ByteBool(_textures[a].Anim) then
1434 begin
1435 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1436 if ntn < 0 then
1437 begin
1438 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1439 ntn := CreateNullTexture(_textures[a].Resource);
1440 end;
1441 end
1442 else // Îáû÷íàÿ òåêñòóðà:
1443 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1444 if ntn < 0 then
1445 begin
1446 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1447 ntn := CreateNullTexture(_textures[a].Resource);
1448 end;
1450 _texnummap[a] := ntn; // fix texture number
1451 g_Game_StepLoading();
1452 end;
1453 end;
1455 // Çàãðóçêà òðèããåðîâ:
1456 gTriggerClientID := 0;
1457 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1458 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1459 triggers := MapReader.GetTriggers();
1461 // Çàãðóçêà ïàíåëåé:
1462 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1463 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1464 panels := MapReader.GetPanels();
1466 // check texture numbers for panels
1467 for a := 0 to High(panels) do
1468 begin
1469 if panels[a].TextureNum > High(_textures) then
1470 begin
1471 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1472 result := false;
1473 exit;
1474 end;
1475 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1476 end;
1478 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1479 if triggers <> nil then
1480 begin
1481 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1482 SetLength(TriggersTable, Length(triggers));
1483 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1485 for a := 0 to High(TriggersTable) do
1486 begin
1487 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1488 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1489 // Ëèôòû:
1490 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1491 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1492 else
1493 TriggersTable[a].LiftPanel := -1;
1494 // Äâåðè:
1495 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1496 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1497 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1498 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1499 else
1500 TriggersTable[a].DoorPanel := -1;
1501 // Òóðåëü:
1502 if triggers[a].TriggerType = TRIGGER_SHOT then
1503 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1504 else
1505 TriggersTable[a].ShotPanel := -1;
1507 g_Game_StepLoading();
1508 end;
1509 end;
1511 // Ñîçäàåì ïàíåëè:
1512 if panels <> nil then
1513 begin
1514 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1515 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1517 for a := 0 to High(panels) do
1518 begin
1519 SetLength(AddTextures, 0);
1520 trigRef := False;
1521 CurTex := -1;
1522 if _textures <> nil then
1523 begin
1524 texture := _textures[panels[a].TextureNum];
1525 ok := True;
1526 end
1527 else
1528 ok := False;
1530 if ok then
1531 begin
1532 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1533 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1534 ok := False;
1535 if (TriggersTable <> nil) and (_textures <> nil) then
1536 for b := 0 to High(TriggersTable) do
1537 if (TriggersTable[b].TexturePanel = a)
1538 or (TriggersTable[b].ShotPanel = a) then
1539 begin
1540 trigRef := True;
1541 ok := True;
1542 Break;
1543 end;
1544 end;
1546 if ok then
1547 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1548 SetLength(s, 64);
1549 CopyMemory(@s[1], @texture.Resource[0], 64);
1550 // Èçìåðÿåì äëèíó:
1551 Len := Length(s);
1552 for c := Len downto 1 do
1553 if s[c] <> #0 then
1554 begin
1555 Len := c;
1556 Break;
1557 end;
1558 SetLength(s, Len);
1560 // Ñïåö-òåêñòóðû çàïðåùåíû:
1561 if g_Map_IsSpecialTexture(s) then
1562 ok := False
1563 else
1564 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1565 ok := g_Texture_NumNameFindStart(s);
1567 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1568 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1569 if ok then
1570 begin
1571 k := NNF_NAME_BEFORE;
1572 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1573 while ok or (k = NNF_NAME_BEFORE) or
1574 (k = NNF_NAME_EQUALS) do
1575 begin
1576 k := g_Texture_NumNameFindNext(TexName);
1578 if (k = NNF_NAME_BEFORE) or
1579 (k = NNF_NAME_AFTER) then
1580 begin
1581 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1582 if ByteBool(texture.Anim) then
1583 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1584 isAnim := True;
1585 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1586 if not ok then
1587 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1588 isAnim := False;
1589 ok := CreateTexture(TexName, FileName, False) >= 0;
1590 end;
1591 end
1592 else
1593 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1594 isAnim := False;
1595 ok := CreateTexture(TexName, FileName, False) >= 0;
1596 if not ok then
1597 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1598 isAnim := True;
1599 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1600 end;
1601 end;
1603 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1604 if ok then
1605 begin
1606 for c := 0 to High(Textures) do
1607 if Textures[c].TextureName = TexName then
1608 begin
1609 SetLength(AddTextures, Length(AddTextures)+1);
1610 AddTextures[High(AddTextures)].Texture := c;
1611 AddTextures[High(AddTextures)].Anim := isAnim;
1612 Break;
1613 end;
1614 end;
1615 end
1616 else
1617 if k = NNF_NAME_EQUALS then
1618 begin
1619 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1620 SetLength(AddTextures, Length(AddTextures)+1);
1621 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1622 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1623 CurTex := High(AddTextures);
1624 ok := True;
1625 end
1626 else // NNF_NO_NAME
1627 ok := False;
1628 end; // while ok...
1630 ok := True;
1631 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1632 end; // if ok - ññûëàþòñÿ òðèããåðû
1634 if not ok then
1635 begin
1636 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1637 SetLength(AddTextures, 1);
1638 AddTextures[0].Texture := panels[a].TextureNum;
1639 AddTextures[0].Anim := ByteBool(texture.Anim);
1640 CurTex := 0;
1641 end;
1643 //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);
1645 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1646 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1648 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1649 if TriggersTable <> nil then
1650 for b := 0 to High(TriggersTable) do
1651 begin
1652 // Òðèããåð äâåðè/ëèôòà:
1653 if (TriggersTable[b].LiftPanel = a) or
1654 (TriggersTable[b].DoorPanel = a) then
1655 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1656 // Òðèããåð ñìåíû òåêñòóðû:
1657 if TriggersTable[b].TexturePanel = a then
1658 triggers[b].TexturePanel := PanelID;
1659 // Òðèããåð "Òóðåëü":
1660 if TriggersTable[b].ShotPanel = a then
1661 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1662 end;
1664 g_Game_StepLoading();
1665 end;
1666 end;
1668 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1669 if (triggers <> nil) and not gLoadGameMode then
1670 begin
1671 e_WriteLog(' Creating triggers...', MSG_NOTIFY);
1672 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1673 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1674 for a := 0 to High(triggers) do
1675 begin
1676 if triggers[a].TexturePanel <> -1 then
1677 b := panels[TriggersTable[a].TexturePanel].PanelType
1678 else
1679 b := 0;
1680 if (triggers[a].TriggerType = TRIGGER_SHOT) and
1681 (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1682 c := panels[TriggersTable[a].ShotPanel].PanelType
1683 else
1684 c := 0;
1685 CreateTrigger(triggers[a], b, c);
1686 end;
1687 end;
1689 // Çàãðóçêà ïðåäìåòîâ:
1690 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1691 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1692 items := MapReader.GetItems();
1694 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1695 if (items <> nil) and not gLoadGameMode then
1696 begin
1697 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1698 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1699 for a := 0 to High(items) do
1700 CreateItem(Items[a]);
1701 end;
1703 // Çàãðóçêà îáëàñòåé:
1704 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1705 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1706 areas := MapReader.GetAreas();
1708 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1709 if areas <> nil then
1710 begin
1711 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1712 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1713 for a := 0 to High(areas) do
1714 CreateArea(areas[a]);
1715 end;
1717 // Çàãðóçêà ìîíñòðîâ:
1718 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1719 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1720 monsters := MapReader.GetMonsters();
1722 gTotalMonsters := 0;
1724 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1725 if (monsters <> nil) and not gLoadGameMode then
1726 begin
1727 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1728 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1729 for a := 0 to High(monsters) do
1730 CreateMonster(monsters[a]);
1731 end;
1733 // Çàãðóçêà îïèñàíèÿ êàðòû:
1734 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1735 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1736 Header := MapReader.GetMapHeader();
1738 MapReader.Free();
1740 with gMapInfo do
1741 begin
1742 Name := Header.MapName;
1743 Description := Header.MapDescription;
1744 Author := Header.MapAuthor;
1745 MusicName := Header.MusicName;
1746 SkyName := Header.SkyName;
1747 Height := Header.Height;
1748 Width := Header.Width;
1749 end;
1751 // Çàãðóçêà íåáà:
1752 if gMapInfo.SkyName <> '' then
1753 begin
1754 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1755 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1756 FileName := g_ExtractWadName(gMapInfo.SkyName);
1758 if FileName <> '' then
1759 FileName := GameDir+'/wads/'+FileName
1760 else
1761 begin
1762 FileName := g_ExtractWadName(Res);
1763 end;
1765 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1766 if g_Texture_CreateWAD(BackID, s) then
1767 begin
1768 g_Game_SetupScreenSize();
1769 end
1770 else
1771 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1772 end;
1774 // Çàãðóçêà ìóçûêè:
1775 ok := False;
1776 if gMapInfo.MusicName <> '' then
1777 begin
1778 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1779 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1780 FileName := g_ExtractWadName(gMapInfo.MusicName);
1782 if FileName <> '' then
1783 FileName := GameDir+'/wads/'+FileName
1784 else
1785 begin
1786 FileName := g_ExtractWadName(Res);
1787 end;
1789 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1790 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1791 ok := True
1792 else
1793 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1794 end;
1796 // Îñòàëüíûå óñòàíâêè:
1797 CreateDoorMap();
1798 CreateLiftMap();
1800 g_Items_Init();
1801 g_Weapon_Init();
1802 g_Monsters_Init();
1804 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1805 if not gLoadGameMode then
1806 g_GFX_Init();
1808 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1809 _textures := nil;
1810 panels := nil;
1811 items := nil;
1812 areas := nil;
1813 triggers := nil;
1814 TriggersTable := nil;
1815 AddTextures := nil;
1817 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
1818 if ok and (not gLoadGameMode) then
1819 begin
1820 gMusic.SetByName(gMapInfo.MusicName);
1821 gMusic.Play();
1822 end
1823 else
1824 gMusic.SetByName('');
1825 finally
1826 sfsGCEnable(); // enable releasing unused volumes
1827 end;
1829 e_WriteLog('Creating map grid', MSG_NOTIFY);
1830 mapCreateGrid();
1832 e_WriteLog('Done loading map.', MSG_NOTIFY);
1833 Result := True;
1834 end;
1836 function g_Map_GetMapInfo(Res: String): TMapInfo;
1837 var
1838 WAD: TWADFile;
1839 MapReader: TMapReader_1;
1840 Header: TMapHeaderRec_1;
1841 FileName: String;
1842 Data: Pointer;
1843 Len: Integer;
1844 begin
1845 FillChar(Result, SizeOf(Result), 0);
1846 FileName := g_ExtractWadName(Res);
1848 WAD := TWADFile.Create();
1849 if not WAD.ReadFile(FileName) then
1850 begin
1851 WAD.Free();
1852 Exit;
1853 end;
1855 //k8: it ignores path again
1856 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
1857 begin
1858 WAD.Free();
1859 Exit;
1860 end;
1862 WAD.Free();
1864 MapReader := TMapReader_1.Create();
1866 if not MapReader.LoadMap(Data) then
1867 begin
1868 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
1869 ZeroMemory(@Header, SizeOf(Header));
1870 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
1871 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
1872 end
1873 else
1874 begin
1875 Header := MapReader.GetMapHeader();
1876 Result.Name := Header.MapName;
1877 Result.Description := Header.MapDescription;
1878 end;
1880 FreeMem(Data);
1881 MapReader.Free();
1883 Result.Map := Res;
1884 Result.Author := Header.MapAuthor;
1885 Result.Height := Header.Height;
1886 Result.Width := Header.Width;
1887 end;
1889 function g_Map_GetMapsList(WADName: string): SArray;
1890 var
1891 WAD: TWADFile;
1892 a: Integer;
1893 ResList: SArray;
1894 begin
1895 Result := nil;
1896 WAD := TWADFile.Create();
1897 if not WAD.ReadFile(WADName) then
1898 begin
1899 WAD.Free();
1900 Exit;
1901 end;
1902 ResList := WAD.GetMapResources();
1903 if ResList <> nil then
1904 begin
1905 for a := 0 to High(ResList) do
1906 begin
1907 SetLength(Result, Length(Result)+1);
1908 Result[High(Result)] := ResList[a];
1909 end;
1910 end;
1911 WAD.Free();
1912 end;
1914 function g_Map_Exist(Res: string): Boolean;
1915 var
1916 WAD: TWADFile;
1917 FileName, mnn: string;
1918 ResList: SArray;
1919 a: Integer;
1920 begin
1921 Result := False;
1923 FileName := addWadExtension(g_ExtractWadName(Res));
1925 WAD := TWADFile.Create;
1926 if not WAD.ReadFile(FileName) then
1927 begin
1928 WAD.Free();
1929 Exit;
1930 end;
1932 ResList := WAD.GetMapResources();
1933 WAD.Free();
1935 mnn := g_ExtractFileName(Res);
1936 if ResList <> nil then
1937 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
1938 begin
1939 Result := True;
1940 Exit;
1941 end;
1942 end;
1944 procedure g_Map_Free();
1945 var
1946 a: Integer;
1948 procedure FreePanelArray(var panels: TPanelArray);
1949 var
1950 i: Integer;
1952 begin
1953 if panels <> nil then
1954 begin
1955 for i := 0 to High(panels) do
1956 panels[i].Free();
1957 panels := nil;
1958 end;
1959 end;
1961 begin
1962 g_GFX_Free();
1963 g_Weapon_Free();
1964 g_Items_Free();
1965 g_Triggers_Free();
1966 g_Monsters_Free();
1968 RespawnPoints := nil;
1969 if FlagPoints[FLAG_RED] <> nil then
1970 begin
1971 Dispose(FlagPoints[FLAG_RED]);
1972 FlagPoints[FLAG_RED] := nil;
1973 end;
1974 if FlagPoints[FLAG_BLUE] <> nil then
1975 begin
1976 Dispose(FlagPoints[FLAG_BLUE]);
1977 FlagPoints[FLAG_BLUE] := nil;
1978 end;
1979 //DOMFlagPoints := nil;
1981 //gDOMFlags := nil;
1983 if Textures <> nil then
1984 begin
1985 for a := 0 to High(Textures) do
1986 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
1987 if Textures[a].Anim then
1988 g_Frames_DeleteByID(Textures[a].FramesID)
1989 else
1990 if Textures[a].TextureID <> TEXTURE_NONE then
1991 e_DeleteTexture(Textures[a].TextureID);
1993 Textures := nil;
1994 end;
1996 FreePanelArray(gWalls);
1997 FreePanelArray(gRenderBackgrounds);
1998 FreePanelArray(gRenderForegrounds);
1999 FreePanelArray(gWater);
2000 FreePanelArray(gAcid1);
2001 FreePanelArray(gAcid2);
2002 FreePanelArray(gSteps);
2003 FreePanelArray(gLifts);
2004 FreePanelArray(gBlockMon);
2006 if BackID <> DWORD(-1) then
2007 begin
2008 gBackSize.X := 0;
2009 gBackSize.Y := 0;
2010 e_DeleteTexture(BackID);
2011 BackID := DWORD(-1);
2012 end;
2014 g_Game_StopAllSounds(False);
2015 gMusic.FreeSound();
2016 g_Sound_Delete(gMapInfo.MusicName);
2018 gMapInfo.Name := '';
2019 gMapInfo.Description := '';
2020 gMapInfo.MusicName := '';
2021 gMapInfo.Height := 0;
2022 gMapInfo.Width := 0;
2024 gDoorMap := nil;
2025 gLiftMap := nil;
2027 PanelByID := nil;
2028 end;
2030 procedure g_Map_Update();
2031 var
2032 a, d, j: Integer;
2033 m: Word;
2034 s: String;
2036 procedure UpdatePanelArray(var panels: TPanelArray);
2037 var
2038 i: Integer;
2040 begin
2041 if panels <> nil then
2042 for i := 0 to High(panels) do
2043 panels[i].Update();
2044 end;
2046 begin
2047 UpdatePanelArray(gWalls);
2048 UpdatePanelArray(gRenderBackgrounds);
2049 UpdatePanelArray(gRenderForegrounds);
2050 UpdatePanelArray(gWater);
2051 UpdatePanelArray(gAcid1);
2052 UpdatePanelArray(gAcid2);
2053 UpdatePanelArray(gSteps);
2055 if gGameSettings.GameMode = GM_CTF then
2056 begin
2057 for a := FLAG_RED to FLAG_BLUE do
2058 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
2059 with gFlags[a] do
2060 begin
2061 if gFlags[a].Animation <> nil then
2062 gFlags[a].Animation.Update();
2064 m := g_Obj_Move(@Obj, True, True);
2066 if gTime mod (GAME_TICK*2) <> 0 then
2067 Continue;
2069 // Ñîïðîòèâëåíèå âîçäóõà:
2070 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
2072 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
2073 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
2074 begin
2075 g_Map_ResetFlag(a);
2076 gFlags[a].CaptureTime := 0;
2077 if a = FLAG_RED then
2078 s := _lc[I_PLAYER_FLAG_RED]
2079 else
2080 s := _lc[I_PLAYER_FLAG_BLUE];
2081 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
2083 if g_Game_IsNet then
2084 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
2085 Continue;
2086 end;
2088 if Count > 0 then
2089 Count := Count - 1;
2091 // Èãðîê áåðåò ôëàã:
2092 if gPlayers <> nil then
2093 begin
2094 j := Random(Length(gPlayers)) - 1;
2096 for d := 0 to High(gPlayers) do
2097 begin
2098 Inc(j);
2099 if j > High(gPlayers) then
2100 j := 0;
2102 if gPlayers[j] <> nil then
2103 if gPlayers[j].Live and
2104 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
2105 begin
2106 if gPlayers[j].GetFlag(a) then
2107 Break;
2108 end;
2109 end;
2110 end;
2111 end;
2112 end;
2113 end;
2116 // old algo
2117 procedure g_Map_DrawPanels (PanelType: Word);
2119 procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False);
2120 var
2121 idx: Integer;
2122 begin
2123 if (panels <> nil) then
2124 begin
2125 // alas, no visible set
2126 for idx := 0 to High(panels) do
2127 begin
2128 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
2129 end;
2130 end;
2131 end;
2133 begin
2134 case PanelType of
2135 PANEL_WALL: DrawPanels(gWalls);
2136 PANEL_CLOSEDOOR: DrawPanels(gWalls, True);
2137 PANEL_BACK: DrawPanels(gRenderBackgrounds);
2138 PANEL_FORE: DrawPanels(gRenderForegrounds);
2139 PANEL_WATER: DrawPanels(gWater);
2140 PANEL_ACID1: DrawPanels(gAcid1);
2141 PANEL_ACID2: DrawPanels(gAcid2);
2142 PANEL_STEP: DrawPanels(gSteps);
2143 end;
2144 end;
2147 // new algo
2148 procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer);
2149 function checker (pan: TPanel; tag: Integer): Boolean;
2150 begin
2151 result := false; // don't stop, ever
2152 if ((tag and GridTagDoor) <> 0) <> pan.Door then exit;
2153 gDrawPanelList.insert(pan);
2154 end;
2156 begin
2157 dplClear();
2158 //tagmask := panelTypeToTag(PanelType);
2160 {if gdbg_map_use_tree_draw then
2161 begin
2162 mapTree.aabbQuery(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore));
2163 end
2164 else}
2165 begin
2166 gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore));
2167 end;
2168 // list will be rendered in `g_game.DrawPlayer()`
2169 end;
2172 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
2173 function checker (pan: TPanel; tag: Integer): Boolean;
2174 begin
2175 result := false; // don't stop, ever
2176 pan.DrawShadowVolume(lightX, lightY, radius);
2177 end;
2179 begin
2180 {if gdbg_map_use_tree_draw then
2181 begin
2182 mapTree.aabbQuery(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2183 end
2184 else}
2185 begin
2186 gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
2187 end;
2188 end;
2191 procedure g_Map_DrawBack(dx, dy: Integer);
2192 begin
2193 if gDrawBackGround and (BackID <> DWORD(-1)) then
2194 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
2195 else
2196 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
2197 end;
2199 function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
2200 PanelType: Word; b1x3: Boolean=false): Boolean;
2201 var
2202 a, h: Integer;
2203 begin
2204 Result := False;
2206 if WordBool(PanelType and PANEL_WALL) then
2207 if gWalls <> nil then
2208 begin
2209 h := High(gWalls);
2211 for a := 0 to h do
2212 if gWalls[a].Enabled and
2213 g_Collide(X, Y, Width, Height,
2214 gWalls[a].X, gWalls[a].Y,
2215 gWalls[a].Width, gWalls[a].Height) then
2216 begin
2217 Result := True;
2218 Exit;
2219 end;
2220 end;
2222 if WordBool(PanelType and PANEL_WATER) then
2223 if gWater <> nil then
2224 begin
2225 h := High(gWater);
2227 for a := 0 to h do
2228 if g_Collide(X, Y, Width, Height,
2229 gWater[a].X, gWater[a].Y,
2230 gWater[a].Width, gWater[a].Height) then
2231 begin
2232 Result := True;
2233 Exit;
2234 end;
2235 end;
2237 if WordBool(PanelType and PANEL_ACID1) then
2238 if gAcid1 <> nil then
2239 begin
2240 h := High(gAcid1);
2242 for a := 0 to h do
2243 if g_Collide(X, Y, Width, Height,
2244 gAcid1[a].X, gAcid1[a].Y,
2245 gAcid1[a].Width, gAcid1[a].Height) then
2246 begin
2247 Result := True;
2248 Exit;
2249 end;
2250 end;
2252 if WordBool(PanelType and PANEL_ACID2) then
2253 if gAcid2 <> nil then
2254 begin
2255 h := High(gAcid2);
2257 for a := 0 to h do
2258 if g_Collide(X, Y, Width, Height,
2259 gAcid2[a].X, gAcid2[a].Y,
2260 gAcid2[a].Width, gAcid2[a].Height) then
2261 begin
2262 Result := True;
2263 Exit;
2264 end;
2265 end;
2267 if WordBool(PanelType and PANEL_STEP) then
2268 if gSteps <> nil then
2269 begin
2270 h := High(gSteps);
2272 for a := 0 to h do
2273 if g_Collide(X, Y, Width, Height,
2274 gSteps[a].X, gSteps[a].Y,
2275 gSteps[a].Width, gSteps[a].Height) then
2276 begin
2277 Result := True;
2278 Exit;
2279 end;
2280 end;
2282 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
2283 if gLifts <> nil then
2284 begin
2285 h := High(gLifts);
2287 for a := 0 to h do
2288 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
2289 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
2290 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
2291 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
2292 g_Collide(X, Y, Width, Height,
2293 gLifts[a].X, gLifts[a].Y,
2294 gLifts[a].Width, gLifts[a].Height) then
2295 begin
2296 Result := True;
2297 Exit;
2298 end;
2299 end;
2301 if WordBool(PanelType and PANEL_BLOCKMON) then
2302 if gBlockMon <> nil then
2303 begin
2304 h := High(gBlockMon);
2306 for a := 0 to h do
2307 if ( (not b1x3) or
2308 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
2309 g_Collide(X, Y, Width, Height,
2310 gBlockMon[a].X, gBlockMon[a].Y,
2311 gBlockMon[a].Width, gBlockMon[a].Height) then
2312 begin
2313 Result := True;
2314 Exit;
2315 end;
2316 end;
2317 end;
2319 function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD;
2320 var
2321 texid: DWORD;
2323 function checkPanels (constref panels: TPanelArray): Boolean;
2324 var
2325 a: Integer;
2326 begin
2327 result := false;
2328 if panels = nil then exit;
2329 for a := 0 to High(panels) do
2330 begin
2331 if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then
2332 begin
2333 result := true;
2334 texid := panels[a].GetTextureID();
2335 exit;
2336 end;
2337 end;
2338 end;
2340 begin
2341 texid := TEXTURE_NONE;
2342 result := texid;
2343 if not checkPanels(gWater) then
2344 if not checkPanels(gAcid1) then
2345 if not checkPanels(gAcid2) then exit;
2346 result := texid;
2347 end;
2350 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
2351 function checker (pan: TPanel; tag: Integer): Boolean;
2352 begin
2353 result := false; // don't stop, ever
2355 if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
2356 begin
2357 if not pan.Enabled then exit;
2358 end;
2360 if ((tag and GridTagLift) <> 0) then
2361 begin
2362 result :=
2363 ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
2364 (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
2365 (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
2366 (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) and
2367 g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2368 exit;
2369 end;
2371 if ((tag and GridTagBlockMon) <> 0) then
2372 begin
2373 result := ((not b1x3) or (pan.Width+pan.Height >= 64)) and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2374 exit;
2375 end;
2377 // other shit
2378 result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height);
2379 end;
2381 var
2382 tagmask: Integer = 0;
2383 begin
2384 if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor);
2385 if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater;
2386 if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1;
2387 if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2;
2388 if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep;
2389 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift;
2390 if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon;
2392 if (tagmask = 0) then begin result := false; exit; end; // just in case
2394 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids');
2395 if gdbg_map_use_accel_coldet then
2396 begin
2397 {if gdbg_map_use_tree_coldet then
2398 begin
2399 //e_WriteLog(Format('coldet query: x=%d; y=%d; w=%d; h=%d', [X, Y, Width, Height]), MSG_NOTIFY);
2400 result := (mapTree.aabbQuery(X, Y, Width, Height, checker, tagmask) <> nil);
2401 if (gdbg_map_dump_coldet_tree_queries) and (mapTree.nodesVisited <> 0) then
2402 begin
2403 //e_WriteLog(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]), MSG_NOTIFY);
2404 g_Console_Add(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]));
2405 end;
2406 end
2407 else}
2408 begin
2409 result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask);
2410 end;
2411 end
2412 else
2413 begin
2414 result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3);
2415 end;
2416 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2417 end;
2420 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
2421 var
2422 cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit
2423 texid: DWORD;
2425 // slightly different from the old code, but meh...
2426 function checker (pan: TPanel; tag: Integer): Boolean;
2427 begin
2428 result := false; // don't stop, ever
2429 //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit;
2430 // check priorities
2431 case cctype of
2432 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water
2433 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1
2434 //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2
2435 end;
2436 // collision?
2437 if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit;
2438 // yeah
2439 texid := pan.GetTextureID();
2440 // water? water has the highest priority, so stop right here
2441 if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end;
2442 // acid2?
2443 if ((tag and GridTagAcid2) <> 0) then cctype := 2;
2444 // acid1?
2445 if ((tag and GridTagAcid1) <> 0) then cctype := 1;
2446 end;
2448 begin
2449 if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids');
2450 if gdbg_map_use_accel_coldet then
2451 begin
2452 texid := TEXTURE_NONE;
2453 {if gdbg_map_use_tree_coldet then
2454 begin
2455 mapTree.aabbQuery(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2456 end
2457 else}
2458 begin
2459 gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
2460 end;
2461 result := texid;
2462 end
2463 else
2464 begin
2465 result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height);
2466 end;
2467 if (profMapCollision <> nil) then profMapCollision.sectionEnd();
2468 end;
2470 procedure g_Map_EnableWall(ID: DWORD);
2471 begin
2472 with gWalls[ID] do
2473 begin
2474 Enabled := True;
2475 //g_Mark(X, Y, Width, Height, MARK_DOOR, True);
2477 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2478 end;
2479 end;
2481 procedure g_Map_DisableWall(ID: DWORD);
2482 begin
2483 with gWalls[ID] do
2484 begin
2485 Enabled := False;
2486 //g_Mark(X, Y, Width, Height, MARK_DOOR, False);
2488 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2489 end;
2490 end;
2492 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2493 var
2494 tp: TPanel;
2495 begin
2496 case PanelType of
2497 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2498 tp := gWalls[ID];
2499 PANEL_FORE:
2500 tp := gRenderForegrounds[ID];
2501 PANEL_BACK:
2502 tp := gRenderBackgrounds[ID];
2503 PANEL_WATER:
2504 tp := gWater[ID];
2505 PANEL_ACID1:
2506 tp := gAcid1[ID];
2507 PANEL_ACID2:
2508 tp := gAcid2[ID];
2509 PANEL_STEP:
2510 tp := gSteps[ID];
2511 else
2512 Exit;
2513 end;
2515 tp.NextTexture(AnimLoop);
2516 if g_Game_IsServer and g_Game_IsNet then
2517 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2518 end;
2520 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2521 begin
2522 if gLifts[ID].LiftType = t then
2523 Exit;
2525 with gLifts[ID] do
2526 begin
2527 LiftType := t;
2530 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2532 if LiftType = 0 then
2533 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2534 else if LiftType = 1 then
2535 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2536 else if LiftType = 2 then
2537 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2538 else if LiftType = 3 then
2539 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2542 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2543 end;
2544 end;
2546 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2547 var
2548 a: Integer;
2549 PointsArray: Array of TRespawnPoint;
2550 begin
2551 Result := False;
2552 SetLength(PointsArray, 0);
2554 if RespawnPoints = nil then
2555 Exit;
2557 for a := 0 to High(RespawnPoints) do
2558 if RespawnPoints[a].PointType = PointType then
2559 begin
2560 SetLength(PointsArray, Length(PointsArray)+1);
2561 PointsArray[High(PointsArray)] := RespawnPoints[a];
2562 end;
2564 if PointsArray = nil then
2565 Exit;
2567 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2568 Result := True;
2569 end;
2571 function g_Map_GetPointCount(PointType: Byte): Word;
2572 var
2573 a: Integer;
2574 begin
2575 Result := 0;
2577 if RespawnPoints = nil then
2578 Exit;
2580 for a := 0 to High(RespawnPoints) do
2581 if RespawnPoints[a].PointType = PointType then
2582 Result := Result + 1;
2583 end;
2585 function g_Map_HaveFlagPoints(): Boolean;
2586 begin
2587 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2588 end;
2590 procedure g_Map_ResetFlag(Flag: Byte);
2591 begin
2592 with gFlags[Flag] do
2593 begin
2594 Obj.X := -1000;
2595 Obj.Y := -1000;
2596 Obj.Vel.X := 0;
2597 Obj.Vel.Y := 0;
2598 Direction := D_LEFT;
2599 State := FLAG_STATE_NONE;
2600 if FlagPoints[Flag] <> nil then
2601 begin
2602 Obj.X := FlagPoints[Flag]^.X;
2603 Obj.Y := FlagPoints[Flag]^.Y;
2604 Direction := FlagPoints[Flag]^.Direction;
2605 State := FLAG_STATE_NORMAL;
2606 end;
2607 Count := -1;
2608 end;
2609 end;
2611 procedure g_Map_DrawFlags();
2612 var
2613 i, dx: Integer;
2614 Mirror: TMirrorType;
2615 begin
2616 if gGameSettings.GameMode <> GM_CTF then
2617 Exit;
2619 for i := FLAG_RED to FLAG_BLUE do
2620 with gFlags[i] do
2621 if State <> FLAG_STATE_CAPTURED then
2622 begin
2623 if State = FLAG_STATE_NONE then
2624 continue;
2626 if Direction = D_LEFT then
2627 begin
2628 Mirror := M_HORIZONTAL;
2629 dx := -1;
2630 end
2631 else
2632 begin
2633 Mirror := M_NONE;
2634 dx := 1;
2635 end;
2637 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2639 if g_debug_Frames then
2640 begin
2641 e_DrawQuad(Obj.X+Obj.Rect.X,
2642 Obj.Y+Obj.Rect.Y,
2643 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2644 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2645 0, 255, 0);
2646 end;
2647 end;
2648 end;
2650 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2651 var
2652 dw: DWORD;
2653 b: Byte;
2654 str: String;
2655 boo: Boolean;
2657 procedure SavePanelArray(var panels: TPanelArray);
2658 var
2659 PAMem: TBinMemoryWriter;
2660 i: Integer;
2661 begin
2662 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2663 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2665 i := 0;
2666 while i < Length(panels) do
2667 begin
2668 if panels[i].SaveIt then
2669 begin
2670 // ID ïàíåëè:
2671 PAMem.WriteInt(i);
2672 // Ñîõðàíÿåì ïàíåëü:
2673 panels[i].SaveState(PAMem);
2674 end;
2675 Inc(i);
2676 end;
2678 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2679 PAMem.SaveToMemory(Mem);
2680 PAMem.Free();
2681 end;
2683 procedure SaveFlag(flag: PFlag);
2684 begin
2685 // Ñèãíàòóðà ôëàãà:
2686 dw := FLAG_SIGNATURE; // 'FLAG'
2687 Mem.WriteDWORD(dw);
2688 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2689 Mem.WriteByte(flag^.RespawnType);
2690 // Ñîñòîÿíèå ôëàãà:
2691 Mem.WriteByte(flag^.State);
2692 // Íàïðàâëåíèå ôëàãà:
2693 if flag^.Direction = D_LEFT then
2694 b := 1
2695 else // D_RIGHT
2696 b := 2;
2697 Mem.WriteByte(b);
2698 // Îáúåêò ôëàãà:
2699 Obj_SaveState(@flag^.Obj, Mem);
2700 end;
2702 begin
2703 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2705 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2706 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2707 SavePanelArray(gWalls);
2708 // Ñîõðàíÿåì ïàíåëè ôîíà:
2709 SavePanelArray(gRenderBackgrounds);
2710 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2711 SavePanelArray(gRenderForegrounds);
2712 // Ñîõðàíÿåì ïàíåëè âîäû:
2713 SavePanelArray(gWater);
2714 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2715 SavePanelArray(gAcid1);
2716 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2717 SavePanelArray(gAcid2);
2718 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2719 SavePanelArray(gSteps);
2720 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2721 SavePanelArray(gLifts);
2722 ///// /////
2724 ///// Ñîõðàíÿåì ìóçûêó: /////
2725 // Ñèãíàòóðà ìóçûêè:
2726 dw := MUSIC_SIGNATURE; // 'MUSI'
2727 Mem.WriteDWORD(dw);
2728 // Íàçâàíèå ìóçûêè:
2729 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2730 if gMusic.NoMusic then
2731 str := ''
2732 else
2733 str := gMusic.Name;
2734 Mem.WriteString(str, 64);
2735 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2736 dw := gMusic.GetPosition();
2737 Mem.WriteDWORD(dw);
2738 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2739 boo := gMusic.SpecPause;
2740 Mem.WriteBoolean(boo);
2741 ///// /////
2743 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2744 Mem.WriteInt(gTotalMonsters);
2745 ///// /////
2747 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2748 if gGameSettings.GameMode = GM_CTF then
2749 begin
2750 // Ôëàã Êðàñíîé êîìàíäû:
2751 SaveFlag(@gFlags[FLAG_RED]);
2752 // Ôëàã Ñèíåé êîìàíäû:
2753 SaveFlag(@gFlags[FLAG_BLUE]);
2754 end;
2755 ///// /////
2757 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2758 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2759 begin
2760 // Î÷êè Êðàñíîé êîìàíäû:
2761 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2762 // Î÷êè Ñèíåé êîìàíäû:
2763 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2764 end;
2765 ///// /////
2766 end;
2768 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2769 var
2770 dw: DWORD;
2771 b: Byte;
2772 str: String;
2773 boo: Boolean;
2775 procedure LoadPanelArray(var panels: TPanelArray);
2776 var
2777 PAMem: TBinMemoryReader;
2778 i, id: Integer;
2779 begin
2780 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
2781 PAMem := TBinMemoryReader.Create();
2782 PAMem.LoadFromMemory(Mem);
2784 for i := 0 to Length(panels)-1 do
2785 if panels[i].SaveIt then
2786 begin
2787 // ID ïàíåëè:
2788 PAMem.ReadInt(id);
2789 if id <> i then
2790 begin
2791 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
2792 end;
2793 // Çàãðóæàåì ïàíåëü:
2794 panels[i].LoadState(PAMem);
2795 end;
2797 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
2798 PAMem.Free();
2799 end;
2801 procedure LoadFlag(flag: PFlag);
2802 begin
2803 // Ñèãíàòóðà ôëàãà:
2804 Mem.ReadDWORD(dw);
2805 if dw <> FLAG_SIGNATURE then // 'FLAG'
2806 begin
2807 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
2808 end;
2809 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2810 Mem.ReadByte(flag^.RespawnType);
2811 // Ñîñòîÿíèå ôëàãà:
2812 Mem.ReadByte(flag^.State);
2813 // Íàïðàâëåíèå ôëàãà:
2814 Mem.ReadByte(b);
2815 if b = 1 then
2816 flag^.Direction := D_LEFT
2817 else // b = 2
2818 flag^.Direction := D_RIGHT;
2819 // Îáúåêò ôëàãà:
2820 Obj_LoadState(@flag^.Obj, Mem);
2821 end;
2823 begin
2824 if Mem = nil then
2825 Exit;
2827 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
2828 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
2829 LoadPanelArray(gWalls);
2830 // Çàãðóæàåì ïàíåëè ôîíà:
2831 LoadPanelArray(gRenderBackgrounds);
2832 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
2833 LoadPanelArray(gRenderForegrounds);
2834 // Çàãðóæàåì ïàíåëè âîäû:
2835 LoadPanelArray(gWater);
2836 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
2837 LoadPanelArray(gAcid1);
2838 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
2839 LoadPanelArray(gAcid2);
2840 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
2841 LoadPanelArray(gSteps);
2842 // Çàãðóæàåì ïàíåëè ëèôòîâ:
2843 LoadPanelArray(gLifts);
2844 ///// /////
2846 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
2847 g_GFX_Init();
2848 mapCreateGrid();
2850 ///// Çàãðóæàåì ìóçûêó: /////
2851 // Ñèãíàòóðà ìóçûêè:
2852 Mem.ReadDWORD(dw);
2853 if dw <> MUSIC_SIGNATURE then // 'MUSI'
2854 begin
2855 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
2856 end;
2857 // Íàçâàíèå ìóçûêè:
2858 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
2859 Mem.ReadString(str);
2860 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2861 Mem.ReadDWORD(dw);
2862 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2863 Mem.ReadBoolean(boo);
2864 // Çàïóñêàåì ýòó ìóçûêó:
2865 gMusic.SetByName(str);
2866 gMusic.SpecPause := boo;
2867 gMusic.Play();
2868 gMusic.Pause(True);
2869 gMusic.SetPosition(dw);
2870 ///// /////
2872 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
2873 Mem.ReadInt(gTotalMonsters);
2874 ///// /////
2876 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
2877 if gGameSettings.GameMode = GM_CTF then
2878 begin
2879 // Ôëàã Êðàñíîé êîìàíäû:
2880 LoadFlag(@gFlags[FLAG_RED]);
2881 // Ôëàã Ñèíåé êîìàíäû:
2882 LoadFlag(@gFlags[FLAG_BLUE]);
2883 end;
2884 ///// /////
2886 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2887 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2888 begin
2889 // Î÷êè Êðàñíîé êîìàíäû:
2890 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
2891 // Î÷êè Ñèíåé êîìàíäû:
2892 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
2893 end;
2894 ///// /////
2895 end;
2897 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
2898 var
2899 Arr: TPanelArray;
2900 begin
2901 Result := nil;
2902 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
2903 Arr := PanelByID[PanelID].PWhere^;
2904 PanelArrayID := PanelByID[PanelID].PArrID;
2905 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
2906 end;
2908 end.