DEADSOFTWARE

optimized light drawing: process only "possible lit panels" instead of all walls
[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 {$MODE DELPHI}
17 unit g_map;
19 interface
21 uses
22 e_graphics, g_basic, MAPSTRUCT, g_textures, Classes,
23 g_phys, wadreader, BinEditor, g_panel, md5;
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 // build "potentially visible panels" set, so we can avoid looping over all level panels again and again
66 procedure g_Map_BuildPVP (minx, miny, maxx, maxy: Integer);
67 procedure g_Map_ResetPVP ();
68 // do not call this without calling `g_Map_BuildPVP()` or `g_Map_ResetPVP()` first!
69 procedure g_Map_DrawPanels(PanelType: Word);
71 procedure g_Map_DrawBack(dx, dy: Integer);
72 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
73 PanelType: Word; b1x3: Boolean): Boolean;
74 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
75 procedure g_Map_EnableWall(ID: DWORD);
76 procedure g_Map_DisableWall(ID: DWORD);
77 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
78 procedure g_Map_SetLift(ID: DWORD; t: Integer);
79 procedure g_Map_ReAdd_DieTriggers();
80 function g_Map_IsSpecialTexture(Texture: String): Boolean;
82 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
83 function g_Map_GetPointCount(PointType: Byte): Word;
85 function g_Map_HaveFlagPoints(): Boolean;
87 procedure g_Map_ResetFlag(Flag: Byte);
88 procedure g_Map_DrawFlags();
90 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
92 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
93 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
95 // build "possibly lit panels" index, so we can avoid looping over all level panels again and again
96 function g_Map_BuildPLP (ltminx, ltminy, ltmaxx, ltmaxy: Integer): Boolean; // returns `false` if no patels lit
97 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
99 const
100 RESPAWNPOINT_PLAYER1 = 1;
101 RESPAWNPOINT_PLAYER2 = 2;
102 RESPAWNPOINT_DM = 3;
103 RESPAWNPOINT_RED = 4;
104 RESPAWNPOINT_BLUE = 5;
106 FLAG_NONE = 0;
107 FLAG_RED = 1;
108 FLAG_BLUE = 2;
109 FLAG_DOM = 3;
111 FLAG_STATE_NONE = 0;
112 FLAG_STATE_NORMAL = 1;
113 FLAG_STATE_DROPPED = 2;
114 FLAG_STATE_CAPTURED = 3;
115 FLAG_STATE_SCORED = 4; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
116 FLAG_STATE_RETURNED = 5; // Äëÿ ýâåíòîâ ÷åðåç ñåòêó.
118 FLAG_TIME = 720; // 20 seconds
120 SKY_STRETCH: Single = 1.5;
122 var
123 gWalls: TPanelArray;
124 gRenderBackgrounds: TPanelArray;
125 gRenderForegrounds: TPanelArray;
126 gWater, gAcid1, gAcid2: TPanelArray;
127 gSteps: TPanelArray;
128 gLifts: TPanelArray;
129 gBlockMon: TPanelArray;
130 gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag;
131 //gDOMFlags: array of TFlag;
132 gMapInfo: TMapInfo;
133 gBackSize: TPoint;
134 gDoorMap: array of array of DWORD;
135 gLiftMap: array of array of DWORD;
136 gWADHash: TMD5Digest;
137 BackID: DWORD = DWORD(-1);
138 gExternalResources: TStringList;
140 implementation
142 uses
143 g_main, e_log, SysUtils, g_items, g_gfx, g_console,
144 GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
145 g_options, MAPREADER, g_triggers, g_player, MAPDEF,
146 Math, g_monsters, g_saveload, g_language, g_netmsg,
147 utils, sfs,
148 ImagingTypes, Imaging, ImagingUtility,
149 ImagingGif, ImagingNetworkGraphics;
151 const
152 FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
153 MUSIC_SIGNATURE = $4953554D; // 'MUSI'
154 FLAG_SIGNATURE = $47414C46; // 'FLAG'
156 type
157 TPanelID = record
158 PWhere: ^TPanelArray;
159 PArrID: Integer;
160 end;
162 var
163 PanelById: array of TPanelID;
164 Textures: TLevelTextureArray;
165 RespawnPoints: Array of TRespawnPoint;
166 FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
167 //DOMFlagPoints: Array of TFlagPoint;
170 function g_Map_IsSpecialTexture(Texture: String): Boolean;
171 begin
172 Result := (Texture = TEXTURE_NAME_WATER) or
173 (Texture = TEXTURE_NAME_ACID1) or
174 (Texture = TEXTURE_NAME_ACID2);
175 end;
177 procedure CreateDoorMap();
178 var
179 PanelArray: Array of record
180 X, Y: Integer;
181 Width, Height: Word;
182 Active: Boolean;
183 PanelID: DWORD;
184 end;
185 a, b, c, m, i, len: Integer;
186 ok: Boolean;
187 begin
188 if gWalls = nil then
189 Exit;
191 i := 0;
192 len := 128;
193 SetLength(PanelArray, len);
195 for a := 0 to High(gWalls) do
196 if gWalls[a].Door then
197 begin
198 PanelArray[i].X := gWalls[a].X;
199 PanelArray[i].Y := gWalls[a].Y;
200 PanelArray[i].Width := gWalls[a].Width;
201 PanelArray[i].Height := gWalls[a].Height;
202 PanelArray[i].Active := True;
203 PanelArray[i].PanelID := a;
205 i := i + 1;
206 if i = len then
207 begin
208 len := len + 128;
209 SetLength(PanelArray, len);
210 end;
211 end;
213 // Íåò äâåðåé:
214 if i = 0 then
215 begin
216 PanelArray := nil;
217 Exit;
218 end;
220 SetLength(gDoorMap, 0);
222 g_Game_SetLoadingText(_lc[I_LOAD_DOOR_MAP], i-1, False);
224 for a := 0 to i-1 do
225 if PanelArray[a].Active then
226 begin
227 PanelArray[a].Active := False;
228 m := Length(gDoorMap);
229 SetLength(gDoorMap, m+1);
230 SetLength(gDoorMap[m], 1);
231 gDoorMap[m, 0] := PanelArray[a].PanelID;
232 ok := True;
234 while ok do
235 begin
236 ok := False;
238 for b := 0 to i-1 do
239 if PanelArray[b].Active then
240 for c := 0 to High(gDoorMap[m]) do
241 if {((gRenderWalls[PanelArray[b].RenderPanelID].TextureID = gRenderWalls[gDoorMap[m, c]].TextureID) or
242 gRenderWalls[PanelArray[b].RenderPanelID].Hide or gRenderWalls[gDoorMap[m, c]].Hide) and}
243 g_CollideAround(PanelArray[b].X, PanelArray[b].Y,
244 PanelArray[b].Width, PanelArray[b].Height,
245 gWalls[gDoorMap[m, c]].X,
246 gWalls[gDoorMap[m, c]].Y,
247 gWalls[gDoorMap[m, c]].Width,
248 gWalls[gDoorMap[m, c]].Height) then
249 begin
250 PanelArray[b].Active := False;
251 SetLength(gDoorMap[m],
252 Length(gDoorMap[m])+1);
253 gDoorMap[m, High(gDoorMap[m])] := PanelArray[b].PanelID;
254 ok := True;
255 Break;
256 end;
257 end;
259 g_Game_StepLoading();
260 end;
262 PanelArray := nil;
263 end;
265 procedure CreateLiftMap();
266 var
267 PanelArray: Array of record
268 X, Y: Integer;
269 Width, Height: Word;
270 Active: Boolean;
271 end;
272 a, b, c, len, i, j: Integer;
273 ok: Boolean;
274 begin
275 if gLifts = nil then
276 Exit;
278 len := Length(gLifts);
279 SetLength(PanelArray, len);
281 for a := 0 to len-1 do
282 begin
283 PanelArray[a].X := gLifts[a].X;
284 PanelArray[a].Y := gLifts[a].Y;
285 PanelArray[a].Width := gLifts[a].Width;
286 PanelArray[a].Height := gLifts[a].Height;
287 PanelArray[a].Active := True;
288 end;
290 SetLength(gLiftMap, len);
291 i := 0;
293 g_Game_SetLoadingText(_lc[I_LOAD_LIFT_MAP], len-1, False);
295 for a := 0 to len-1 do
296 if PanelArray[a].Active then
297 begin
298 PanelArray[a].Active := False;
299 SetLength(gLiftMap[i], 32);
300 j := 0;
301 gLiftMap[i, j] := a;
302 ok := True;
304 while ok do
305 begin
306 ok := False;
307 for b := 0 to len-1 do
308 if PanelArray[b].Active then
309 for c := 0 to j do
310 if g_CollideAround(PanelArray[b].X,
311 PanelArray[b].Y,
312 PanelArray[b].Width,
313 PanelArray[b].Height,
314 PanelArray[gLiftMap[i, c]].X,
315 PanelArray[gLiftMap[i, c]].Y,
316 PanelArray[gLiftMap[i, c]].Width,
317 PanelArray[gLiftMap[i, c]].Height) then
318 begin
319 PanelArray[b].Active := False;
320 j := j+1;
321 if j > High(gLiftMap[i]) then
322 SetLength(gLiftMap[i],
323 Length(gLiftMap[i])+32);
325 gLiftMap[i, j] := b;
326 ok := True;
328 Break;
329 end;
330 end;
332 SetLength(gLiftMap[i], j+1);
333 i := i+1;
335 g_Game_StepLoading();
336 end;
338 SetLength(gLiftMap, i);
340 PanelArray := nil;
341 end;
343 function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray;
344 CurTex: Integer; sav: Boolean): Integer;
345 var
346 len: Integer;
347 panels: ^TPanelArray;
348 begin
349 Result := -1;
351 case PanelRec.PanelType of
352 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
353 panels := @gWalls;
354 PANEL_BACK:
355 panels := @gRenderBackgrounds;
356 PANEL_FORE:
357 panels := @gRenderForegrounds;
358 PANEL_WATER:
359 panels := @gWater;
360 PANEL_ACID1:
361 panels := @gAcid1;
362 PANEL_ACID2:
363 panels := @gAcid2;
364 PANEL_STEP:
365 panels := @gSteps;
366 PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT:
367 panels := @gLifts;
368 PANEL_BLOCKMON:
369 panels := @gBlockMon;
370 else
371 Exit;
372 end;
374 len := Length(panels^);
375 SetLength(panels^, len + 1);
377 panels^[len] := TPanel.Create(PanelRec, AddTextures,
378 CurTex, Textures);
379 if sav then
380 panels^[len].SaveIt := True;
382 Result := len;
384 len := Length(PanelByID);
385 SetLength(PanelByID, len + 1);
386 PanelByID[len].PWhere := panels;
387 PanelByID[len].PArrID := Result;
388 end;
390 function CreateNullTexture(RecName: String): Integer;
391 begin
392 SetLength(Textures, Length(Textures)+1);
393 result := High(Textures);
395 with Textures[High(Textures)] do
396 begin
397 TextureName := RecName;
398 Width := 1;
399 Height := 1;
400 Anim := False;
401 TextureID := TEXTURE_NONE;
402 end;
403 end;
405 function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
406 var
407 WAD: TWADFile;
408 TextureData: Pointer;
409 WADName, txname: String;
410 a, ResLength: Integer;
411 begin
412 Result := -1;
414 if Textures <> nil then
415 for a := 0 to High(Textures) do
416 if Textures[a].TextureName = RecName then
417 begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
418 Result := a;
419 Exit;
420 end;
422 // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
423 if (RecName = TEXTURE_NAME_WATER) or
424 (RecName = TEXTURE_NAME_ACID1) or
425 (RecName = TEXTURE_NAME_ACID2) then
426 begin
427 SetLength(Textures, Length(Textures)+1);
429 with Textures[High(Textures)] do
430 begin
431 TextureName := RecName;
433 if TextureName = TEXTURE_NAME_WATER then
434 TextureID := TEXTURE_SPECIAL_WATER
435 else
436 if TextureName = TEXTURE_NAME_ACID1 then
437 TextureID := TEXTURE_SPECIAL_ACID1
438 else
439 if TextureName = TEXTURE_NAME_ACID2 then
440 TextureID := TEXTURE_SPECIAL_ACID2;
442 Anim := False;
443 end;
445 result := High(Textures);
446 Exit;
447 end;
449 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
450 WADName := g_ExtractWadName(RecName);
452 WAD := TWADFile.Create();
454 if WADName <> '' then
455 WADName := GameDir+'/wads/'+WADName
456 else
457 WADName := Map;
459 WAD.ReadFile(WADName);
461 txname := RecName;
463 if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
464 begin
465 FreeMem(TextureData);
466 RecName := 'COMMON\ALIEN';
467 end;
470 if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
471 begin
472 SetLength(Textures, Length(Textures)+1);
473 if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
474 Exit;
475 e_GetTextureSize(Textures[High(Textures)].TextureID,
476 @Textures[High(Textures)].Width,
477 @Textures[High(Textures)].Height);
478 FreeMem(TextureData);
479 Textures[High(Textures)].TextureName := {RecName}txname;
480 Textures[High(Textures)].Anim := False;
482 result := High(Textures);
483 end
484 else // Íåò òàêîãî ðåóñðñà â WAD'å
485 begin
486 //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
487 if log then
488 begin
489 e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
490 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
491 end;
492 end;
494 WAD.Free();
495 end;
497 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
498 var
499 WAD: TWADFile;
500 TextureWAD: PChar = nil;
501 TextData: Pointer = nil;
502 TextureData: Pointer = nil;
503 cfg: TConfig = nil;
504 WADName: String;
505 ResLength: Integer;
506 TextureResource: String;
507 _width, _height, _framecount, _speed: Integer;
508 _backanimation: Boolean;
509 //imgfmt: string;
510 ia: TDynImageDataArray = nil;
511 f, c, frdelay, frloop: Integer;
512 begin
513 result := -1;
515 //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
517 // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
518 WADName := g_ExtractWadName(RecName);
520 WAD := TWADFile.Create();
521 try
522 if WADName <> '' then
523 WADName := GameDir+'/wads/'+WADName
524 else
525 WADName := Map;
527 WAD.ReadFile(WADName);
529 if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
530 begin
531 if log then
532 begin
533 e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
534 //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
535 end;
536 exit;
537 end;
539 {TEST
540 if WADName = Map then
541 begin
542 //FreeMem(TextureWAD);
543 if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
544 end;
547 WAD.FreeWAD();
549 if ResLength < 6 then
550 begin
551 e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
552 exit;
553 end;
555 // ýòî ïòèöà? ýòî ñàìîë¸ò?
556 if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
557 (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
558 begin
559 // íåò, ýòî ñóïåðìåí!
560 if not WAD.ReadMemory(TextureWAD, ResLength) then
561 begin
562 e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
563 exit;
564 end;
566 // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
567 if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
568 begin
569 e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
570 exit;
571 end;
573 cfg := TConfig.CreateMem(TextData, ResLength);
575 TextureResource := cfg.ReadStr('', 'resource', '');
576 if TextureResource = '' then
577 begin
578 e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
579 exit;
580 end;
582 _width := cfg.ReadInt('', 'framewidth', 0);
583 _height := cfg.ReadInt('', 'frameheight', 0);
584 _framecount := cfg.ReadInt('', 'framecount', 0);
585 _speed := cfg.ReadInt('', 'waitcount', 0);
586 _backanimation := cfg.ReadBool('', 'backanimation', False);
588 cfg.Free();
589 cfg := nil;
591 // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
592 if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
593 begin
594 e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
595 exit;
596 end;
598 WAD.Free();
599 WAD := nil;
601 SetLength(Textures, Length(Textures)+1);
602 with Textures[High(Textures)] do
603 begin
604 // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
605 if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
606 begin
607 TextureName := RecName;
608 Width := _width;
609 Height := _height;
610 Anim := True;
611 FramesCount := _framecount;
612 Speed := _speed;
613 result := High(Textures);
614 end
615 else
616 begin
617 if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
618 end;
619 end;
620 end
621 else
622 begin
623 // try animated image
625 imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
626 if length(imgfmt) = 0 then
627 begin
628 e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
629 exit;
630 end;
632 GlobalMetadata.ClearMetaItems();
633 GlobalMetadata.ClearMetaItemsForSaving();
634 if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
635 begin
636 e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
637 exit;
638 end;
639 if length(ia) = 0 then
640 begin
641 e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
642 exit;
643 end;
645 WAD.Free();
646 WAD := nil;
648 _width := ia[0].width;
649 _height := ia[0].height;
650 _framecount := length(ia);
651 _speed := 1;
652 _backanimation := false;
653 frdelay := -1;
654 frloop := -666;
655 if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
656 begin
657 //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
658 try
659 f := GlobalMetadata.MetaItems[SMetaFrameDelay];
660 frdelay := f;
661 if f < 0 then f := 0;
662 // rounding ;-)
663 c := f mod 28;
664 if c < 13 then c := 0 else c := 1;
665 f := (f div 28)+c;
666 if f < 1 then f := 1 else if f > 255 then f := 255;
667 _speed := f;
668 except
669 end;
670 end;
671 if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
672 begin
673 //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
674 try
675 f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
676 frloop := f;
677 if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
678 except
679 end;
680 end;
681 //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
682 //for f := 0 to high(ia) do writeln(' frame #', f, ': ', ia[f].width, 'x', ia[f].height);
683 f := ord(_backanimation);
684 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);
686 SetLength(Textures, Length(Textures)+1);
687 // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
688 if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
689 begin
690 Textures[High(Textures)].TextureName := RecName;
691 Textures[High(Textures)].Width := _width;
692 Textures[High(Textures)].Height := _height;
693 Textures[High(Textures)].Anim := True;
694 Textures[High(Textures)].FramesCount := length(ia);
695 Textures[High(Textures)].Speed := _speed;
696 result := High(Textures);
697 //writeln(' CREATED!');
698 end
699 else
700 begin
701 if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
702 end;
703 end;
704 finally
705 for f := 0 to High(ia) do FreeImage(ia[f]);
706 WAD.Free();
707 cfg.Free();
708 if TextureWAD <> nil then FreeMem(TextureWAD);
709 if TextData <> nil then FreeMem(TextData);
710 if TextureData <> nil then FreeMem(TextureData);
711 end;
712 end;
714 procedure CreateItem(Item: TItemRec_1);
715 begin
716 if g_Game_IsClient then Exit;
718 if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) and
719 ByteBool(Item.Options and ITEM_OPTION_ONLYDM) then
720 Exit;
722 g_Items_Create(Item.X, Item.Y, Item.ItemType, ByteBool(Item.Options and ITEM_OPTION_FALL),
723 gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]);
724 end;
726 procedure CreateArea(Area: TAreaRec_1);
727 var
728 a: Integer;
729 id: DWORD;
730 begin
731 case Area.AreaType of
732 AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2,
733 AREA_REDTEAMPOINT, AREA_BLUETEAMPOINT:
734 begin
735 SetLength(RespawnPoints, Length(RespawnPoints)+1);
736 with RespawnPoints[High(RespawnPoints)] do
737 begin
738 X := Area.X;
739 Y := Area.Y;
740 Direction := TDirection(Area.Direction);
742 case Area.AreaType of
743 AREA_DMPOINT: PointType := RESPAWNPOINT_DM;
744 AREA_PLAYERPOINT1: PointType := RESPAWNPOINT_PLAYER1;
745 AREA_PLAYERPOINT2: PointType := RESPAWNPOINT_PLAYER2;
746 AREA_REDTEAMPOINT: PointType := RESPAWNPOINT_RED;
747 AREA_BLUETEAMPOINT: PointType := RESPAWNPOINT_BLUE;
748 end;
749 end;
750 end;
752 AREA_REDFLAG, AREA_BLUEFLAG:
753 begin
754 if Area.AreaType = AREA_REDFLAG then a := FLAG_RED else a := FLAG_BLUE;
756 if FlagPoints[a] <> nil then Exit;
758 New(FlagPoints[a]);
760 with FlagPoints[a]^ do
761 begin
762 X := Area.X-FLAGRECT.X;
763 Y := Area.Y-FLAGRECT.Y;
764 Direction := TDirection(Area.Direction);
765 end;
767 with gFlags[a] do
768 begin
769 case a of
770 FLAG_RED: g_Frames_Get(id, 'FRAMES_FLAG_RED');
771 FLAG_BLUE: g_Frames_Get(id, 'FRAMES_FLAG_BLUE');
772 end;
774 Animation := TAnimation.Create(id, True, 8);
775 Obj.Rect := FLAGRECT;
777 g_Map_ResetFlag(a);
778 end;
779 end;
781 AREA_DOMFLAG:
782 begin
783 {SetLength(DOMFlagPoints, Length(DOMFlagPoints)+1);
784 with DOMFlagPoints[High(DOMFlagPoints)] do
785 begin
786 X := Area.X;
787 Y := Area.Y;
788 Direction := TDirection(Area.Direction);
789 end;
791 g_Map_CreateFlag(DOMFlagPoints[High(DOMFlagPoints)], FLAG_DOM, FLAG_STATE_NORMAL);}
792 end;
793 end;
794 end;
796 procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word);
797 var
798 _trigger: TTrigger;
799 begin
800 if g_Game_IsClient and not (Trigger.TriggerType in [TRIGGER_SOUND, TRIGGER_MUSIC]) then Exit;
802 with _trigger do
803 begin
804 X := Trigger.X;
805 Y := Trigger.Y;
806 Width := Trigger.Width;
807 Height := Trigger.Height;
808 Enabled := ByteBool(Trigger.Enabled);
809 TexturePanel := Trigger.TexturePanel;
810 TexturePanelType := fTexturePanel1Type;
811 ShotPanelType := fTexturePanel2Type;
812 TriggerType := Trigger.TriggerType;
813 ActivateType := Trigger.ActivateType;
814 Keys := Trigger.Keys;
815 Data.Default := Trigger.DATA;
816 end;
818 g_Triggers_Create(_trigger);
819 end;
821 procedure CreateMonster(monster: TMonsterRec_1);
822 var
823 a, i: Integer;
824 begin
825 if g_Game_IsClient then Exit;
827 if (gGameSettings.GameType = GT_SINGLE)
828 or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then
829 begin
830 i := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y,
831 TDirection(monster.Direction));
833 if gTriggers <> nil then
834 for a := 0 to High(gTriggers) do
835 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
836 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
837 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
838 gMonsters[i].AddTrigger(a);
840 if monster.MonsterType <> MONSTER_BARREL then
841 Inc(gTotalMonsters);
842 end;
843 end;
845 procedure g_Map_ReAdd_DieTriggers();
846 var
847 i, a: Integer;
848 begin
849 if g_Game_IsClient then Exit;
851 for i := 0 to High(gMonsters) do
852 if gMonsters[i] <> nil then
853 begin
854 gMonsters[i].ClearTriggers();
856 for a := 0 to High(gTriggers) do
857 if gTriggers[a].TriggerType in [TRIGGER_PRESS,
858 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then
859 if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then
860 gMonsters[i].AddTrigger(a);
861 end;
862 end;
864 function extractWadName(resourceName: string): string;
865 var
866 posN: Integer;
867 begin
868 posN := Pos(':', resourceName);
869 if posN > 0 then
870 Result:= Copy(resourceName, 0, posN-1)
871 else
872 Result := '';
873 end;
875 procedure addResToExternalResList(res: string);
876 begin
877 res := extractWadName(res);
878 if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
879 gExternalResources.Add(res);
880 end;
882 procedure generateExternalResourcesList(mapReader: TMapReader_1);
883 var
884 textures: TTexturesRec1Array;
885 mapHeader: TMapHeaderRec_1;
886 i: integer;
887 resFile: String = '';
888 begin
889 if gExternalResources = nil then
890 gExternalResources := TStringList.Create;
892 gExternalResources.Clear;
893 textures := mapReader.GetTextures();
894 for i := 0 to High(textures) do
895 begin
896 addResToExternalResList(resFile);
897 end;
899 textures := nil;
901 mapHeader := mapReader.GetMapHeader;
903 addResToExternalResList(mapHeader.MusicName);
904 addResToExternalResList(mapHeader.SkyName);
905 end;
907 function g_Map_Load(Res: String): Boolean;
908 const
909 DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
910 DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
911 var
912 WAD: TWADFile;
913 MapReader: TMapReader_1;
914 Header: TMapHeaderRec_1;
915 _textures: TTexturesRec1Array;
916 _texnummap: array of Integer; // `_textures` -> `Textures`
917 panels: TPanelsRec1Array;
918 items: TItemsRec1Array;
919 monsters: TMonsterRec1Array;
920 areas: TAreasRec1Array;
921 triggers: TTriggersRec1Array;
922 a, b, c, k: Integer;
923 PanelID: DWORD;
924 AddTextures: TAddTextureArray;
925 texture: TTextureRec_1;
926 TriggersTable: Array of record
927 TexturePanel: Integer;
928 LiftPanel: Integer;
929 DoorPanel: Integer;
930 ShotPanel: Integer;
931 end;
932 FileName, mapResName, s, TexName: String;
933 Data: Pointer;
934 Len: Integer;
935 ok, isAnim, trigRef: Boolean;
936 CurTex, ntn: Integer;
937 begin
938 Result := False;
939 gMapInfo.Map := Res;
940 TriggersTable := nil;
941 FillChar(texture, SizeOf(texture), 0);
943 sfsGCDisable(); // temporary disable removing of temporary volumes
944 try
945 // Çàãðóçêà WAD:
946 FileName := g_ExtractWadName(Res);
947 e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
948 g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
950 WAD := TWADFile.Create();
951 if not WAD.ReadFile(FileName) then
952 begin
953 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
954 WAD.Free();
955 Exit;
956 end;
957 //k8: why loader ignores path here?
958 mapResName := g_ExtractFileName(Res);
959 if not WAD.GetMapResource(mapResName, Data, Len) then
960 begin
961 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
962 WAD.Free();
963 Exit;
964 end;
966 WAD.Free();
968 // Çàãðóçêà êàðòû:
969 e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
970 g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
971 MapReader := TMapReader_1.Create();
973 if not MapReader.LoadMap(Data) then
974 begin
975 g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
976 FreeMem(Data);
977 MapReader.Free();
978 Exit;
979 end;
981 FreeMem(Data);
982 generateExternalResourcesList(MapReader);
983 // Çàãðóçêà òåêñòóð:
984 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
985 _textures := MapReader.GetTextures();
986 _texnummap := nil;
988 // Äîáàâëåíèå òåêñòóð â Textures[]:
989 if _textures <> nil then
990 begin
991 e_WriteLog(' Loading textures:', MSG_NOTIFY);
992 g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
993 SetLength(_texnummap, length(_textures));
995 for a := 0 to High(_textures) do
996 begin
997 SetLength(s, 64);
998 CopyMemory(@s[1], @_textures[a].Resource[0], 64);
999 for b := 1 to Length(s) do
1000 if s[b] = #0 then
1001 begin
1002 SetLength(s, b-1);
1003 Break;
1004 end;
1005 e_WriteLog(Format(' Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
1006 //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY);
1007 // Àíèìèðîâàííàÿ òåêñòóðà:
1008 if ByteBool(_textures[a].Anim) then
1009 begin
1010 ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
1011 if ntn < 0 then
1012 begin
1013 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
1014 ntn := CreateNullTexture(_textures[a].Resource);
1015 end;
1016 end
1017 else // Îáû÷íàÿ òåêñòóðà:
1018 ntn := CreateTexture(_textures[a].Resource, FileName, True);
1019 if ntn < 0 then
1020 begin
1021 g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
1022 ntn := CreateNullTexture(_textures[a].Resource);
1023 end;
1025 _texnummap[a] := ntn; // fix texture number
1026 g_Game_StepLoading();
1027 end;
1028 end;
1030 // Çàãðóçêà òðèããåðîâ:
1031 gTriggerClientID := 0;
1032 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1033 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
1034 triggers := MapReader.GetTriggers();
1036 // Çàãðóçêà ïàíåëåé:
1037 e_WriteLog(' Loading panels...', MSG_NOTIFY);
1038 g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
1039 panels := MapReader.GetPanels();
1041 // check texture numbers for panels
1042 for a := 0 to High(panels) do
1043 begin
1044 if panels[a].TextureNum > High(_textures) then
1045 begin
1046 e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
1047 result := false;
1048 exit;
1049 end;
1050 panels[a].TextureNum := _texnummap[panels[a].TextureNum];
1051 end;
1053 // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
1054 if triggers <> nil then
1055 begin
1056 e_WriteLog(' Setting up trigger table...', MSG_NOTIFY);
1057 SetLength(TriggersTable, Length(triggers));
1058 g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
1060 for a := 0 to High(TriggersTable) do
1061 begin
1062 // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
1063 TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
1064 // Ëèôòû:
1065 if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
1066 TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
1067 else
1068 TriggersTable[a].LiftPanel := -1;
1069 // Äâåðè:
1070 if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
1071 TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
1072 TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
1073 TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
1074 else
1075 TriggersTable[a].DoorPanel := -1;
1076 // Òóðåëü:
1077 if triggers[a].TriggerType = TRIGGER_SHOT then
1078 TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
1079 else
1080 TriggersTable[a].ShotPanel := -1;
1082 g_Game_StepLoading();
1083 end;
1084 end;
1086 // Ñîçäàåì ïàíåëè:
1087 if panels <> nil then
1088 begin
1089 e_WriteLog(' Setting up trigger links...', MSG_NOTIFY);
1090 g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
1092 for a := 0 to High(panels) do
1093 begin
1094 SetLength(AddTextures, 0);
1095 trigRef := False;
1096 CurTex := -1;
1097 if _textures <> nil then
1098 begin
1099 texture := _textures[panels[a].TextureNum];
1100 ok := True;
1101 end
1102 else
1103 ok := False;
1105 if ok then
1106 begin
1107 // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
1108 // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
1109 ok := False;
1110 if (TriggersTable <> nil) and (_textures <> nil) then
1111 for b := 0 to High(TriggersTable) do
1112 if (TriggersTable[b].TexturePanel = a)
1113 or (TriggersTable[b].ShotPanel = a) then
1114 begin
1115 trigRef := True;
1116 ok := True;
1117 Break;
1118 end;
1119 end;
1121 if ok then
1122 begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
1123 SetLength(s, 64);
1124 CopyMemory(@s[1], @texture.Resource[0], 64);
1125 // Èçìåðÿåì äëèíó:
1126 Len := Length(s);
1127 for c := Len downto 1 do
1128 if s[c] <> #0 then
1129 begin
1130 Len := c;
1131 Break;
1132 end;
1133 SetLength(s, Len);
1135 // Ñïåö-òåêñòóðû çàïðåùåíû:
1136 if g_Map_IsSpecialTexture(s) then
1137 ok := False
1138 else
1139 // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
1140 ok := g_Texture_NumNameFindStart(s);
1142 // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
1143 // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
1144 if ok then
1145 begin
1146 k := NNF_NAME_BEFORE;
1147 // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
1148 while ok or (k = NNF_NAME_BEFORE) or
1149 (k = NNF_NAME_EQUALS) do
1150 begin
1151 k := g_Texture_NumNameFindNext(TexName);
1153 if (k = NNF_NAME_BEFORE) or
1154 (k = NNF_NAME_AFTER) then
1155 begin
1156 // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
1157 if ByteBool(texture.Anim) then
1158 begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
1159 isAnim := True;
1160 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1161 if not ok then
1162 begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
1163 isAnim := False;
1164 ok := CreateTexture(TexName, FileName, False) >= 0;
1165 end;
1166 end
1167 else
1168 begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
1169 isAnim := False;
1170 ok := CreateTexture(TexName, FileName, False) >= 0;
1171 if not ok then
1172 begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
1173 isAnim := True;
1174 ok := CreateAnimTexture(TexName, FileName, False) >= 0;
1175 end;
1176 end;
1178 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
1179 if ok then
1180 begin
1181 for c := 0 to High(Textures) do
1182 if Textures[c].TextureName = TexName then
1183 begin
1184 SetLength(AddTextures, Length(AddTextures)+1);
1185 AddTextures[High(AddTextures)].Texture := c;
1186 AddTextures[High(AddTextures)].Anim := isAnim;
1187 Break;
1188 end;
1189 end;
1190 end
1191 else
1192 if k = NNF_NAME_EQUALS then
1193 begin
1194 // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
1195 SetLength(AddTextures, Length(AddTextures)+1);
1196 AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
1197 AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
1198 CurTex := High(AddTextures);
1199 ok := True;
1200 end
1201 else // NNF_NO_NAME
1202 ok := False;
1203 end; // while ok...
1205 ok := True;
1206 end; // if ok - åñòü ñìåæíûå òåêñòóðû
1207 end; // if ok - ññûëàþòñÿ òðèããåðû
1209 if not ok then
1210 begin
1211 // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
1212 SetLength(AddTextures, 1);
1213 AddTextures[0].Texture := panels[a].TextureNum;
1214 AddTextures[0].Anim := ByteBool(texture.Anim);
1215 CurTex := 0;
1216 end;
1218 //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);
1220 // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
1221 PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
1223 // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
1224 if TriggersTable <> nil then
1225 for b := 0 to High(TriggersTable) do
1226 begin
1227 // Òðèããåð äâåðè/ëèôòà:
1228 if (TriggersTable[b].LiftPanel = a) or
1229 (TriggersTable[b].DoorPanel = a) then
1230 TTriggerData(triggers[b].DATA).PanelID := PanelID;
1231 // Òðèããåð ñìåíû òåêñòóðû:
1232 if TriggersTable[b].TexturePanel = a then
1233 triggers[b].TexturePanel := PanelID;
1234 // Òðèããåð "Òóðåëü":
1235 if TriggersTable[b].ShotPanel = a then
1236 TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
1237 end;
1239 g_Game_StepLoading();
1240 end;
1241 end;
1243 // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
1244 if (triggers <> nil) and not gLoadGameMode then
1245 begin
1246 e_WriteLog(' Creating triggers...', MSG_NOTIFY);
1247 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
1248 // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
1249 for a := 0 to High(triggers) do
1250 begin
1251 if triggers[a].TexturePanel <> -1 then
1252 b := panels[TriggersTable[a].TexturePanel].PanelType
1253 else
1254 b := 0;
1255 if (triggers[a].TriggerType = TRIGGER_SHOT) and
1256 (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
1257 c := panels[TriggersTable[a].ShotPanel].PanelType
1258 else
1259 c := 0;
1260 CreateTrigger(triggers[a], b, c);
1261 end;
1262 end;
1264 // Çàãðóçêà ïðåäìåòîâ:
1265 e_WriteLog(' Loading triggers...', MSG_NOTIFY);
1266 g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
1267 items := MapReader.GetItems();
1269 // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
1270 if (items <> nil) and not gLoadGameMode then
1271 begin
1272 e_WriteLog(' Spawning items...', MSG_NOTIFY);
1273 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
1274 for a := 0 to High(items) do
1275 CreateItem(Items[a]);
1276 end;
1278 // Çàãðóçêà îáëàñòåé:
1279 e_WriteLog(' Loading areas...', MSG_NOTIFY);
1280 g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
1281 areas := MapReader.GetAreas();
1283 // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
1284 if areas <> nil then
1285 begin
1286 e_WriteLog(' Creating areas...', MSG_NOTIFY);
1287 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
1288 for a := 0 to High(areas) do
1289 CreateArea(areas[a]);
1290 end;
1292 // Çàãðóçêà ìîíñòðîâ:
1293 e_WriteLog(' Loading monsters...', MSG_NOTIFY);
1294 g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
1295 monsters := MapReader.GetMonsters();
1297 gTotalMonsters := 0;
1299 // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
1300 if (monsters <> nil) and not gLoadGameMode then
1301 begin
1302 e_WriteLog(' Spawning monsters...', MSG_NOTIFY);
1303 g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
1304 for a := 0 to High(monsters) do
1305 CreateMonster(monsters[a]);
1306 end;
1308 // Çàãðóçêà îïèñàíèÿ êàðòû:
1309 e_WriteLog(' Reading map info...', MSG_NOTIFY);
1310 g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
1311 Header := MapReader.GetMapHeader();
1313 MapReader.Free();
1315 with gMapInfo do
1316 begin
1317 Name := Header.MapName;
1318 Description := Header.MapDescription;
1319 Author := Header.MapAuthor;
1320 MusicName := Header.MusicName;
1321 SkyName := Header.SkyName;
1322 Height := Header.Height;
1323 Width := Header.Width;
1324 end;
1326 // Çàãðóçêà íåáà:
1327 if gMapInfo.SkyName <> '' then
1328 begin
1329 e_WriteLog(' Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
1330 g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
1331 FileName := g_ExtractWadName(gMapInfo.SkyName);
1333 if FileName <> '' then
1334 FileName := GameDir+'/wads/'+FileName
1335 else
1336 begin
1337 FileName := g_ExtractWadName(Res);
1338 end;
1340 s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
1341 if g_Texture_CreateWAD(BackID, s) then
1342 begin
1343 g_Game_SetupScreenSize();
1344 end
1345 else
1346 g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
1347 end;
1349 // Çàãðóçêà ìóçûêè:
1350 ok := False;
1351 if gMapInfo.MusicName <> '' then
1352 begin
1353 e_WriteLog(' Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
1354 g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
1355 FileName := g_ExtractWadName(gMapInfo.MusicName);
1357 if FileName <> '' then
1358 FileName := GameDir+'/wads/'+FileName
1359 else
1360 begin
1361 FileName := g_ExtractWadName(Res);
1362 end;
1364 s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
1365 if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
1366 ok := True
1367 else
1368 g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
1369 end;
1371 // Îñòàëüíûå óñòàíâêè:
1372 CreateDoorMap();
1373 CreateLiftMap();
1375 g_Items_Init();
1376 g_Weapon_Init();
1377 g_Monsters_Init();
1379 // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
1380 if not gLoadGameMode then
1381 g_GFX_Init();
1383 // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
1384 _textures := nil;
1385 panels := nil;
1386 items := nil;
1387 areas := nil;
1388 triggers := nil;
1389 TriggersTable := nil;
1390 AddTextures := nil;
1392 // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
1393 if ok and (not gLoadGameMode) then
1394 begin
1395 gMusic.SetByName(gMapInfo.MusicName);
1396 gMusic.Play();
1397 end
1398 else
1399 gMusic.SetByName('');
1400 finally
1401 sfsGCEnable(); // enable releasing unused volumes
1402 end;
1404 e_WriteLog('Done loading map.', MSG_NOTIFY);
1405 Result := True;
1406 end;
1408 function g_Map_GetMapInfo(Res: String): TMapInfo;
1409 var
1410 WAD: TWADFile;
1411 MapReader: TMapReader_1;
1412 Header: TMapHeaderRec_1;
1413 FileName: String;
1414 Data: Pointer;
1415 Len: Integer;
1416 begin
1417 FillChar(Result, SizeOf(Result), 0);
1418 FileName := g_ExtractWadName(Res);
1420 WAD := TWADFile.Create();
1421 if not WAD.ReadFile(FileName) then
1422 begin
1423 WAD.Free();
1424 Exit;
1425 end;
1427 //k8: it ignores path again
1428 if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
1429 begin
1430 WAD.Free();
1431 Exit;
1432 end;
1434 WAD.Free();
1436 MapReader := TMapReader_1.Create();
1438 if not MapReader.LoadMap(Data) then
1439 begin
1440 g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
1441 ZeroMemory(@Header, SizeOf(Header));
1442 Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
1443 Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
1444 end
1445 else
1446 begin
1447 Header := MapReader.GetMapHeader();
1448 Result.Name := Header.MapName;
1449 Result.Description := Header.MapDescription;
1450 end;
1452 FreeMem(Data);
1453 MapReader.Free();
1455 Result.Map := Res;
1456 Result.Author := Header.MapAuthor;
1457 Result.Height := Header.Height;
1458 Result.Width := Header.Width;
1459 end;
1461 function g_Map_GetMapsList(WADName: string): SArray;
1462 var
1463 WAD: TWADFile;
1464 a: Integer;
1465 ResList: SArray;
1466 begin
1467 Result := nil;
1468 WAD := TWADFile.Create();
1469 if not WAD.ReadFile(WADName) then
1470 begin
1471 WAD.Free();
1472 Exit;
1473 end;
1474 ResList := WAD.GetMapResources();
1475 if ResList <> nil then
1476 begin
1477 for a := 0 to High(ResList) do
1478 begin
1479 SetLength(Result, Length(Result)+1);
1480 Result[High(Result)] := ResList[a];
1481 end;
1482 end;
1483 WAD.Free();
1484 end;
1486 function g_Map_Exist(Res: string): Boolean;
1487 var
1488 WAD: TWADFile;
1489 FileName, mnn: string;
1490 ResList: SArray;
1491 a: Integer;
1492 begin
1493 Result := False;
1495 FileName := addWadExtension(g_ExtractWadName(Res));
1497 WAD := TWADFile.Create;
1498 if not WAD.ReadFile(FileName) then
1499 begin
1500 WAD.Free();
1501 Exit;
1502 end;
1504 ResList := WAD.GetMapResources();
1505 WAD.Free();
1507 mnn := g_ExtractFileName(Res);
1508 if ResList <> nil then
1509 for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
1510 begin
1511 Result := True;
1512 Exit;
1513 end;
1514 end;
1516 procedure g_Map_Free();
1517 var
1518 a: Integer;
1520 procedure FreePanelArray(var panels: TPanelArray);
1521 var
1522 i: Integer;
1524 begin
1525 if panels <> nil then
1526 begin
1527 for i := 0 to High(panels) do
1528 panels[i].Free();
1529 panels := nil;
1530 end;
1531 end;
1533 begin
1534 g_GFX_Free();
1535 g_Weapon_Free();
1536 g_Items_Free();
1537 g_Triggers_Free();
1538 g_Monsters_Free();
1540 RespawnPoints := nil;
1541 if FlagPoints[FLAG_RED] <> nil then
1542 begin
1543 Dispose(FlagPoints[FLAG_RED]);
1544 FlagPoints[FLAG_RED] := nil;
1545 end;
1546 if FlagPoints[FLAG_BLUE] <> nil then
1547 begin
1548 Dispose(FlagPoints[FLAG_BLUE]);
1549 FlagPoints[FLAG_BLUE] := nil;
1550 end;
1551 //DOMFlagPoints := nil;
1553 //gDOMFlags := nil;
1555 if Textures <> nil then
1556 begin
1557 for a := 0 to High(Textures) do
1558 if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
1559 if Textures[a].Anim then
1560 g_Frames_DeleteByID(Textures[a].FramesID)
1561 else
1562 if Textures[a].TextureID <> TEXTURE_NONE then
1563 e_DeleteTexture(Textures[a].TextureID);
1565 Textures := nil;
1566 end;
1568 FreePanelArray(gWalls);
1569 FreePanelArray(gRenderBackgrounds);
1570 FreePanelArray(gRenderForegrounds);
1571 FreePanelArray(gWater);
1572 FreePanelArray(gAcid1);
1573 FreePanelArray(gAcid2);
1574 FreePanelArray(gSteps);
1575 FreePanelArray(gLifts);
1576 FreePanelArray(gBlockMon);
1578 if BackID <> DWORD(-1) then
1579 begin
1580 gBackSize.X := 0;
1581 gBackSize.Y := 0;
1582 e_DeleteTexture(BackID);
1583 BackID := DWORD(-1);
1584 end;
1586 g_Game_StopAllSounds(False);
1587 gMusic.FreeSound();
1588 g_Sound_Delete(gMapInfo.MusicName);
1590 gMapInfo.Name := '';
1591 gMapInfo.Description := '';
1592 gMapInfo.MusicName := '';
1593 gMapInfo.Height := 0;
1594 gMapInfo.Width := 0;
1596 gDoorMap := nil;
1597 gLiftMap := nil;
1599 PanelByID := nil;
1600 end;
1602 procedure g_Map_Update();
1603 var
1604 a, d, j: Integer;
1605 m: Word;
1606 s: String;
1608 procedure UpdatePanelArray(var panels: TPanelArray);
1609 var
1610 i: Integer;
1612 begin
1613 if panels <> nil then
1614 for i := 0 to High(panels) do
1615 panels[i].Update();
1616 end;
1618 begin
1619 UpdatePanelArray(gWalls);
1620 UpdatePanelArray(gRenderBackgrounds);
1621 UpdatePanelArray(gRenderForegrounds);
1622 UpdatePanelArray(gWater);
1623 UpdatePanelArray(gAcid1);
1624 UpdatePanelArray(gAcid2);
1625 UpdatePanelArray(gSteps);
1627 if gGameSettings.GameMode = GM_CTF then
1628 begin
1629 for a := FLAG_RED to FLAG_BLUE do
1630 if not (gFlags[a].State in [FLAG_STATE_NONE, FLAG_STATE_CAPTURED]) then
1631 with gFlags[a] do
1632 begin
1633 if gFlags[a].Animation <> nil then
1634 gFlags[a].Animation.Update();
1636 m := g_Obj_Move(@Obj, True, True);
1638 if gTime mod (GAME_TICK*2) <> 0 then
1639 Continue;
1641 // Ñîïðîòèâëåíèå âîçäóõà:
1642 Obj.Vel.X := z_dec(Obj.Vel.X, 1);
1644 // Òàéìàóò ïîòåðÿííîãî ôëàãà, ëèáî îí âûïàë çà êàðòó:
1645 if ((Count = 0) or ByteBool(m and MOVE_FALLOUT)) and g_Game_IsServer then
1646 begin
1647 g_Map_ResetFlag(a);
1648 gFlags[a].CaptureTime := 0;
1649 if a = FLAG_RED then
1650 s := _lc[I_PLAYER_FLAG_RED]
1651 else
1652 s := _lc[I_PLAYER_FLAG_BLUE];
1653 g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
1655 if g_Game_IsNet then
1656 MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
1657 Continue;
1658 end;
1660 if Count > 0 then
1661 Count := Count - 1;
1663 // Èãðîê áåðåò ôëàã:
1664 if gPlayers <> nil then
1665 begin
1666 j := Random(Length(gPlayers)) - 1;
1668 for d := 0 to High(gPlayers) do
1669 begin
1670 Inc(j);
1671 if j > High(gPlayers) then
1672 j := 0;
1674 if gPlayers[j] <> nil then
1675 if gPlayers[j].Live and
1676 g_Obj_Collide(@Obj, @gPlayers[j].Obj) then
1677 begin
1678 if gPlayers[j].GetFlag(a) then
1679 Break;
1680 end;
1681 end;
1682 end;
1683 end;
1684 end;
1685 end;
1688 var
1689 pvpset: array of PPanel = nil; // potentially lit panels
1690 pvpb, pvpe: array [0..7] of Integer; // start/end (inclusive) of the correspoinding type panels in pvpset
1691 pvpcount: Integer = -1; // to avoid constant reallocations
1693 function pvpType (panelType: Word): Integer;
1694 begin
1695 case panelType of
1696 PANEL_WALL, PANEL_CLOSEDOOR: result := 0; // gWalls
1697 PANEL_BACK: result := 1; // gRenderBackgrounds
1698 PANEL_FORE: result := 2; // gRenderForegrounds
1699 PANEL_WATER: result := 3; // gWater
1700 PANEL_ACID1: result := 4; // gAcid1
1701 PANEL_ACID2: result := 5; // gAcid2
1702 PANEL_STEP: result := 6; // gSteps
1703 else result := -1;
1704 end;
1705 end;
1707 procedure g_Map_ResetPVP ();
1708 begin
1709 pvpcount := -1; // special
1710 end;
1712 procedure g_Map_BuildPVP (minx, miny, maxx, maxy: Integer);
1713 var
1714 idx: Integer;
1715 tpc: Integer;
1717 procedure checkPanels (var panels: TPanelArray; stp: Integer);
1718 var
1719 idx, x, y, w, h: Integer;
1720 begin
1721 if panels = nil then exit;
1722 tpc := tpc+Length(panels);
1723 if (stp < 0) or (stp > 6) then exit;
1724 pvpb[stp] := pvpcount;
1725 for idx := 0 to High(panels) do
1726 begin
1727 w := panels[idx].Width;
1728 h := panels[idx].Height;
1729 if (w < 1) or (h < 1) then continue;
1730 x := panels[idx].X;
1731 y := panels[idx].Y;
1732 if (x > maxx) or (y > maxy) then continue;
1733 if (x+w <= minx) or (y+h <= miny) then continue;
1734 if pvpcount = length(pvpset) then SetLength(pvpset, pvpcount+32768);
1735 pvpset[pvpcount] := @panels[idx];
1736 Inc(pvpcount);
1737 end;
1738 pvpe[stp] := pvpcount-1;
1739 end;
1741 begin
1742 //e_WriteLog(Format('visible rect: (%d,%d)-(%d,%d)', [minx, miny, maxx, maxy]), MSG_NOTIFY);
1743 pvpcount := 0;
1744 for idx := 0 to High(pvpb) do begin pvpb[idx] := 0; pvpe[idx] := -1; end;
1745 tpc := 0;
1746 checkPanels(gWalls, 0);
1747 checkPanels(gRenderBackgrounds, 1);
1748 checkPanels(gRenderForegrounds, 2);
1749 checkPanels(gWater, 3);
1750 checkPanels(gAcid1, 4);
1751 checkPanels(gAcid2, 5);
1752 checkPanels(gSteps, 6);
1753 //e_WriteLog(Format('total panels: %d; visible panels: %d', [tpc, pvpcount]), MSG_NOTIFY);
1754 end;
1756 procedure g_Map_DrawPanels(PanelType: Word);
1758 procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
1759 var
1760 idx: Integer;
1761 begin
1762 if (panels <> nil) and (stp >= 0) and (stp <= 6) then
1763 begin
1764 if pvpcount < 0 then
1765 begin
1766 // alas, no visible set
1767 for idx := 0 to High(panels) do
1768 begin
1769 if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
1770 end;
1771 end
1772 else
1773 begin
1774 // wow, use visible set
1775 if pvpb[stp] <= pvpe[stp] then
1776 begin
1777 for idx := pvpb[stp] to pvpe[stp] do
1778 begin
1779 if not (drawDoors xor pvpset[idx].Door) then pvpset[idx].Draw();
1780 end;
1781 end;
1782 end;
1783 end;
1784 end;
1786 begin
1787 case PanelType of
1788 PANEL_WALL: DrawPanels(0, gWalls);
1789 PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True);
1790 PANEL_BACK: DrawPanels(1, gRenderBackgrounds);
1791 PANEL_FORE: DrawPanels(2, gRenderForegrounds);
1792 PANEL_WATER: DrawPanels(3, gWater);
1793 PANEL_ACID1: DrawPanels(4, gAcid1);
1794 PANEL_ACID2: DrawPanels(5, gAcid2);
1795 PANEL_STEP: DrawPanels(6, gSteps);
1796 end;
1797 end;
1799 var
1800 plpset: array of Integer = nil; // potentially lit panels
1801 plpcount: Integer; // to avoid constant reallocations
1803 function g_Map_BuildPLP (ltminx, ltminy, ltmaxx, ltmaxy: Integer): Boolean;
1804 var
1805 idx: Integer;
1806 panels: TPanelArray;
1807 begin
1808 panels := gWalls;
1809 plpcount := 0;
1810 if (ltminx < ltmaxx) and (ltminy < ltmaxy) then
1811 begin
1812 if panels <> nil then
1813 begin
1814 for idx := 0 to High(panels) do
1815 begin
1816 if (panels[idx].Width < 1) or (panels[idx].Height < 1) then continue;
1817 if (panels[idx].X+panels[idx].Width <= ltminx) then continue;
1818 if (panels[idx].Y+panels[idx].Height <= ltminy) then continue;
1819 if (panels[idx].X > ltmaxx) then continue;
1820 if (panels[idx].Y > ltmaxy) then continue;
1821 if plpcount = length(plpset) then SetLength(plpset, plpcount+32768);
1822 plpset[plpcount] := idx;
1823 Inc(plpcount);
1824 end;
1825 //e_WriteLog(Format('%d panels left out of %d', [plpcount, Length(panels)]), MSG_NOTIFY);
1826 end;
1827 end;
1828 result := (plpcount > 0);
1829 end;
1831 procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
1833 (* old
1834 procedure drawPanels (var panels: TPanelArray);
1835 var
1836 a: Integer;
1837 begin
1838 if panels <> nil then
1839 begin
1840 for a := 0 to High(panels) do
1841 begin
1842 panels[a].DrawShadowVolume(lightX, lightY, radius);
1843 end;
1844 end;
1845 end;
1846 *)
1847 var
1848 idx: Integer;
1849 begin
1850 (*
1851 drawPanels(gWalls);
1852 //drawPanels(gRenderForegrounds);
1853 *)
1854 for idx := 0 to plpcount-1 do
1855 begin
1856 gWalls[plpset[idx]].DrawShadowVolume(lightX, lightY, radius);
1857 end;
1858 end;
1860 procedure g_Map_DrawBack(dx, dy: Integer);
1861 begin
1862 if gDrawBackGround and (BackID <> DWORD(-1)) then
1863 e_DrawSize(BackID, dx, dy, 0, False, False, gBackSize.X, gBackSize.Y)
1864 else
1865 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
1866 end;
1868 function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
1869 PanelType: Word; b1x3: Boolean): Boolean;
1870 var
1871 a, h: Integer;
1872 begin
1873 Result := False;
1875 if WordBool(PanelType and PANEL_WALL) then
1876 if gWalls <> nil then
1877 begin
1878 h := High(gWalls);
1880 for a := 0 to h do
1881 if gWalls[a].Enabled and
1882 g_Collide(X, Y, Width, Height,
1883 gWalls[a].X, gWalls[a].Y,
1884 gWalls[a].Width, gWalls[a].Height) then
1885 begin
1886 Result := True;
1887 Exit;
1888 end;
1889 end;
1891 if WordBool(PanelType and PANEL_WATER) then
1892 if gWater <> nil then
1893 begin
1894 h := High(gWater);
1896 for a := 0 to h do
1897 if g_Collide(X, Y, Width, Height,
1898 gWater[a].X, gWater[a].Y,
1899 gWater[a].Width, gWater[a].Height) then
1900 begin
1901 Result := True;
1902 Exit;
1903 end;
1904 end;
1906 if WordBool(PanelType and PANEL_ACID1) then
1907 if gAcid1 <> nil then
1908 begin
1909 h := High(gAcid1);
1911 for a := 0 to h do
1912 if g_Collide(X, Y, Width, Height,
1913 gAcid1[a].X, gAcid1[a].Y,
1914 gAcid1[a].Width, gAcid1[a].Height) then
1915 begin
1916 Result := True;
1917 Exit;
1918 end;
1919 end;
1921 if WordBool(PanelType and PANEL_ACID2) then
1922 if gAcid2 <> nil then
1923 begin
1924 h := High(gAcid2);
1926 for a := 0 to h do
1927 if g_Collide(X, Y, Width, Height,
1928 gAcid2[a].X, gAcid2[a].Y,
1929 gAcid2[a].Width, gAcid2[a].Height) then
1930 begin
1931 Result := True;
1932 Exit;
1933 end;
1934 end;
1936 if WordBool(PanelType and PANEL_STEP) then
1937 if gSteps <> nil then
1938 begin
1939 h := High(gSteps);
1941 for a := 0 to h do
1942 if g_Collide(X, Y, Width, Height,
1943 gSteps[a].X, gSteps[a].Y,
1944 gSteps[a].Width, gSteps[a].Height) then
1945 begin
1946 Result := True;
1947 Exit;
1948 end;
1949 end;
1951 if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then
1952 if gLifts <> nil then
1953 begin
1954 h := High(gLifts);
1956 for a := 0 to h do
1957 if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
1958 (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
1959 (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
1960 (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
1961 g_Collide(X, Y, Width, Height,
1962 gLifts[a].X, gLifts[a].Y,
1963 gLifts[a].Width, gLifts[a].Height) then
1964 begin
1965 Result := True;
1966 Exit;
1967 end;
1968 end;
1970 if WordBool(PanelType and PANEL_BLOCKMON) then
1971 if gBlockMon <> nil then
1972 begin
1973 h := High(gBlockMon);
1975 for a := 0 to h do
1976 if ( (not b1x3) or
1977 ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64) ) and
1978 g_Collide(X, Y, Width, Height,
1979 gBlockMon[a].X, gBlockMon[a].Y,
1980 gBlockMon[a].Width, gBlockMon[a].Height) then
1981 begin
1982 Result := True;
1983 Exit;
1984 end;
1985 end;
1986 end;
1988 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
1989 var
1990 a, h: Integer;
1991 begin
1992 Result := TEXTURE_NONE;
1994 if gWater <> nil then
1995 begin
1996 h := High(gWater);
1998 for a := 0 to h do
1999 if g_Collide(X, Y, Width, Height,
2000 gWater[a].X, gWater[a].Y,
2001 gWater[a].Width, gWater[a].Height) then
2002 begin
2003 Result := gWater[a].GetTextureID();
2004 Exit;
2005 end;
2006 end;
2008 if gAcid1 <> nil then
2009 begin
2010 h := High(gAcid1);
2012 for a := 0 to h do
2013 if g_Collide(X, Y, Width, Height,
2014 gAcid1[a].X, gAcid1[a].Y,
2015 gAcid1[a].Width, gAcid1[a].Height) then
2016 begin
2017 Result := gAcid1[a].GetTextureID();
2018 Exit;
2019 end;
2020 end;
2022 if gAcid2 <> nil then
2023 begin
2024 h := High(gAcid2);
2026 for a := 0 to h do
2027 if g_Collide(X, Y, Width, Height,
2028 gAcid2[a].X, gAcid2[a].Y,
2029 gAcid2[a].Width, gAcid2[a].Height) then
2030 begin
2031 Result := gAcid2[a].GetTextureID();
2032 Exit;
2033 end;
2034 end;
2035 end;
2037 procedure g_Map_EnableWall(ID: DWORD);
2038 begin
2039 with gWalls[ID] do
2040 begin
2041 Enabled := True;
2042 g_Mark(X, Y, Width, Height, MARK_DOOR, True);
2044 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2045 end;
2046 end;
2048 procedure g_Map_DisableWall(ID: DWORD);
2049 begin
2050 with gWalls[ID] do
2051 begin
2052 Enabled := False;
2053 g_Mark(X, Y, Width, Height, MARK_DOOR, False);
2055 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2056 end;
2057 end;
2059 procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0);
2060 var
2061 tp: TPanel;
2062 begin
2063 case PanelType of
2064 PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR:
2065 tp := gWalls[ID];
2066 PANEL_FORE:
2067 tp := gRenderForegrounds[ID];
2068 PANEL_BACK:
2069 tp := gRenderBackgrounds[ID];
2070 PANEL_WATER:
2071 tp := gWater[ID];
2072 PANEL_ACID1:
2073 tp := gAcid1[ID];
2074 PANEL_ACID2:
2075 tp := gAcid2[ID];
2076 PANEL_STEP:
2077 tp := gSteps[ID];
2078 else
2079 Exit;
2080 end;
2082 tp.NextTexture(AnimLoop);
2083 if g_Game_IsServer and g_Game_IsNet then
2084 MH_SEND_PanelTexture(PanelType, ID, AnimLoop);
2085 end;
2087 procedure g_Map_SetLift(ID: DWORD; t: Integer);
2088 begin
2089 if gLifts[ID].LiftType = t then
2090 Exit;
2092 with gLifts[ID] do
2093 begin
2094 LiftType := t;
2096 g_Mark(X, Y, Width, Height, MARK_LIFT, False);
2098 if LiftType = 0 then
2099 g_Mark(X, Y, Width, Height, MARK_LIFTUP, True)
2100 else if LiftType = 1 then
2101 g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True)
2102 else if LiftType = 2 then
2103 g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True)
2104 else if LiftType = 3 then
2105 g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True);
2107 if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
2108 end;
2109 end;
2111 function g_Map_GetPoint(PointType: Byte; var RespawnPoint: TRespawnPoint): Boolean;
2112 var
2113 a: Integer;
2114 PointsArray: Array of TRespawnPoint;
2115 begin
2116 Result := False;
2117 SetLength(PointsArray, 0);
2119 if RespawnPoints = nil then
2120 Exit;
2122 for a := 0 to High(RespawnPoints) do
2123 if RespawnPoints[a].PointType = PointType then
2124 begin
2125 SetLength(PointsArray, Length(PointsArray)+1);
2126 PointsArray[High(PointsArray)] := RespawnPoints[a];
2127 end;
2129 if PointsArray = nil then
2130 Exit;
2132 RespawnPoint := PointsArray[Random(Length(PointsArray))];
2133 Result := True;
2134 end;
2136 function g_Map_GetPointCount(PointType: Byte): Word;
2137 var
2138 a: Integer;
2139 begin
2140 Result := 0;
2142 if RespawnPoints = nil then
2143 Exit;
2145 for a := 0 to High(RespawnPoints) do
2146 if RespawnPoints[a].PointType = PointType then
2147 Result := Result + 1;
2148 end;
2150 function g_Map_HaveFlagPoints(): Boolean;
2151 begin
2152 Result := (FlagPoints[FLAG_RED] <> nil) and (FlagPoints[FLAG_BLUE] <> nil);
2153 end;
2155 procedure g_Map_ResetFlag(Flag: Byte);
2156 begin
2157 with gFlags[Flag] do
2158 begin
2159 Obj.X := -1000;
2160 Obj.Y := -1000;
2161 Obj.Vel.X := 0;
2162 Obj.Vel.Y := 0;
2163 Direction := D_LEFT;
2164 State := FLAG_STATE_NONE;
2165 if FlagPoints[Flag] <> nil then
2166 begin
2167 Obj.X := FlagPoints[Flag]^.X;
2168 Obj.Y := FlagPoints[Flag]^.Y;
2169 Direction := FlagPoints[Flag]^.Direction;
2170 State := FLAG_STATE_NORMAL;
2171 end;
2172 Count := -1;
2173 end;
2174 end;
2176 procedure g_Map_DrawFlags();
2177 var
2178 i, dx: Integer;
2179 Mirror: TMirrorType;
2180 begin
2181 if gGameSettings.GameMode <> GM_CTF then
2182 Exit;
2184 for i := FLAG_RED to FLAG_BLUE do
2185 with gFlags[i] do
2186 if State <> FLAG_STATE_CAPTURED then
2187 begin
2188 if State = FLAG_STATE_NONE then
2189 continue;
2191 if Direction = D_LEFT then
2192 begin
2193 Mirror := M_HORIZONTAL;
2194 dx := -1;
2195 end
2196 else
2197 begin
2198 Mirror := M_NONE;
2199 dx := 1;
2200 end;
2202 Animation.Draw(Obj.X+dx, Obj.Y+1, Mirror);
2204 if g_debug_Frames then
2205 begin
2206 e_DrawQuad(Obj.X+Obj.Rect.X,
2207 Obj.Y+Obj.Rect.Y,
2208 Obj.X+Obj.Rect.X+Obj.Rect.Width-1,
2209 Obj.Y+Obj.Rect.Y+Obj.Rect.Height-1,
2210 0, 255, 0);
2211 end;
2212 end;
2213 end;
2215 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
2216 var
2217 dw: DWORD;
2218 b: Byte;
2219 str: String;
2220 boo: Boolean;
2222 procedure SavePanelArray(var panels: TPanelArray);
2223 var
2224 PAMem: TBinMemoryWriter;
2225 i: Integer;
2226 begin
2227 // Ñîçäàåì íîâûé ñïèñîê ñîõðàíÿåìûõ ïàíåëåé:
2228 PAMem := TBinMemoryWriter.Create((Length(panels)+1) * 40);
2230 i := 0;
2231 while i < Length(panels) do
2232 begin
2233 if panels[i].SaveIt then
2234 begin
2235 // ID ïàíåëè:
2236 PAMem.WriteInt(i);
2237 // Ñîõðàíÿåì ïàíåëü:
2238 panels[i].SaveState(PAMem);
2239 end;
2240 Inc(i);
2241 end;
2243 // Ñîõðàíÿåì ýòîò ñïèñîê ïàíåëåé:
2244 PAMem.SaveToMemory(Mem);
2245 PAMem.Free();
2246 end;
2248 procedure SaveFlag(flag: PFlag);
2249 begin
2250 // Ñèãíàòóðà ôëàãà:
2251 dw := FLAG_SIGNATURE; // 'FLAG'
2252 Mem.WriteDWORD(dw);
2253 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2254 Mem.WriteByte(flag^.RespawnType);
2255 // Ñîñòîÿíèå ôëàãà:
2256 Mem.WriteByte(flag^.State);
2257 // Íàïðàâëåíèå ôëàãà:
2258 if flag^.Direction = D_LEFT then
2259 b := 1
2260 else // D_RIGHT
2261 b := 2;
2262 Mem.WriteByte(b);
2263 // Îáúåêò ôëàãà:
2264 Obj_SaveState(@flag^.Obj, Mem);
2265 end;
2267 begin
2268 Mem := TBinMemoryWriter.Create(1024 * 1024); // 1 MB
2270 ///// Ñîõðàíÿåì ñïèñêè ïàíåëåé: /////
2271 // Ñîõðàíÿåì ïàíåëè ñòåí è äâåðåé:
2272 SavePanelArray(gWalls);
2273 // Ñîõðàíÿåì ïàíåëè ôîíà:
2274 SavePanelArray(gRenderBackgrounds);
2275 // Ñîõðàíÿåì ïàíåëè ïåðåäíåãî ïëàíà:
2276 SavePanelArray(gRenderForegrounds);
2277 // Ñîõðàíÿåì ïàíåëè âîäû:
2278 SavePanelArray(gWater);
2279 // Ñîõðàíÿåì ïàíåëè êèñëîòû-1:
2280 SavePanelArray(gAcid1);
2281 // Ñîõðàíÿåì ïàíåëè êèñëîòû-2:
2282 SavePanelArray(gAcid2);
2283 // Ñîõðàíÿåì ïàíåëè ñòóïåíåé:
2284 SavePanelArray(gSteps);
2285 // Ñîõðàíÿåì ïàíåëè ëèôòîâ:
2286 SavePanelArray(gLifts);
2287 ///// /////
2289 ///// Ñîõðàíÿåì ìóçûêó: /////
2290 // Ñèãíàòóðà ìóçûêè:
2291 dw := MUSIC_SIGNATURE; // 'MUSI'
2292 Mem.WriteDWORD(dw);
2293 // Íàçâàíèå ìóçûêè:
2294 Assert(gMusic <> nil, 'g_Map_SaveState: gMusic = nil');
2295 if gMusic.NoMusic then
2296 str := ''
2297 else
2298 str := gMusic.Name;
2299 Mem.WriteString(str, 64);
2300 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2301 dw := gMusic.GetPosition();
2302 Mem.WriteDWORD(dw);
2303 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2304 boo := gMusic.SpecPause;
2305 Mem.WriteBoolean(boo);
2306 ///// /////
2308 ///// Ñîõðàíÿåì êîëè÷åñòâî ìîíñòðîâ: /////
2309 Mem.WriteInt(gTotalMonsters);
2310 ///// /////
2312 //// Ñîõðàíÿåì ôëàãè, åñëè ýòî CTF: /////
2313 if gGameSettings.GameMode = GM_CTF then
2314 begin
2315 // Ôëàã Êðàñíîé êîìàíäû:
2316 SaveFlag(@gFlags[FLAG_RED]);
2317 // Ôëàã Ñèíåé êîìàíäû:
2318 SaveFlag(@gFlags[FLAG_BLUE]);
2319 end;
2320 ///// /////
2322 ///// Ñîõðàíÿåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2323 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2324 begin
2325 // Î÷êè Êðàñíîé êîìàíäû:
2326 Mem.WriteSmallInt(gTeamStat[TEAM_RED].Goals);
2327 // Î÷êè Ñèíåé êîìàíäû:
2328 Mem.WriteSmallInt(gTeamStat[TEAM_BLUE].Goals);
2329 end;
2330 ///// /////
2331 end;
2333 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
2334 var
2335 dw: DWORD;
2336 b: Byte;
2337 str: String;
2338 boo: Boolean;
2340 procedure LoadPanelArray(var panels: TPanelArray);
2341 var
2342 PAMem: TBinMemoryReader;
2343 i, id: Integer;
2344 begin
2345 // Çàãðóæàåì òåêóùèé ñïèñîê ïàíåëåé:
2346 PAMem := TBinMemoryReader.Create();
2347 PAMem.LoadFromMemory(Mem);
2349 for i := 0 to Length(panels)-1 do
2350 if panels[i].SaveIt then
2351 begin
2352 // ID ïàíåëè:
2353 PAMem.ReadInt(id);
2354 if id <> i then
2355 begin
2356 raise EBinSizeError.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel ID');
2357 end;
2358 // Çàãðóæàåì ïàíåëü:
2359 panels[i].LoadState(PAMem);
2360 end;
2362 // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí:
2363 PAMem.Free();
2364 end;
2366 procedure LoadFlag(flag: PFlag);
2367 begin
2368 // Ñèãíàòóðà ôëàãà:
2369 Mem.ReadDWORD(dw);
2370 if dw <> FLAG_SIGNATURE then // 'FLAG'
2371 begin
2372 raise EBinSizeError.Create('g_Map_LoadState: LoadFlag: Wrong Flag Signature');
2373 end;
2374 // Âðåìÿ ïåðåïîÿâëåíèÿ ôëàãà:
2375 Mem.ReadByte(flag^.RespawnType);
2376 // Ñîñòîÿíèå ôëàãà:
2377 Mem.ReadByte(flag^.State);
2378 // Íàïðàâëåíèå ôëàãà:
2379 Mem.ReadByte(b);
2380 if b = 1 then
2381 flag^.Direction := D_LEFT
2382 else // b = 2
2383 flag^.Direction := D_RIGHT;
2384 // Îáúåêò ôëàãà:
2385 Obj_LoadState(@flag^.Obj, Mem);
2386 end;
2388 begin
2389 if Mem = nil then
2390 Exit;
2392 ///// Çàãðóæàåì ñïèñêè ïàíåëåé: /////
2393 // Çàãðóæàåì ïàíåëè ñòåí è äâåðåé:
2394 LoadPanelArray(gWalls);
2395 // Çàãðóæàåì ïàíåëè ôîíà:
2396 LoadPanelArray(gRenderBackgrounds);
2397 // Çàãðóæàåì ïàíåëè ïåðåäíåãî ïëàíà:
2398 LoadPanelArray(gRenderForegrounds);
2399 // Çàãðóæàåì ïàíåëè âîäû:
2400 LoadPanelArray(gWater);
2401 // Çàãðóæàåì ïàíåëè êèñëîòû-1:
2402 LoadPanelArray(gAcid1);
2403 // Çàãðóæàåì ïàíåëè êèñëîòû-2:
2404 LoadPanelArray(gAcid2);
2405 // Çàãðóæàåì ïàíåëè ñòóïåíåé:
2406 LoadPanelArray(gSteps);
2407 // Çàãðóæàåì ïàíåëè ëèôòîâ:
2408 LoadPanelArray(gLifts);
2409 ///// /////
2411 // Îáíîâëÿåì êàðòó ñòîëêíîâåíèé:
2412 g_GFX_Init();
2414 ///// Çàãðóæàåì ìóçûêó: /////
2415 // Ñèãíàòóðà ìóçûêè:
2416 Mem.ReadDWORD(dw);
2417 if dw <> MUSIC_SIGNATURE then // 'MUSI'
2418 begin
2419 raise EBinSizeError.Create('g_Map_LoadState: Wrong Music Signature');
2420 end;
2421 // Íàçâàíèå ìóçûêè:
2422 Assert(gMusic <> nil, 'g_Map_LoadState: gMusic = nil');
2423 Mem.ReadString(str);
2424 // Ïîçèöèÿ ïðîèãðûâàíèÿ ìóçûêè:
2425 Mem.ReadDWORD(dw);
2426 // Ñòîèò ëè ìóçûêà íà ñïåö-ïàóçå:
2427 Mem.ReadBoolean(boo);
2428 // Çàïóñêàåì ýòó ìóçûêó:
2429 gMusic.SetByName(str);
2430 gMusic.SpecPause := boo;
2431 gMusic.Play();
2432 gMusic.Pause(True);
2433 gMusic.SetPosition(dw);
2434 ///// /////
2436 ///// Çàãðóæàåì êîëè÷åñòâî ìîíñòðîâ: /////
2437 Mem.ReadInt(gTotalMonsters);
2438 ///// /////
2440 //// Çàãðóæàåì ôëàãè, åñëè ýòî CTF: /////
2441 if gGameSettings.GameMode = GM_CTF then
2442 begin
2443 // Ôëàã Êðàñíîé êîìàíäû:
2444 LoadFlag(@gFlags[FLAG_RED]);
2445 // Ôëàã Ñèíåé êîìàíäû:
2446 LoadFlag(@gFlags[FLAG_BLUE]);
2447 end;
2448 ///// /////
2450 ///// Çàãðóæàåì êîëè÷åñòâî ïîáåä, åñëè ýòî TDM/CTF: /////
2451 if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
2452 begin
2453 // Î÷êè Êðàñíîé êîìàíäû:
2454 Mem.ReadSmallInt(gTeamStat[TEAM_RED].Goals);
2455 // Î÷êè Ñèíåé êîìàíäû:
2456 Mem.ReadSmallInt(gTeamStat[TEAM_BLUE].Goals);
2457 end;
2458 ///// /////
2459 end;
2461 function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
2462 var
2463 Arr: TPanelArray;
2464 begin
2465 Result := nil;
2466 if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
2467 Arr := PanelByID[PanelID].PWhere^;
2468 PanelArrayID := PanelByID[PanelID].PArrID;
2469 Result := Addr(Arr[PanelByID[PanelID].PArrID]);
2470 end;
2472 end.