1 (* Copyright (C) SovietPony
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, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 {$MODESWITCH ANSISTRINGS+}
18 {$MODESWITCH AUTODEREF+}
19 {$MODESWITCH CLASSICPROCVARS+}
20 {$MODESWITCH DEFAULTPARAMETERS+}
21 {$MODESWITCH DUPLICATELOCALS-}
22 {$MODESWITCH EXCEPTIONS+}
23 {$MODESWITCH HINTDIRECTIVE+}
24 {$MODESWITCH NESTEDCOMMENTS+}
25 {$MODESWITCH NESTEDPROCVARS+}
28 {$MODESWITCH PCHARTOSTRING+}
29 {$MODESWITCH POINTERTOPROCVAR+}
30 {$MODESWITCH REPEATFORWARD+}
32 {$MODESWITCH UNICODESTRINGS-}
40 uses Classes
, SysUtils
, Math
, SDL
, SDL_image
;
66 MP_AMMO_BACKPACK
= 20;
68 MP_AMMO_BULLETS_BOX
= 22;
70 MP_AMMO_SHELLS_BOX
= 24;
74 MP_AMMO_ROCKET_BOX
= 28;
76 MP_AMMO_CELL_BIG
= 30;
77 MP_WEAPON_SHOTGUN1
= 31;
78 MP_WEAPON_SHOTGUN2
= 32;
79 MP_WEAPON_CHAINGUN
= 33;
81 MP_WEAPON_ROCKETLAUNCHER
= 35;
82 MP_WEAPON_PLASMA
= 36;
84 MP_WEAPON_SUPERPULEMET
= 38;
85 // areas and triggers here!
92 MP_BLUETEAMPOINT
= 41;
96 (* MP Trigger activation *)
124 PANEL_LIFTDOWN
= 256;
125 PANEL_OPENDOOR
= 512;
126 PANEL_CLOSEDOOR
= 1024;
127 PANEL_BLOCKMON
= 2048;
128 PANEL_LIFTLEFT
= 4096;
129 PANEL_LIFTRIGHT
= 8192;
131 PANEL_FLAG_BLENDING
= 1;
133 PANEL_FLAG_WATERTEXTURES
= 4;
136 ITEM_MEDKIT_SMALL
= 1;
137 ITEM_MEDKIT_LARGE
= 2;
138 ITEM_MEDKIT_BLACK
= 3;
139 ITEM_ARMOR_GREEN
= 4;
141 ITEM_SPHERE_BLUE
= 6;
142 ITEM_SPHERE_WHITE
= 7;
146 ITEM_WEAPON_SAW
= 11;
147 ITEM_WEAPON_SHOTGUN1
= 12;
148 ITEM_WEAPON_SHOTGUN2
= 13;
149 ITEM_WEAPON_CHAINGUN
= 14;
150 ITEM_WEAPON_ROCKETLAUNCHER
= 15;
151 ITEM_WEAPON_PLASMA
= 16;
152 ITEM_WEAPON_BFG
= 17;
153 ITEM_WEAPON_SUPERPULEMET
= 18;
154 ITEM_AMMO_BULLETS
= 19;
155 ITEM_AMMO_BULLETS_BOX
= 20;
156 ITEM_AMMO_SHELLS
= 21;
157 ITEM_AMMO_SHELLS_BOX
= 22;
158 ITEM_AMMO_ROCKET
= 23;
159 ITEM_AMMO_ROCKET_BOX
= 24;
161 ITEM_AMMO_CELL_BIG
= 26;
162 ITEM_AMMO_BACKPACK
= 27;
166 ITEM_WEAPON_KASTET
= 31;
167 ITEM_WEAPON_PISTOL
= 32;
172 ITEM_WEAPON_FLAMETHROWER
= 37;
173 ITEM_AMMO_FUELCAN
= 38;
175 ITEM_OPTION_ONLYDM
= 1;
176 ITEM_OPTION_FALL
= 2;
179 AREA_PLAYERPOINT1
= 1;
180 AREA_PLAYERPOINT2
= 2;
185 AREA_REDTEAMPOINT
= 7;
186 AREA_BLUETEAMPOINT
= 8;
190 TRIGGER_TELEPORT
= 2;
191 TRIGGER_OPENDOOR
= 3;
192 TRIGGER_CLOSEDOOR
= 4;
195 TRIGGER_CLOSETRAP
= 7;
200 TRIGGER_LIFTDOWN
= 12;
202 TRIGGER_TEXTURE
= 14;
207 TRIGGER_SPAWNMONSTER
= 19;
208 TRIGGER_SPAWNITEM
= 20;
212 TRIGGER_MESSAGE
= 24;
219 TRIGGER_SHOT_PISTOL
= 0;
220 TRIGGER_SHOT_BULLET
= 1;
221 TRIGGER_SHOT_SHOTGUN
= 2;
222 TRIGGER_SHOT_SSG
= 3;
223 TRIGGER_SHOT_IMP
= 4;
224 TRIGGER_SHOT_PLASMA
= 5;
225 TRIGGER_SHOT_SPIDER
= 6;
226 TRIGGER_SHOT_CACO
= 7;
227 TRIGGER_SHOT_BARON
= 8;
228 TRIGGER_SHOT_MANCUB
= 9;
229 TRIGGER_SHOT_REV
= 10;
230 TRIGGER_SHOT_ROCKET
= 11;
231 TRIGGER_SHOT_BFG
= 12;
232 TRIGGER_SHOT_EXPL
= 13;
233 TRIGGER_SHOT_BFGEXPL
= 14;
234 TRIGGER_SHOT_FLAME
= 15;
235 TRIGGER_SHOT_MAX
= 15;
237 TRIGGER_SHOT_TARGET_NONE
= 0;
238 TRIGGER_SHOT_TARGET_MON
= 1;
239 TRIGGER_SHOT_TARGET_PLR
= 2;
240 TRIGGER_SHOT_TARGET_RED
= 3;
241 TRIGGER_SHOT_TARGET_BLUE
= 4;
242 TRIGGER_SHOT_TARGET_MONPLR
= 5;
243 TRIGGER_SHOT_TARGET_PLRMON
= 6;
245 TRIGGER_SHOT_AIM_DEFAULT
= 0;
246 TRIGGER_SHOT_AIM_ALLMAP
= 1;
247 TRIGGER_SHOT_AIM_TRACE
= 2;
248 TRIGGER_SHOT_AIM_TRACEALL
= 3;
250 TRIGGER_EFFECT_PARTICLE
= 0;
251 TRIGGER_EFFECT_ANIMATION
= 1;
253 TRIGGER_EFFECT_SLIQUID
= 0;
254 TRIGGER_EFFECT_LLIQUID
= 1;
255 TRIGGER_EFFECT_DLIQUID
= 2;
256 TRIGGER_EFFECT_BLOOD
= 3;
257 TRIGGER_EFFECT_SPARK
= 4;
258 TRIGGER_EFFECT_BUBBLE
= 5;
259 TRIGGER_EFFECT_MAX
= 5;
261 TRIGGER_EFFECT_POS_CENTER
= 0;
262 TRIGGER_EFFECT_POS_AREA
= 1;
264 ACTIVATE_PLAYERCOLLIDE
= 1;
265 ACTIVATE_MONSTERCOLLIDE
= 2;
266 ACTIVATE_PLAYERPRESS
= 4;
267 ACTIVATE_MONSTERPRESS
= 8;
269 ACTIVATE_NOMONSTER
= 32;
270 ACTIVATE_CUSTOM
= 255;
278 TEXTURE_SPECIAL_WATER
= DWORD(-1);
279 TEXTURE_SPECIAL_ACID1
= DWORD(-2);
280 TEXTURE_SPECIAL_ACID2
= DWORD(-3);
281 TEXTURE_NONE
= DWORD(-4);
283 mp2df_wall
: array [MP_WALL
..MP_LIFTRIGHT
] of Integer = (
284 PANEL_WALL
, PANEL_STEP
, PANEL_BACK
, PANEL_FORE
, PANEL_WATER
,
285 PANEL_ACID1
, PANEL_ACID2
, PANEL_LIFTUP
, PANEL_LIFTDOWN
,
286 PANEL_LIFTLEFT
, PANEL_LIFTRIGHT
289 mp2df_item
: array [MP_MEDKIT_SMALL
..MP_SUIT
] of Byte = (
290 ITEM_MEDKIT_SMALL
, ITEM_MEDKIT_LARGE
, ITEM_ARMOR_GREEN
, ITEM_ARMOR_BLUE
,
291 ITEM_SPHERE_BLUE
, ITEM_SPHERE_WHITE
, ITEM_INVUL
, ITEM_JETPACK
,
292 ITEM_MEDKIT_BLACK
, ITEM_AMMO_BACKPACK
, ITEM_AMMO_BULLETS
, ITEM_AMMO_BULLETS_BOX
,
293 ITEM_AMMO_SHELLS
, ITEM_AMMO_SHELLS_BOX
, ITEM_BOTTLE
, ITEM_HELMET
,
294 ITEM_AMMO_ROCKET
, ITEM_AMMO_ROCKET_BOX
, ITEM_AMMO_CELL
, ITEM_AMMO_CELL_BIG
,
295 ITEM_WEAPON_SHOTGUN1
, ITEM_WEAPON_SHOTGUN2
, ITEM_WEAPON_CHAINGUN
, ITEM_WEAPON_SAW
,
296 ITEM_WEAPON_ROCKETLAUNCHER
, ITEM_WEAPON_PLASMA
, ITEM_WEAPON_BFG
, ITEM_WEAPON_SUPERPULEMET
,
297 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused here
298 ITEM_INVIS
, ITEM_SUIT
301 mp2df_area
: array [MP_DMPOINT
..MP_BLUEFLAG
] of Byte = (
302 AREA_DMPOINT
, AREA_REDTEAMPOINT
, AREA_BLUETEAMPOINT
,
303 AREA_REDFLAG
, AREA_BLUEFLAG
306 mp2df_act
: array [MP_COLLIDE
..MP_NOACT
] of Byte = (
307 ACTIVATE_PLAYERCOLLIDE
, ACTIVATE_PLAYERPRESS
, ACTIVATE_SHOT
,
308 ACTIVATE_NOMONSTER
or ACTIVATE_MONSTERCOLLIDE
, 0
311 mp2df_trig
: array [MP_CLOSEDOOR
..MP_EXIT
] of Byte = (
312 TRIGGER_CLOSETRAP
, TRIGGER_OPENDOOR
, TRIGGER_DOOR
,
313 TRIGGER_PRESS
, TRIGGER_PRESS
, // random, extender
314 TRIGGER_ONOFF
, TRIGGER_DAMAGE
, TRIGGER_TELEPORT
, TRIGGER_EXIT
321 x0
, y0
, x1
, y1
: Integer;
323 door
: Boolean; // door
324 switch
: Boolean; // switchable door
325 id
: Integer; // door panel id
326 did
: Integer; // switchable door id
337 name
, desc
, music
, sky
: AnsiString;
338 width
, height
: Integer;
339 textures
: array of Texture
;
340 tiles
: array of Tile
;
344 haveTriggers
: Boolean;
346 (* --------- Reader --------- *)
348 procedure ReadMap (fname
: AnsiString);
349 var f
: TextFile
; i
, n
: Integer;
351 procedure ReadInt (var i
: Integer);
358 procedure ReadFloat (var i
: Single);
375 SetLength(textures
, n
);
376 for i
:= 1 to n
- 1 do
378 ReadLn(f
, textures
[i
].name
);
381 while eof(f
) = false do
384 SetLength(tiles
, i
+ 1);
391 if (t
>= 44) and (t
<= 48) then
411 (* --------- Analyse --------- *)
413 function isCollide (x0
, y0
, w0
, h0
, x1
, y1
, w1
, h1
: Integer): Boolean;
414 var xx0
, yy0
, xx1
, yy1
: Integer;
420 result
:= (xx0
>= x1
) and (x0
<= xx1
) and (yy0
>= y1
) and (y0
<= yy1
)
424 var i
, j
, t
, x
, y
, w
, h
, xx
, yy
, ww
, hh
: Integer; n
: AnsiString; s
: PSDL_Surface
;
426 if sky
= '*NO_BACKGROUND' then sky
:= '';
427 if music
= '*NO_MUSIC' then music
:= '';
431 textures
[0].flags
:= PANEL_FLAG_HIDE
;
432 for i
:= 1 to High(textures
) do
434 n
:= StringReplace(textures
[i
].name
, '\', '/', [rfReplaceAll
]);
435 s
:= IMG_Load(PChar(n
));
438 textures
[i
].w
:= s
.w
;
439 textures
[i
].h
:= s
.h
;
446 WriteLn('warning: texture ', n
, ' not loaded: ', IMG_GetError())
448 textures
[i
].id
:= i
- 1;
449 textures
[i
].flags
:= 0;
451 for i
:= 0 to High(tiles
) do
455 MP_WALL
..MP_LIFTRIGHT
: havePanels
:= true;
456 MP_MEDKIT_SMALL
..MP_WEAPON_SUPERPULEMET
, MP_INVIS
, MP_SUIT
: haveItems
:= true;
457 MP_DMPOINT
..MP_BLUEFLAG
: haveAreas
:= true;
458 MP_COLLIDE
..MP_NOACT
:
460 haveTriggers
:= true;
462 if (t
in [MP_CLOSEDOOR
..MP_DOOR
]) or (t
>= 101) and (t
<= 400) or (t
>= 501) and (t
<= 800) then
466 w
:= tiles
[i
].x1
- tiles
[i
].x0
;
467 h
:= tiles
[i
].y1
- tiles
[i
].y0
;
468 for j
:= 0 to High(tiles
) do
470 if tiles
[j
].t
= MP_WALL
then
474 ww
:= textures
[tiles
[j
].s
].w
;
475 hh
:= textures
[tiles
[j
].s
].h
;
476 if isCollide(x
, y
, w
, h
, xx
, yy
, ww
, hh
) then
478 tiles
[j
].door
:= tiles
[j
].t
= MP_WALL
;
479 if tiles
[j
].door
and ((t
= MP_DOOR
) or ((t
>= 101) and (t
<= 400))) then
480 tiles
[j
].switch
:= true
490 for i
:= 0 to High(tiles
) do
493 if tiles
[i
].switch
then
499 // TODO merge tiles to panels, but this can be done by df editor so...
502 (* --------- Writer --------- *)
512 procedure WriteBytes (f
: TFileStream
; x
: array of Byte);
514 f
.Write(x
, Length(x
));
515 Inc(wrCount
, Length(x
))
518 procedure WriteChars(f
: TFileStream
; x
: array of Char);
521 for i
:= 0 to High(x
) do
523 WriteBytes(f
, [Ord(x
[i
])])
527 procedure WriteAnsiString (f
: TFileStream
; s
: AnsiString; maxlen
: Integer);
532 len
:= min(Length(s
), maxlen
);
536 WriteChars(f
, [s
[i
]]);
546 procedure WriteInt (f
: TFileStream
; x
: Integer);
547 var a
, b
, c
, d
: Integer;
551 c
:= x
>> 16 and $FF;
552 d
:= x
>> 24 and $FF;
553 WriteBytes(f
, [a
, b
, c
, d
])
556 procedure WriteInt16 (f
: TFileStream
; x
: Integer);
561 WriteBytes(f
, [a
, b
])
564 procedure BeginBlock (f
: TFileStream
; t
: Byte);
569 wrFixup
:= f
.Position
;
574 procedure EndBlock(f
: TFileStream
);
578 f
.Position
:= wrFixup
;
579 WriteInt(f
, wrCount
);
585 procedure WriteHeader (f
: TFileStream
; name
, author
, desc
, mus
, sky
: AnsiString; w
, h
: Integer);
588 WriteAnsiString(f
, name
, 32);
589 WriteAnsiString(f
, author
, 32);
590 WriteAnsiString(f
, desc
, 256);
591 WriteAnsiString(f
, mus
, 64);
592 WriteAnsiString(f
, sky
, 64);
598 procedure WriteTexture (f
: TFileStream
; name
: AnsiString; anim
: Byte);
600 WriteAnsiString(f
, name
, 64);
601 WriteBytes(f
, [anim
]);
604 procedure WritePanel (f
: TFileStream
; x
, y
, w
, h
, tex
, typ
, alpha
, flags
: Integer);
606 if (tex
< 0) or (tex
> High(textures
)) then
608 WriteLn('warning: panel ', wrPanels
, ' [', x
, 'x', y
, ':', w
, 'x', h
, ':typ=', typ
, '] have invalid texture id ', tex
);
617 WriteBytes(f
, [alpha
, flags
]);
621 procedure WriteItem (f
: TFileStream
; x
, y
: Integer; typ
, flags
: Byte);
625 WriteBytes(f
, [typ
, flags
]);
628 procedure WriteArea (f
: TFileStream
; x
, y
: Integer; typ
, dir
: Byte);
632 WriteBytes(f
, [typ
, dir
]);
635 procedure WriteEnd (f
: TFileStream
);
641 function BoolToInt (x
: Boolean): Integer;
643 if x
then result
:= 1 else result
:= 0
646 procedure BeginTrigger (f
: TFileStream
; x
, y
, w
, h
, enabled
, texpan
, typ
, act
, keys
: Integer);
649 assert(typ
in [TRIGGER_EXIT
..TRIGGER_MAX
]);
655 WriteBytes(f
, [enabled
]);
657 WriteBytes(f
, [typ
, act
, keys
]);
661 procedure EndTrigger (f
: TFileStream
);
665 for i
:= wrCount
mod 148 to 148 - 1 do
669 assert(wrCount
mod 148 = 0);
674 procedure ExitTrigger (f
: TFileStream
; map
: AnsiString);
677 assert(wrTag
= TRIGGER_EXIT
);
678 WriteAnsiString(f
, map
, 16);
682 procedure TeleportTrigger (f
: TFileStream
; x
, y
: Integer; d2d
, silent
: Boolean; dir
: Integer);
685 assert(wrTag
= TRIGGER_TELEPORT
);
688 WriteBytes(f
, [BoolToInt(d2d
), BoolToInt(silent
), dir
]);
692 procedure DoorTrigger (f
: TFileStream
; panelid
: Integer; nosound
, d2d
: Boolean);
695 assert(wrTag
in [TRIGGER_OPENDOOR
..TRIGGER_TRAP
, TRIGGER_LIFTUP
..TRIGGER_LIFT
]);
696 WriteInt(f
, panelid
);
697 WriteBytes(f
, [BoolToInt(nosound
), BoolToInt(d2d
)]);
701 procedure SwitchTrigger (f
: TFileStream
; x
, y
, w
, h
, wait
, count
, monsterid
: Integer; random
: Boolean);
704 assert(wrTag
in [TRIGGER_PRESS
, TRIGGER_ON
..TRIGGER_ONOFF
]);
710 WriteInt16(f
, count
);
711 WriteInt(f
, monsterid
);
712 WriteBytes(f
, [BoolToInt(random
)]);
716 procedure DamageTrigger (f
: TFileStream
; damage
, interval
, typ
: Integer);
719 assert(wrTag
= TRIGGER_DAMAGE
);
720 WriteInt16(f
, damage
);
721 WriteInt16(f
, interval
);
722 WriteBytes(f
, [typ
]);
726 function SecToTick (sec
: Integer): Integer;
728 result
:= sec
* 1000 div 28;
731 procedure WriteMap (fname
: AnsiString);
732 var f
: TFileStream
; i
, j
, typ
, wait
: Integer; t
: Tile
;
734 f
:= TFileStream
.Create(fname
, fmCreate
);
736 WriteBytes(f
, [Ord('M'), Ord('A'), Ord('P'), 1]);
737 WriteLn('header...');
738 WriteHeader(f
, name
, '', desc
, music
, sky
, width
, height
);
739 if High(textures
) > 0 then
741 WriteLn('textures...');
743 for i
:= 1 to High(textures
) do
744 WriteTexture(f
, ':' + textures
[i
].name
, 0);
749 WriteLn('panels...');
751 for i
:= 0 to High(tiles
) do
754 if t
.t
in [MP_WALL
..MP_LIFTRIGHT
] then
756 tiles
[i
].id
:= wrPanels
;
758 WritePanel(f
, t
.x
, t
.y
, textures
[t
.s
].w
, textures
[t
.s
].h
, textures
[t
.s
].id
, PANEL_CLOSEDOOR
, 0, textures
[t
.s
].flags
)
759 else if t
.t
in [MP_LIFTUP
..MP_LIFTRIGHT
] then
760 WritePanel(f
, t
.x
, t
.y
, 16, 16, 0, mp2df_wall
[t
.t
], 0, PANEL_FLAG_HIDE
)
761 else if t
.t
in [MP_WATER
..MP_ACID2
] then
762 WritePanel(f
, t
.x
, t
.y
, textures
[t
.s
].w
, textures
[t
.s
].h
, textures
[t
.s
].id
, mp2df_wall
[t
.t
], 0, textures
[t
.s
].flags
or PANEL_FLAG_WATERTEXTURES
)
764 WritePanel(f
, t
.x
, t
.y
, textures
[t
.s
].w
, textures
[t
.s
].h
, textures
[t
.s
].id
, mp2df_wall
[t
.t
], 0, textures
[t
.s
].flags
)
773 for i
:= 0 to High(tiles
) do
776 if t
.t
in [MP_MEDKIT_SMALL
..MP_WEAPON_SUPERPULEMET
, MP_INVIS
, MP_SUIT
] then
777 WriteItem(f
, t
.x
, t
.y
, mp2df_item
[t
.t
], 0)
785 for i
:= 0 to High(tiles
) do
789 MP_DMPOINT
..MP_BLUETEAMPOINT
: WriteArea(f
, t
.x
, t
.y
+ 12, mp2df_area
[t
.t
], 0);
790 MP_REDFLAG
, MP_BLUEFLAG
: WriteArea(f
, t
.x
+ 8, t
.y
- 4, mp2df_area
[t
.t
], 1);
797 WriteLn('triggers...');
799 for i
:= 0 to High(tiles
) do
802 if t
.t
in [MP_COLLIDE
..MP_NOACT
] then
807 BeginTrigger(f
, t
.x
, t
.y
, Round(16 * t
.sx
), Round(16 * t
.sy
), 1, -1, TRIGGER_EXIT
, mp2df_act
[t
.t
], 0);
813 BeginTrigger(f
, t
.x
, t
.y
, Round(16 * t
.sx
), Round(16 * t
.sy
), 1, -1, TRIGGER_TELEPORT
, mp2df_act
[t
.t
], 0);
814 TeleportTrigger(f
, t
.x0
, t
.y0
+ 32, true, false, 0);
817 MP_CLOSEDOOR
, MP_OPENDOOR
:
819 for j
:= 0 to High(tiles
) do
821 if tiles
[j
].door
and isCollide(t
.x0
, t
.y0
, t
.x1
- t
.x0
, t
.y1
- t
.y0
, tiles
[j
].x
, tiles
[j
].y
, textures
[tiles
[j
].s
].w
, textures
[tiles
[j
].s
].h
) then
823 if tiles
[j
].switch
then
826 BeginTrigger(f
, t
.x
, t
.y
, Round(16 * t
.sx
), Round(16 * t
.sy
), 1, -1, TRIGGER_PRESS
, mp2df_act
[t
.t
], 0);
827 SwitchTrigger(f
, tiles
[j
].did
* 32, IfThen(t
.s
= MP_CLOSEDOOR
, -16, -32), 32, 16, 0, 1, 0, false);
832 BeginTrigger(f
, t
.x
, t
.y
, Round(16 * t
.sx
), Round(16 * t
.sy
), 1, -1, mp2df_trig
[t
.s
], mp2df_act
[t
.t
], 0);
833 DoorTrigger(f
, tiles
[j
].id
, false, false);
841 wait
:= IfThen(t
.s
= MP_DOOR
, 0, t
.s
- 100);
842 // button: activate sequence in not locked
843 BeginTrigger(f
, t
.x
, t
.y
, Round(16 * t
.sx
), Round(16 * t
.sy
), 1, -1, TRIGGER_PRESS
, mp2df_act
[t
.t
], 0);
844 SwitchTrigger(f
, wrX
, -80, 32, 16, 0, 1, 0, false);
846 // start: activate group + timer
847 BeginTrigger(f
, wrX
, -80, 16, 16, 1, -1, TRIGGER_PRESS
, 0, 0);
848 SwitchTrigger(f
, wrX
, -64, 32, 32, 0, 1, 0, false);
851 BeginTrigger(f
, wrX
+ 16, -80, 16, 16, 1, -1, TRIGGER_OFF
, 0, 0);
852 SwitchTrigger(f
, wrX
, -80, 16, 16, 0, 1, 0, false);
854 for j
:= 0 to High(tiles
) do
856 if tiles
[j
].switch
and isCollide(t
.x0
, t
.y0
, t
.x1
- t
.x0
, t
.y1
- t
.y0
, tiles
[j
].x
, tiles
[j
].y
, textures
[tiles
[j
].s
].w
, textures
[tiles
[j
].s
].h
) then
858 // group: activate both branches
859 BeginTrigger(f
, wrX
, -64, 16, 16, 1, -1, TRIGGER_PRESS
, 0, 0);
860 SwitchTrigger(f
, tiles
[j
].did
* 32, -32, 32, 32, 0, 1, 0, false);
864 // timer: wait & activate group
865 BeginTrigger(f
, wrX
, -48, 16, 16, 1, -1, TRIGGER_PRESS
, 0, 0);
866 SwitchTrigger(f
, wrX
, -64, 32, 16, SecToTick(wait
), 1, 0, false);
868 // timer: wait & unlock start
869 BeginTrigger(f
, wrX
+ 16, -48, 16, 16, 1, -1, TRIGGER_ON
, 0, 0);
870 SwitchTrigger(f
, wrX
, -80, 16, 16, SecToTick(wait
), 1, 0, false);
874 MP_EXTENDER
, MP_RANDOM
, MP_SWITCH
, 501..800:
876 wait
:= IfThen(t
.s
in [MP_EXTENDER
, MP_RANDOM
, MP_SWITCH
], 0, t
.s
- 500);
877 typ
:= IfThen(t
.s
in [MP_EXTENDER
, MP_RANDOM
, MP_SWITCH
], mp2df_trig
[t
.s
], TRIGGER_PRESS
);
878 BeginTrigger(f
, t
.x
, t
.y
, Round(16 * t
.sx
), Round(16 * t
.sy
), 1, -1, typ
, mp2df_act
[t
.t
], 0);
879 SwitchTrigger(f
, t
.x0
, t
.y0
, t
.x1
- t
.x0
, t
.y1
- t
.y0
, SecToTick(wait
), 1, 0, t
.s
= MP_RANDOM
);
884 if (t
.x
= t
.x0
) and (t
.y
= t
.y0
) and (16 * t
.sx
= t
.x1
- t
.x0
) and (16 * t
.sy
= t
.y1
- t
.y0
) then
886 BeginTrigger(f
, t
.x
, t
.y
, t
.x1
- t
.x0
, t
.y1
- t
.y0
, 1, -1, TRIGGER_DAMAGE
, mp2df_act
[t
.t
], 0);
887 DamageTrigger(f
, 666, 0, 0);
892 // TODO find non intersectable location for activation
893 BeginTrigger(f
, t
.x
, t
.y
, Round(16 * t
.sx
), Round(16 * t
.sy
), 1, -1, TRIGGER_PRESS
, mp2df_act
[t
.t
], 0);
894 SwitchTrigger(f
, t
.x0
, t
.y0
, 16, 16, 0, 1, 0, false);
896 BeginTrigger(f
, t
.x0
, t
.y0
, t
.x1
- t
.x0
, t
.y1
- t
.y0
, 1, -1, TRIGGER_DAMAGE
, 0, 0);
897 DamageTrigger(f
, 999, 0, 0);
899 for j
:= 0 to High(tiles
) do
900 if (j
<> i
) and isCollide(t
.x0
, t
.y0
, 16, 16, tiles
[j
].x0
, tiles
[j
].y0
, tiles
[j
].x1
- tiles
[j
].x0
, tiles
[j
].x1
- tiles
[j
].y0
) then
901 WriteLn('waring: trigger damage may activate triggers at ', t
.x0
, 'x', t
.y0
)
904 else WriteLn('warning: unknown MP trigger ', t
.s
)
908 // MP_DOOR intermediate triggers
909 for i
:= 0 to High(tiles
) do
911 if tiles
[i
].switch
then
914 BeginTrigger(f
, tiles
[i
].did
* 32 + 16, -32, 16, 32, 1, -1, TRIGGER_ONOFF
, 0, 0);
915 SwitchTrigger(f
, tiles
[i
].did
* 32, -32, 16, 32, 0, 1, 0, false);
918 BeginTrigger(f
, tiles
[i
].did
* 32, -32, 16, 16, 1, -1, TRIGGER_OPENDOOR
, 0, 0);
919 DoorTrigger(f
, tiles
[i
].id
, false, false);
922 BeginTrigger(f
, tiles
[i
].did
* 32, -16, 16, 16, 0, -1, TRIGGER_CLOSETRAP
, 0, 0);
923 DoorTrigger(f
, tiles
[i
].id
, false, false);
933 (* --------- Init --------- *)
936 inputFile
: AnsiString;
937 outputFile
: AnsiString = 'MAP01';
938 listTextures
: Boolean = false;
940 procedure PrintTextureList
;
941 var i
: Integer; n
: AnsiString;
943 for i
:= 1 to High(textures
) do
945 n
:= StringReplace(textures
[i
].name
, '\', '/', [rfReplaceAll
]);
952 WriteLn('Usage: mp2df [OPTION] FILE.dlv [OUTPUT]');
954 WriteLn(' -l list textures used on map and exit');
955 WriteLn(' -h show this help');
960 var i
, n
: Integer; done
: Boolean; str
: AnsiString;
962 i
:= 1; done
:= false;
963 while (i
<= ParamCount
) and (not done
) do
966 done
:= (Length(str
) = 0) or (str
[1] <> '-');
970 '-l': listTextures
:= true;
973 WriteLn('mp2df: unknown argument ', str
);
979 n
:= ParamCount
- i
+ 1;
982 inputFile
:= ParamStr(i
);
984 else if (n
= 2) and (not listTextures
) then
986 inputFile
:= ParamStr(i
);
987 outputFile
:= ParamStr(i
+ 1);
991 WriteLn('mp2df: you may specify input file and output file only, use -h to know more');
994 if inputFile
= '' then
996 WriteLn('mp2df: empty input file path');
999 if outputFile
= '' then
1001 WriteLn('mp2df: empty output file path');
1009 if listTextures
then
1016 WriteMap(outputFile
)