1 /** @file p_xgline.c Extended generalized line types.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2013 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 2007 Jamie Jones <jamie_jones_au@yahoo.com.au>
6  *
7  * @par License
8  * GPL: http://www.gnu.org/licenses/gpl.html
9  *
10  * <small>This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2 of the License, or (at your
13  * option) any later version. This program is distributed in the hope that it
14  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details. You should have received a copy of the GNU
17  * General Public License along with this program; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA</small>
20  */
21 
22 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
23 
24 #include <time.h>
25 #include <math.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 
30 #include "common.h"
31 
32 #include "d_net.h"
33 #include "dmu_lib.h"
34 #include "gamesession.h"
35 #include "p_mapsetup.h"
36 #include "p_xgline.h"
37 #include "p_xgsec.h"
38 #include "p_actor.h"
39 #include "player.h"
40 #include "p_map.h"
41 #include "p_mapspec.h"
42 #include "p_terraintype.h"
43 #include "p_tick.h"
44 #include "p_sound.h"
45 #include "p_switch.h"
46 
47 using namespace de;
48 
49 #define XLTIMER_STOPPED 1    // Timer stopped.
50 
51 #define EVTYPESTR(evtype) (evtype == XLE_CHAIN? "CHAIN" \
52         : evtype == XLE_CROSS? "CROSS" \
53         : evtype == XLE_USE? "USE" \
54         : evtype == XLE_SHOOT? "SHOOT" \
55         : evtype == XLE_HIT? "HIT" \
56         : evtype == XLE_TICKER? "TICKER" \
57         : evtype == XLE_AUTO? "AUTO" \
58         : evtype == XLE_FORCED? "FORCED" \
59         : evtype == XLE_FUNC? "FUNCTION" : "???")
60 
61 #define LREFTYPESTR(reftype) (reftype == LREF_NONE? "NONE" \
62         : reftype == LREF_SELF? "SELF" \
63         : reftype == LREF_TAGGED? "TAGGED LINES" \
64         : reftype == LREF_LINE_TAGGED? "LINE TAGGED LINES" \
65         : reftype == LREF_ACT_TAGGED? "ACT TAGGED LINES" \
66         : reftype == LREF_INDEX? "INDEXED LINE" \
67         : reftype == LREF_ALL? "ALL LINES" : "???")
68 
69 #define LPREFTYPESTR(reftype) (reftype == LPREF_NONE? "NONE" \
70         : reftype == LPREF_MY_FLOOR? "MY FLOOR" \
71         : reftype == LPREF_TAGGED_FLOORS? "TAGGED FLOORS" \
72         : reftype == LPREF_LINE_TAGGED_FLOORS? "LINE TAGGED FLOORS" \
73         : reftype == LPREF_ACT_TAGGED_FLOORS? "ACT TAGGED FLOORS" \
74         : reftype == LPREF_INDEX_FLOOR? "INDEXED FLOOR" \
75         : reftype == LPREF_ALL_FLOORS? "ALL FLOORS" \
76         : reftype == LPREF_MY_CEILING? "MY CEILING" \
77         : reftype == LPREF_TAGGED_CEILINGS? "TAGGED CEILINGS" \
78         : reftype == LPREF_LINE_TAGGED_CEILINGS? "LINE TAGGED CEILINGS" \
79         : reftype == LPREF_ACT_TAGGED_CEILINGS? "ACT TAGGED CEILINGS" \
80         : reftype == LPREF_INDEX_CEILING? "INDEXED CEILING" \
81         : reftype == LPREF_ALL_CEILINGS? "ALL CEILINGS" \
82         : reftype == LPREF_SPECIAL? "SPECIAL" \
83         : reftype == LPREF_BACK_FLOOR? "BACK FLOOR" \
84         : reftype == LPREF_BACK_CEILING? "BACK CEILING" \
85         : reftype == LPREF_THING_EXIST_FLOORS? "SECTORS WITH THING - FLOOR" \
86         : reftype == LPREF_THING_EXIST_CEILINGS? "SECTORS WITH THING - CEILING" \
87         : reftype == LPREF_THING_NOEXIST_FLOORS? "SECTORS WITHOUT THING - FLOOR" \
88         : reftype == LPREF_THING_NOEXIST_CEILINGS? "SECTORS WITHOUT THING - CEILING" : "???")
89 
90 #define LSREFTYPESTR(reftype) (reftype == LSREF_NONE? "NONE" \
91         : reftype == LSREF_MY? "MY SECTOR" \
92         : reftype == LSREF_TAGGED? "TAGGED SECTORS" \
93         : reftype == LSREF_LINE_TAGGED? "LINE TAGGED SECTORS" \
94         : reftype == LSREF_ACT_TAGGED? "ACT TAGGED SECTORS" \
95         : reftype == LSREF_INDEX? "INDEXED SECTOR" \
96         : reftype == LSREF_ALL? "ALL SECTORS" \
97         : reftype == LSREF_BACK? "BACK SECTOR" \
98         : reftype == LSREF_THING_EXIST? "SECTORS WITH THING" \
99         : reftype == LSREF_THING_NOEXIST? "SECTORS WITHOUT THING" : "???")
100 
101 #define TO_DMU_TOP_COLOR(x) ((x) == 0? DMU_TOP_COLOR_RED \
102         : (x) == 1? DMU_TOP_COLOR_GREEN \
103         : DMU_TOP_COLOR_BLUE)
104 
105 #define TO_DMU_MIDDLE_COLOR(x) ((x) == 0? DMU_MIDDLE_COLOR_RED \
106         : (x) == 1? DMU_MIDDLE_COLOR_GREEN \
107         : (x) == 2? DMU_MIDDLE_COLOR_BLUE \
108         : DMU_MIDDLE_ALPHA)
109 
110 #define TO_DMU_BOTTOM_COLOR(x) ((x) == 0? DMU_BOTTOM_COLOR_RED \
111         : (x) == 1? DMU_BOTTOM_COLOR_GREEN \
112         : DMU_BOTTOM_COLOR_BLUE)
113 
114 void XL_ChangeMaterial(Line *line, int sidenum, int section, world_Material *mat,
115     blendmode_t blend = BM_NORMAL, Vector4f const &tintColor = Vector4f(), int flags = 0);
116 
117 int XL_DoChainSequence(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
118 int XL_DoDamage(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
119 int XL_DoPower(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
120 int XL_DoKey(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
121 int XL_DoExplode(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
122 int XL_DoCommand(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
123 
124 int XLTrav_ChangeLineType(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
125 int XLTrav_Activate(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
126 int XLTrav_Music(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
127 int XLTrav_LineCount(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
128 int XLTrav_LeaveMap(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
129 int XLTrav_DisableLine(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
130 int XLTrav_EnableLine(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
131 int XLTrav_ChangeWallMaterial(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
132 int XLTrav_LineTeleport(Line *line, dd_bool ceiling, void *context, void *context2, mobj_t *activator);
133 
134 int xgDev = 0; // Print dev messages.
135 
136 static linetype_t typebuffer;
137 static char msgbuf[80];
138 ThinkerT<mobj_s> dummyThing;
139 
XG_DummyThing()140 struct mobj_s *XG_DummyThing()
141 {
142     return dummyThing;
143 }
144 
145 /* ADD NEW XG CLASSES TO THE END - ORIGINAL INDICES MUST STAY THE SAME!!! */
146 xgclass_t xgClasses[NUMXGCLASSES] =
147 {
148     { NULL, NULL, TRAV_NONE, 0, 1, 0, "None",
149       // Dummy class (has no functions but enables use of secondary actions) (no params)
150       {{XGPF_INT, "", "", -1},
151        {XGPF_INT, "", "", -1},
152        {XGPF_INT, "", "", -1},
153        {XGPF_INT, "", "", -1},
154        {XGPF_INT, "", "", -1},
155        {XGPF_INT, "", "", -1},
156        {XGPF_INT, "", "", -1},
157        {XGPF_INT, "", "", -1},
158        {XGPF_INT, "", "", -1},
159        {XGPF_INT, "", "", -1},
160        {XGPF_INT, "", "", -1},
161        {XGPF_INT, "", "", -1},
162        {XGPF_INT, "", "", -1},
163        {XGPF_INT, "", "", -1},
164        {XGPF_INT, "", "", -1},
165        {XGPF_INT, "", "", -1},
166        {XGPF_INT, "", "", -1},
167        {XGPF_INT, "", "", -1},
168        {XGPF_INT, "", "", -1},
169        {XGPF_INT, "", "", -1},
170       }
171     },
172     { de::function_cast<int (*)()>(XL_DoChainSequence), NULL, TRAV_NONE, 0, 1, 0, "Chain Sequence",
173       // Execute a chain of other XG line types (a zero ends the list)
174        {{XGPF_INT, "Chain Flags", "chsf_", 0},              // ip0: (chsf_) chain sequence flags
175         {XGPF_INT, "Line Type 0", "", -1},                  // ip1: Type to execute
176         {XGPF_INT, "Line Type 1", "", -1},                  // ip2:  ""  ""  ""
177         {XGPF_INT, "Line Type 2", "", -1},                  // ip3:  ""  ""  ""
178         {XGPF_INT, "Line Type 3", "", -1},                  // ip4:  ""  ""  ""
179         {XGPF_INT, "Line Type 4", "", -1},                  // ip5:  ""  ""  ""
180         {XGPF_INT, "Line Type 5", "", -1},                  // ip6:  ""  ""  ""
181         {XGPF_INT, "Line Type 6", "", -1},                  // ip7:  ""  ""  ""
182         {XGPF_INT, "Line Type 7", "", -1},                  // ip8:  ""  ""  ""
183         {XGPF_INT, "Line Type 8", "", -1},                  // ip9:  ""  ""  ""
184         {XGPF_INT, "Line Type 9", "", -1},                  // ip10: ""  ""  ""
185         {XGPF_INT, "Line Type 10", "", -1},                 // ip11: ""  ""  ""
186         {XGPF_INT, "Line Type 11", "", -1},                 // ip12: ""  ""  ""
187         {XGPF_INT, "Line Type 12", "", -1},                 // ip13: ""  ""  ""
188         {XGPF_INT, "Line Type 13", "", -1},                 // ip14: ""  ""  ""
189         {XGPF_INT, "Line Type 14", "", -1},                 // ip15: ""  ""  ""
190         {XGPF_INT, "Line Type 15", "", -1},                 // ip16: ""  ""  ""
191         {XGPF_INT, "Line Type 16", "", -1},                 // ip17: ""  ""  ""
192         {XGPF_INT, "Line Type 17", "", -1},                 // ip18: ""  ""  ""
193         {XGPF_INT, "Line Type 18", "", -1}                  // ip19: ""  ""  ""
194        }
195     },
196     { de::function_cast<int (*)()>(XSTrav_MovePlane), XS_InitMovePlane, TRAV_PLANES, 0, 1, 0, "Move Plane",
197       // Move one or more planes. Optionaly change textures/types on start/end
198        {{XGPF_INT, "Target Ref", "lpref_", 0},              // ip0: (plane ref) plane(s) to move.
199         {XGPF_INT, "Target Num", "", -1},                   // ip1:
200         {XGPF_INT, "Destination Ref", "spref_", 2},         // ip2: destination height type (zero, relative to current, surrounding highest/lowest floor/ceiling)
201         {XGPF_INT, "Move Flags", "pmf_", 3},                // ip3: flags (PMF_*)
202         {XGPF_INT, "Start Sound", "", 4 | MAP_SND},         // ip4: start sound
203         {XGPF_INT, "End Sound", "", 5 | MAP_SND},           // ip5: end sound
204         {XGPF_INT, "Move Sound", "", 6 | MAP_SND},          // ip6: move sound
205         {XGPF_INT, "Start Material Ref", "spref_", 7},       // ip7: start texture origin (uses same ids as i2) (spec: use ip8 as tex num)
206         {XGPF_INT, "Start Material Num", "", 8 | MAP_MATERIAL}, // ip8: data component or number/name of flat
207         {XGPF_INT, "End Material Ref", "spref_", 9},         // ip9: end texture origin (uses same ids as i2) (spec: use ip10 as tex num)
208         {XGPF_INT, "End Material Num", "", 10 | MAP_MATERIAL}, // ip10: data component or number/name of flat
209         {XGPF_INT, "Start Type Ref", "lpref_", 11},         // ip11: (plane ref) start sector type (spec: use i12 as type ID)
210         {XGPF_INT, "Start Type Num", "", -1},               // ip12: data component or type ID
211         {XGPF_INT, "End Type Ref", "lpref_", 13},           // ip13: (plane ref) end sector type (spec: use i14 as type ID)
212         {XGPF_INT, "End Type Num", "", -1},                 // ip14: data component or type ID
213         {XGPF_INT, "", "", -1},
214         {XGPF_INT, "", "", -1},
215         {XGPF_INT, "", "", -1},
216         {XGPF_INT, "", "", -1},
217         {XGPF_INT, "", "", -1}
218       }
219     },
220     { de::function_cast<int (*)()>(XSTrav_BuildStairs), XS_InitStairBuilder, TRAV_PLANES, 0, 1, 0, "Build Stairs",
221       // Moves one or more planes, incrementing their height with each move
222        {{XGPF_INT, "Target Ref", "lpref_", 0},              // ip0: (plane ref) plane to start from
223         {XGPF_INT, "Target Num", "", -1},                   // ip1:
224         {XGPF_INT, "Spread Material", "", -1},               // ip2: (true/false) stop when texture changes
225         {XGPF_INT, "Spread Build", "", -1},                 // ip3: (true/false) spread build?
226         {XGPF_INT, "Start Sound", "", 4 | MAP_SND},         // ip4: start build sound (doesn't wait)
227         {XGPF_INT, "Step Start Sound", "", 5 | MAP_SND},    // ip5: step start sound
228         {XGPF_INT, "Step End Sound", "", 6 | MAP_SND},      // ip6: step end sound
229         {XGPF_INT, "Step Move Sound", "", 7 | MAP_SND},     // ip7: step move sound
230         {XGPF_INT, "", "", -1},
231         {XGPF_INT, "", "", -1},
232         {XGPF_INT, "", "", -1},
233         {XGPF_INT, "", "", -1},
234         {XGPF_INT, "", "", -1},
235         {XGPF_INT, "", "", -1},
236         {XGPF_INT, "", "", -1},
237         {XGPF_INT, "", "", -1},
238         {XGPF_INT, "", "", -1},
239         {XGPF_INT, "", "", -1},
240         {XGPF_INT, "", "", -1},
241         {XGPF_INT, "", "", -1}
242       }
243     },
244     { de::function_cast<int (*)()>(XL_DoDamage), NULL, TRAV_NONE, 0, 1, 0, "Damage",
245       // Deals health damage to the activator
246        {{XGPF_INT, "Min Delta", "", -1},                    // ip0: min damage delta
247         {XGPF_INT, "Max Delta", "", -1},                    // ip1: max damage delta
248         {XGPF_INT, "Min Limit", "", -1},                    // ip2: min limit (wont damage if health bellow)
249         {XGPF_INT, "Max Limit", "", -1},                    // ip3: max limit (wont damage if health above)
250         {XGPF_INT, "", "", -1},
251         {XGPF_INT, "", "", -1},
252         {XGPF_INT, "", "", -1},
253         {XGPF_INT, "", "", -1},
254         {XGPF_INT, "", "", -1},
255         {XGPF_INT, "", "", -1},
256         {XGPF_INT, "", "", -1},
257         {XGPF_INT, "", "", -1},
258         {XGPF_INT, "", "", -1},
259         {XGPF_INT, "", "", -1},
260         {XGPF_INT, "", "", -1},
261         {XGPF_INT, "", "", -1},
262         {XGPF_INT, "", "", -1},
263         {XGPF_INT, "", "", -1},
264         {XGPF_INT, "", "", -1},
265         {XGPF_INT, "", "", -1},
266       }
267     },
268     { de::function_cast<int (*)()>(XL_DoPower), NULL, TRAV_NONE, 0, 1, 0, "Power",
269       // Deals armor damage to the activator (must be a player)
270        {{XGPF_INT, "Min Delta", "", -1},                    // ip0: min power delta
271         {XGPF_INT, "Max Delta", "", -1},                    // ip1: max power delta
272         {XGPF_INT, "Min Limit", "", -1},                    // ip2: min limit
273         {XGPF_INT, "Max Limit", "", -1},                    // ip3: max limit
274         {XGPF_INT, "", "", -1},
275         {XGPF_INT, "", "", -1},
276         {XGPF_INT, "", "", -1},
277         {XGPF_INT, "", "", -1},
278         {XGPF_INT, "", "", -1},
279         {XGPF_INT, "", "", -1},
280         {XGPF_INT, "", "", -1},
281         {XGPF_INT, "", "", -1},
282         {XGPF_INT, "", "", -1},
283         {XGPF_INT, "", "", -1},
284         {XGPF_INT, "", "", -1},
285         {XGPF_INT, "", "", -1},
286         {XGPF_INT, "", "", -1},
287         {XGPF_INT, "", "", -1},
288         {XGPF_INT, "", "", -1},
289         {XGPF_INT, "", "", -1}
290       }
291     },
292     { de::function_cast<int (*)()>(XLTrav_ChangeLineType), NULL, TRAV_LINES, 0, 1, 0, "Line Type",
293       // Changes a line's type (must be an XG type)
294        {{XGPF_INT, "Target Ref", "lref_", 0},               // ip0: (line ref) line(s) to change
295         {XGPF_INT, "Target Num", "", -1},                   // ip1:
296         {XGPF_INT, "Line Type", "", -1},                    // ip2: new type (must be an XG line type)
297         {XGPF_INT, "", "", -1},
298         {XGPF_INT, "", "", -1},
299         {XGPF_INT, "", "", -1},
300         {XGPF_INT, "", "", -1},
301         {XGPF_INT, "", "", -1},
302         {XGPF_INT, "", "", -1},
303         {XGPF_INT, "", "", -1},
304         {XGPF_INT, "", "", -1},
305         {XGPF_INT, "", "", -1},
306         {XGPF_INT, "", "", -1},
307         {XGPF_INT, "", "", -1},
308         {XGPF_INT, "", "", -1},
309         {XGPF_INT, "", "", -1},
310         {XGPF_INT, "", "", -1},
311         {XGPF_INT, "", "", -1},
312         {XGPF_INT, "", "", -1},
313         {XGPF_INT, "", "", -1}
314       }
315     },
316     { de::function_cast<int (*)()>(XSTrav_SectorType), NULL, TRAV_SECTORS, 0, 1, 0, "Sector Type",
317       // Changes a sector's type (must be an XG type)
318        {{XGPF_INT, "Target Ref", "lsref_", 0},              // ip0: (sector ref) sector(s) to change
319         {XGPF_INT, "Target Num", "", -1},                   // ip1:
320         {XGPF_INT, "Sector Type", "", -1},                  // ip2: new type (zero or an XG sector type)
321         {XGPF_INT, "", "", -1},
322         {XGPF_INT, "", "", -1},
323         {XGPF_INT, "", "", -1},
324         {XGPF_INT, "", "", -1},
325         {XGPF_INT, "", "", -1},
326         {XGPF_INT, "", "", -1},
327         {XGPF_INT, "", "", -1},
328         {XGPF_INT, "", "", -1},
329         {XGPF_INT, "", "", -1},
330         {XGPF_INT, "", "", -1},
331         {XGPF_INT, "", "", -1},
332         {XGPF_INT, "", "", -1},
333         {XGPF_INT, "", "", -1},
334         {XGPF_INT, "", "", -1},
335         {XGPF_INT, "", "", -1},
336         {XGPF_INT, "", "", -1},
337         {XGPF_INT, "", "", -1}
338       }
339     },
340     { de::function_cast<int (*)()>(XSTrav_SectorLight), NULL, TRAV_SECTORS, 0, 1, 0, "Sector Light",
341       // Change the light level and/or color  of the target sector(s).
342        {{XGPF_INT, "Target Ref", "lsref_", 0},              // ip0: (sector ref) sector(s) to change
343         {XGPF_INT, "Target Num", "", -1},                   // ip1:
344         {XGPF_INT, "Change Light", "", -1},                 // ip2: if non-zero light level will be changed
345         {XGPF_INT, "Change Color", "", -1},                 // ip3: if non-zero colour will be changed
346         {XGPF_INT, "Light Ref", "lightref_", 4},            // ip4: (light ref) sector to get the initial light delta from.
347                                                             //      lightref_none makes ip5 an absolute value
348         {XGPF_INT, "Light Delta", "", -1},                  // ip5: offset to the delta or absolute value
349         {XGPF_INT, "Color Ref", "lightref_", 6},            // ip6: (light ref) sector to get the initial colour deltas from.
350                                                             //      lightref_none makes ip7-9 absolute values
351         {XGPF_INT, "Red Delta", "", -1},                    // ip7: offset to red delta
352         {XGPF_INT, "Green Delta", "", -1},                  // ip8: offset to green delta
353         {XGPF_INT, "Blue Delta", "", -1},                   // ip9: offset to blue delta
354         {XGPF_INT, "", "", -1},
355         {XGPF_INT, "", "", -1},
356         {XGPF_INT, "", "", -1},
357         {XGPF_INT, "", "", -1},
358         {XGPF_INT, "", "", -1},
359         {XGPF_INT, "", "", -1},
360         {XGPF_INT, "", "", -1},
361         {XGPF_INT, "", "", -1},
362         {XGPF_INT, "", "", -1},
363         {XGPF_INT, "", "", -1}
364       }
365     },
366     { de::function_cast<int (*)()>(XLTrav_Activate), NULL, TRAV_LINES, 0, 1, 0, "Activate",
367       // Sends a chain event to all the referenced lines
368        {{XGPF_INT, "Target Ref", "lref_", 0},               // ip0: (line ref) line(s) to activate
369         {XGPF_INT, "Target Num", "", -1},                   // ip1:
370         {XGPF_INT, "", "", -1},
371         {XGPF_INT, "", "", -1},
372         {XGPF_INT, "", "", -1},
373         {XGPF_INT, "", "", -1},
374         {XGPF_INT, "", "", -1},
375         {XGPF_INT, "", "", -1},
376         {XGPF_INT, "", "", -1},
377         {XGPF_INT, "", "", -1},
378         {XGPF_INT, "", "", -1},
379         {XGPF_INT, "", "", -1},
380         {XGPF_INT, "", "", -1},
381         {XGPF_INT, "", "", -1},
382         {XGPF_INT, "", "", -1},
383         {XGPF_INT, "", "", -1},
384         {XGPF_INT, "", "", -1},
385         {XGPF_INT, "", "", -1},
386         {XGPF_INT, "", "", -1},
387         {XGPF_INT, "", "", -1}
388       }
389     },
390     { de::function_cast<int (*)()>(XL_DoKey), NULL, TRAV_NONE, 0, 1, 0, "Key",
391       // Gives/takes keys to/from the activator (must be a player)
392       // Params are bitfields! Bit 1 (0x1) corresponds key 1, bit 2 (0x2) key 2, etc.
393        {{XGPF_INT, "Give Keys", "", -1},                    // ip0: keys to give
394         {XGPF_INT, "Take Keys", "", -1},                    // ip1: keys to take away.
395         {XGPF_INT, "", "", -1},
396         {XGPF_INT, "", "", -1},
397         {XGPF_INT, "", "", -1},
398         {XGPF_INT, "", "", -1},
399         {XGPF_INT, "", "", -1},
400         {XGPF_INT, "", "", -1},
401         {XGPF_INT, "", "", -1},
402         {XGPF_INT, "", "", -1},
403         {XGPF_INT, "", "", -1},
404         {XGPF_INT, "", "", -1},
405         {XGPF_INT, "", "", -1},
406         {XGPF_INT, "", "", -1},
407         {XGPF_INT, "", "", -1},
408         {XGPF_INT, "", "", -1},
409         {XGPF_INT, "", "", -1},
410         {XGPF_INT, "", "", -1},
411         {XGPF_INT, "", "", -1},
412         {XGPF_INT, "", "", -1}
413       }
414     },
415     { de::function_cast<int (*)()>(XLTrav_Music), NULL, TRAV_LINES, 2, 3, 0, "Music",
416       // Changes the music track being played
417        {{XGPF_INT, "Song ID", "ldref_", 0 | MAP_MUS},       // ip0: song id/name or (line data ref from ip2)
418         {XGPF_INT, "Play Looped", "", -1},                  // ip1: non-zero means play looped
419         {XGPF_INT, "Data Ref", "lref_", 2},                 // ip2: (line ref) used with line data ref eg set music track to line-tag
420         {XGPF_INT, "Data Num", "", -1},                     // ip3:
421         {XGPF_INT, "", "", -1},
422         {XGPF_INT, "", "", -1},
423         {XGPF_INT, "", "", -1},
424         {XGPF_INT, "", "", -1},
425         {XGPF_INT, "", "", -1},
426         {XGPF_INT, "", "", -1},
427         {XGPF_INT, "", "", -1},
428         {XGPF_INT, "", "", -1},
429         {XGPF_INT, "", "", -1},
430         {XGPF_INT, "", "", -1},
431         {XGPF_INT, "", "", -1},
432         {XGPF_INT, "", "", -1},
433         {XGPF_INT, "", "", -1},
434         {XGPF_INT, "", "", -1},
435         {XGPF_INT, "", "", -1},
436         {XGPF_INT, "", "", -1}
437       }
438     },
439     { de::function_cast<int (*)()>(XLTrav_LineCount), NULL, TRAV_LINES, 0, 1, 0, "Line Count",
440       // Changes the XG line(s)' internal activation counter
441        {{XGPF_INT, "Target Ref", "lref_", 0},               // ip0: (line ref) line(s) to change
442         {XGPF_INT, "Target Num", "", -1},                   // ip1:
443         {XGPF_INT, "Set Absolute", "", -1},                 // ip2: non-zero makes ip3 absolute
444         {XGPF_INT, "Count Delta", "", -1},                  // ip3: count delta or absolute
445         {XGPF_INT, "", "", -1},
446         {XGPF_INT, "", "", -1},
447         {XGPF_INT, "", "", -1},
448         {XGPF_INT, "", "", -1},
449         {XGPF_INT, "", "", -1},
450         {XGPF_INT, "", "", -1},
451         {XGPF_INT, "", "", -1},
452         {XGPF_INT, "", "", -1},
453         {XGPF_INT, "", "", -1},
454         {XGPF_INT, "", "", -1},
455         {XGPF_INT, "", "", -1},
456         {XGPF_INT, "", "", -1},
457         {XGPF_INT, "", "", -1},
458         {XGPF_INT, "", "", -1},
459         {XGPF_INT, "", "", -1},
460         {XGPF_INT, "", "", -1}
461       }
462     },
463     { de::function_cast<int (*)()>(XLTrav_LeaveMap), NULL, TRAV_LINES, 1, 2, 0, "Leave Map",
464       // Exits the current map
465        {{XGPF_INT, "Secret Exit", "", -1},                  // ip0: non-zero goto secret map
466         {XGPF_INT, "Data Ref", "lref_", 1},                 // ip1: (line ref) line to acquire (line data ref) from
467         {XGPF_INT, "Data Num", "", -1},                     // ip2:
468         {XGPF_INT, "Goto Map", "ldref_", 3},                // ip3: map ID or (line data ref from ip1)
469         {XGPF_INT, "", "", -1},
470         {XGPF_INT, "", "", -1},
471         {XGPF_INT, "", "", -1},
472         {XGPF_INT, "", "", -1},
473         {XGPF_INT, "", "", -1},
474         {XGPF_INT, "", "", -1},
475         {XGPF_INT, "", "", -1},
476         {XGPF_INT, "", "", -1},
477         {XGPF_INT, "", "", -1},
478         {XGPF_INT, "", "", -1},
479         {XGPF_INT, "", "", -1},
480         {XGPF_INT, "", "", -1},
481         {XGPF_INT, "", "", -1},
482         {XGPF_INT, "", "", -1},
483         {XGPF_INT, "", "", -1},
484         {XGPF_INT, "", "", -1}
485       }
486     },
487     { de::function_cast<int (*)()>(XLTrav_DisableLine), NULL, TRAV_LINES, 0, 1, 0, "Disable Line",
488       // Disables the referenced line(s) if active
489        {{XGPF_INT, "Target Ref", "lref_", 0},               // ip0: (line ref) line(s) to disable
490         {XGPF_INT, "Target Num", "", -1},                   // ip1:
491         {XGPF_INT, "", "", -1},
492         {XGPF_INT, "", "", -1},
493         {XGPF_INT, "", "", -1},
494         {XGPF_INT, "", "", -1},
495         {XGPF_INT, "", "", -1},
496         {XGPF_INT, "", "", -1},
497         {XGPF_INT, "", "", -1},
498         {XGPF_INT, "", "", -1},
499         {XGPF_INT, "", "", -1},
500         {XGPF_INT, "", "", -1},
501         {XGPF_INT, "", "", -1},
502         {XGPF_INT, "", "", -1},
503         {XGPF_INT, "", "", -1},
504         {XGPF_INT, "", "", -1},
505         {XGPF_INT, "", "", -1},
506         {XGPF_INT, "", "", -1},
507         {XGPF_INT, "", "", -1},
508         {XGPF_INT, "", "", -1}
509       }
510     },
511     { de::function_cast<int (*)()>(XLTrav_EnableLine), NULL, TRAV_LINES, 0, 1, 0, "Enable Line",
512       // Enables the referenced line(s) if active.
513        {{XGPF_INT, "Target Ref", "lref_", 0},               // ip0: (line ref) line(s) to enable
514         {XGPF_INT, "Target Num", "", -1},                   // ip1:
515         {XGPF_INT, "", "", -1},
516         {XGPF_INT, "", "", -1},
517         {XGPF_INT, "", "", -1},
518         {XGPF_INT, "", "", -1},
519         {XGPF_INT, "", "", -1},
520         {XGPF_INT, "", "", -1},
521         {XGPF_INT, "", "", -1},
522         {XGPF_INT, "", "", -1},
523         {XGPF_INT, "", "", -1},
524         {XGPF_INT, "", "", -1},
525         {XGPF_INT, "", "", -1},
526         {XGPF_INT, "", "", -1},
527         {XGPF_INT, "", "", -1},
528         {XGPF_INT, "", "", -1},
529         {XGPF_INT, "", "", -1},
530         {XGPF_INT, "", "", -1},
531         {XGPF_INT, "", "", -1},
532         {XGPF_INT, "", "", -1}
533       }
534     },
535     { de::function_cast<int (*)()>(XL_DoExplode), NULL, TRAV_NONE, 0, 1, 0, "Explode",
536       // Explodes the activator (no params).
537       {{XGPF_INT, "", "", -1},
538        {XGPF_INT, "", "", -1},
539        {XGPF_INT, "", "", -1},
540        {XGPF_INT, "", "", -1},
541        {XGPF_INT, "", "", -1},
542        {XGPF_INT, "", "", -1},
543        {XGPF_INT, "", "", -1},
544        {XGPF_INT, "", "", -1},
545        {XGPF_INT, "", "", -1},
546        {XGPF_INT, "", "", -1},
547        {XGPF_INT, "", "", -1},
548        {XGPF_INT, "", "", -1},
549        {XGPF_INT, "", "", -1},
550        {XGPF_INT, "", "", -1},
551        {XGPF_INT, "", "", -1},
552        {XGPF_INT, "", "", -1},
553        {XGPF_INT, "", "", -1},
554        {XGPF_INT, "", "", -1},
555        {XGPF_INT, "", "", -1},
556        {XGPF_INT, "", "", -1},
557       }
558     },
559     { de::function_cast<int (*)()>(XSTrav_PlaneMaterial), NULL, TRAV_PLANES, 0, 1, 0, "Plane Material",
560       // Change the material and/or surface color of a plane.
561        {{XGPF_INT, "Target Ref", "lpref_", 0},              // ip0 : (plane ref) plane(s) to change
562         {XGPF_INT, "Target Num", "", -1},                   // ip1 : ref data
563         {XGPF_INT, "Material Ref", "spref_", 2},            // ip2 : Texture ref
564         {XGPF_INT, "Material Num", "", 3 | MAP_MATERIAL},   // ip3 : texture number (flat), used with SPREF_NONE
565         {XGPF_INT, "Red Delta", "", -1},                    // ip4 : plane surface color (red)
566         {XGPF_INT, "Green Delta", "", -1},                  // ip5 : "" (green)
567         {XGPF_INT, "Blue Delta", "", -1},                   // ip6 : "" (blue)
568         {XGPF_INT, "Change Color", "", -1},                 // ip7: if non-zero tint color will be changed
569         {XGPF_INT, "", "", -1},
570         {XGPF_INT, "", "", -1},
571         {XGPF_INT, "", "", -1},
572         {XGPF_INT, "", "", -1},
573         {XGPF_INT, "", "", -1},
574         {XGPF_INT, "", "", -1},
575         {XGPF_INT, "", "", -1},
576         {XGPF_INT, "", "", -1},
577         {XGPF_INT, "", "", -1},
578         {XGPF_INT, "", "", -1},
579         {XGPF_INT, "", "", -1},
580         {XGPF_INT, "", "", -1}
581       }
582     },
583     { de::function_cast<int (*)()>(XLTrav_ChangeWallMaterial), NULL, TRAV_LINES, 0, 1, 0, "Wall Material",
584       // Changes material(s) on the referenced line(s).
585       // Changes surface colour(s), alpha, mid textue blendmode and side flags
586        {{XGPF_INT, "Target Ref", "lref_", 0},               // ip0: (line ref) line(s) to change
587         {XGPF_INT, "Target Num", "", -1},                   // ip1:
588         {XGPF_INT, "Side Num", "", -1},                     // ip2: non-zero change the back side
589         {XGPF_INT, "Top Material", "", 3 | MAP_MATERIAL},    // ip3: top texture to change to (blank indicates no change)
590         {XGPF_INT, "Middle Material", "", 4 | MAP_MATERIAL}, // ip4: middle texture to change to (blank indicates no change)
591         {XGPF_INT, "Bottom Material", "", 5 | MAP_MATERIAL}, // ip5: bottom texture to change to (blank indicates no change)
592         {XGPF_INT, "Set Mid If None", "", -1},              // ip6: set mid texture even if previously zero
593         {XGPF_INT, "Sidedef Flags", "sdf_", 7},             // ip7: (sdf_) side flags (used with surface colour blending, fullbright etc)
594         {XGPF_INT, "Middle Blendmode", "bm_", 8},           // ip8: (bm_) middle texture blendmode
595         {XGPF_INT, "Top Red Delta", "", -1},                // ip9:
596         {XGPF_INT, "Top Green Delta", "", -1},              // ip10:
597         {XGPF_INT, "Top Blue Delta", "", -1},               // ip11:
598         {XGPF_INT, "Middle Red Delta", "", -1},             // ip12:
599         {XGPF_INT, "Middle Green Delta", "", -1},           // ip13:
600         {XGPF_INT, "Middle Blue Delta", "", -1},            // ip14:
601         {XGPF_INT, "Middle Alpha Delta", "", -1},           // ip15:
602         {XGPF_INT, "Bottom Red Delta", "", -1},             // ip16:
603         {XGPF_INT, "Bottom Green Delta", "", -1},           // ip17:
604         {XGPF_INT, "Bottom Blue Delta", "", -1},            // ip18:
605         {XGPF_INT, "", "", -1}
606       }
607     },
608     { de::function_cast<int (*)()>(XL_DoCommand), NULL, TRAV_NONE, 0, 1, 0, "Command",
609       // Executes a console command (CCmd)
610       {{XGPF_INT, "", "", -1},
611        {XGPF_INT, "", "", -1},
612        {XGPF_INT, "", "", -1},
613        {XGPF_INT, "", "", -1},
614        {XGPF_INT, "", "", -1},
615        {XGPF_INT, "", "", -1},
616        {XGPF_INT, "", "", -1},
617        {XGPF_INT, "", "", -1},
618        {XGPF_INT, "", "", -1},
619        {XGPF_INT, "", "", -1},
620        {XGPF_INT, "", "", -1},
621        {XGPF_INT, "", "", -1},
622        {XGPF_INT, "", "", -1},
623        {XGPF_INT, "", "", -1},
624        {XGPF_INT, "", "", -1},
625        {XGPF_INT, "", "", -1},
626        {XGPF_INT, "", "", -1},
627        {XGPF_INT, "", "", -1},
628        {XGPF_INT, "", "", -1},
629        {XGPF_INT, "", "", -1},
630       }
631     },
632     { de::function_cast<int (*)()>(XSTrav_SectorSound), NULL, TRAV_SECTORS, 0, 1, 0, "Sector Sound",
633       // Plays a sound in sector(s)
634        {{XGPF_INT, "Target Ref", "lsref_", 0},              // ip0: (sector ref) sector(s) to play the sound in
635         {XGPF_INT, "Target Num", "", -1},                   // ip1:
636         {XGPF_INT, "Sound ID", "", 2 | MAP_SND},            // ip2: sound name/id to play
637         {XGPF_INT, "Origin", "", -1},                       // ip3: non-zero = play from a specific origin (1=floor, 2=ceiling) else 0=center
638         {XGPF_INT, "", "", -1},
639         {XGPF_INT, "", "", -1},
640         {XGPF_INT, "", "", -1},
641         {XGPF_INT, "", "", -1},
642         {XGPF_INT, "", "", -1},
643         {XGPF_INT, "", "", -1},
644         {XGPF_INT, "", "", -1},
645         {XGPF_INT, "", "", -1},
646         {XGPF_INT, "", "", -1},
647         {XGPF_INT, "", "", -1},
648         {XGPF_INT, "", "", -1},
649         {XGPF_INT, "", "", -1},
650         {XGPF_INT, "", "", -1},
651         {XGPF_INT, "", "", -1},
652         {XGPF_INT, "", "", -1},
653         {XGPF_INT, "", "", -1}
654       }
655     },
656     { de::function_cast<int (*)()>(XSTrav_MimicSector), NULL, TRAV_SECTORS, 0, 1, 0, "Mimic Sector",
657       // Copies all properties from target sector to destination sector(s)
658        {{XGPF_INT, "Target Ref", "lsref_", 0},              // ip0: (sector ref) sector(s) to change
659         {XGPF_INT, "Target Num", "", -1},                   // ip1:
660         {XGPF_INT, "Mimic Ref", "spref_", 2},               // ip2: (spref) sector to mimic
661         {XGPF_INT, "Mimic Num", "", -1},                    // ip3:
662         {XGPF_INT, "", "", -1},
663         {XGPF_INT, "", "", -1},
664         {XGPF_INT, "", "", -1},
665         {XGPF_INT, "", "", -1},
666         {XGPF_INT, "", "", -1},
667         {XGPF_INT, "", "", -1},
668         {XGPF_INT, "", "", -1},
669         {XGPF_INT, "", "", -1},
670         {XGPF_INT, "", "", -1},
671         {XGPF_INT, "", "", -1},
672         {XGPF_INT, "", "", -1},
673         {XGPF_INT, "", "", -1},
674         {XGPF_INT, "", "", -1},
675         {XGPF_INT, "", "", -1},
676         {XGPF_INT, "", "", -1},
677         {XGPF_INT, "", "", -1}
678       }
679     },
680     { de::function_cast<int (*)()>(XSTrav_Teleport), NULL, TRAV_SECTORS, 0, 1, 0, "Teleport",
681       // Teleports the activator to the first teleport exit in the target sector
682        {{XGPF_INT, "Target Ref", "lsref_", 0},              // ip0: (sector ref) sector(s) to teleport to (first acceptable target is used)
683         {XGPF_INT, "Target Num", "", -1},                   // ip1:
684         {XGPF_INT, "No Flash", "", -1},                     // ip2: non-zero = no flash (or sound)
685         {XGPF_INT, "No Sound", "", -1},                     // ip3: non-zero = no sound
686         {XGPF_INT, "Always Stomp", "", -1},                 // ip4: non-zero = Always telefrag
687         {XGPF_INT, "", "", -1},
688         {XGPF_INT, "", "", -1},
689         {XGPF_INT, "", "", -1},
690         {XGPF_INT, "", "", -1},
691         {XGPF_INT, "", "", -1},
692         {XGPF_INT, "", "", -1},
693         {XGPF_INT, "", "", -1},
694         {XGPF_INT, "", "", -1},
695         {XGPF_INT, "", "", -1},
696         {XGPF_INT, "", "", -1},
697         {XGPF_INT, "", "", -1},
698         {XGPF_INT, "", "", -1},
699         {XGPF_INT, "", "", -1},
700         {XGPF_INT, "", "", -1},
701         {XGPF_INT, "", "", -1}
702       }
703     },
704     { de::function_cast<int (*)()>(XLTrav_LineTeleport), NULL, TRAV_LINES, 0, 1, 1 | XLE_CROSS, "Line Teleport",
705       // Teleports the activator to the referenced line
706        {{XGPF_INT, "Target Ref", "lref_", 0},               // ip0: (line ref) teleport destination
707         {XGPF_INT, "Target Num", "", -1},                   // ip1:
708         {XGPF_INT, "No Flash", "", -1},                     // ip2: non-zero = spawn MT_TFOG
709         {XGPF_INT, "Teleport Sound", "", 3 | MAP_SND},      // ip3: sound ID/name to play (or silent)
710         {XGPF_INT, "Exit Side", "", -1},                    // ip4: non-zero = exit from the back of the target line
711         {XGPF_INT, "Always Stomp", "", -1},                 // ip5: non-zero = Always telefrag
712         {XGPF_INT, "", "", -1},
713         {XGPF_INT, "", "", -1},
714         {XGPF_INT, "", "", -1},
715         {XGPF_INT, "", "", -1},
716         {XGPF_INT, "", "", -1},
717         {XGPF_INT, "", "", -1},
718         {XGPF_INT, "", "", -1},
719         {XGPF_INT, "", "", -1},
720         {XGPF_INT, "", "", -1},
721         {XGPF_INT, "", "", -1},
722         {XGPF_INT, "", "", -1},
723         {XGPF_INT, "", "", -1},
724         {XGPF_INT, "", "", -1},
725         {XGPF_INT, "", "", -1}
726       }
727     }
728 };
729 
XG_Register()730 void XG_Register()
731 {
732     C_VAR_INT("xg-dev", &xgDev, CVF_NO_ARCHIVE, 0, 1);
733 
734     C_CMD("movefloor",  0, MovePlane);
735     C_CMD("moveceil",   0, MovePlane);
736     C_CMD("movesec",    0, MovePlane);
737 }
738 
739 /**
740  * Init XG data for the map.
741  */
XG_Init(void)742 void XG_Init(void)
743 {
744     XL_Init(); // Init lines.
745     XS_Init(); // Init sectors.
746 }
747 
XG_Ticker(void)748 void XG_Ticker(void)
749 {
750     // Nothing to do.
751 }
752 
753 /**
754  * This is called during an engine reset. Disables all XG functionality!
755  */
XG_Update(void)756 void XG_Update(void)
757 {
758     // Clients rely on the server, they don't do XG themselves.
759     if(IS_CLIENT)
760         return;
761 
762     XG_ReadTypes();
763     XS_Update();
764     XL_Update();
765 }
766 
767 /**
768  * Adds the given binary format line type to the generated types array
769  */
XL_AddAutoGenType(linetype_t *)770 int XL_AddAutoGenType(linetype_t * /*newtype*/)
771 {
772     return true;
773 }
774 
775 /**
776  * Converts a line ID number to a line type (BOOM support).
777  */
XL_AutoGenType(int,linetype_t *)778 int XL_AutoGenType(int /*id*/, linetype_t * /*outptr*/)
779 {
780     return false; // Cos we don't work yet.
781 
782 #if 0
783     linetype_t *l;
784 
785     // Do the magic
786     // is the ID in the range 12160 -> 32768
787     if(!(id >= 12160 && id <= 32768))
788         return false;
789 
790     // Have we already generated a line of this type?
791     if(1)
792     {
793         // Then return a ptr to it
794         outptr = l;
795         return true;
796     }
797 
798     // Which bitfield format are we generating from?
799     if(1) //XG format
800     {
801         /*
802          // Generate the new line type
803         l->id = id;
804         l->flags = def->flags[0];
805         l->flags2 = def->flags[1];
806         l->flags3 = def->flags[2];
807         l->lineClass = def->lineClass;
808         l->actType = def->actType;
809         l->actCount = def->actCount;
810         l->actTime = def->actTime;
811      //   l->actTag = def->actTag;
812         for(i = 0; i < 10; i++)
813         {
814             if(i == 9)
815                 l->aparm[i] = Def_GetMobjNum(def->aparm9);
816             else
817                 l->aparm[i] = def->aparm[i];
818         }
819      //   l->tickerStart = def->tickerStart;
820      //   l->tickerEnd = def->tickerEnd;
821      //   l->tickerInterval = def->tickerInterval;
822         l->actSound = Friendly(Def_GetSoundNum(def->actSound));
823         l->deactSound = Friendly(Def_GetSoundNum(def->deactSound));
824         l->evChain = def->evChain;
825         l->actChain = def->actChain;
826         l->deactChain = def->deactChain;
827         l->actLineType = def->actLineType;
828         l->deactLineType = def->deactLineType;
829         l->wallSection = def->wallSection;
830         l->actTex = Friendly(R_CheckTextureNumForName(def->actTex));
831         l->deactTex = Friendly(R_CheckTextureNumForName(def->deactTex));
832         l->actMsg = def->actMsg;
833         l->deactMsg = def->deactMsg;
834         l->texMoveAngle = def->texMoveAngle;
835         l->texMoveSpeed = def->texMoveSpeed;*/
836 
837         // Add it to the generated line types array
838         XL_AddAutoGenType(l);
839         outptr = l;
840     }
841 #if __JDOOM__ || __JDOOM64__
842     else // BOOM format
843     {
844         // Generate the new line type
845         outptr = l;
846     }
847 #endif
848 
849     return true;
850 #endif
851  }
852 
853 /**
854  * @return              @c true, if the type is defined.
855  */
XL_GetType(int id)856 linetype_t* XL_GetType(int id)
857 {
858     linetype_t*         ptr;
859     char                buff[6];
860 
861     // Try finding it from the DDXGDATA lump.
862     ptr = XG_GetLumpLine(id);
863     if(ptr)
864     {   // Got it!
865         memcpy(&typebuffer, ptr, sizeof(*ptr));
866         return &typebuffer;
867     }
868 
869     // Does Doomsday have a definition for this?
870     dd_snprintf(buff, 6, "%i", id);
871 
872     if(Def_Get(DD_DEF_LINE_TYPE, buff, &typebuffer))
873         return &typebuffer;
874 
875     // Is this a type we can generate automatically?
876     if(XL_AutoGenType(id, &typebuffer))
877         return &typebuffer;
878 
879     // A definition was not found.
880     return NULL;
881 }
882 
XG_RandomInt(int min,int max)883 int XG_RandomInt(int min, int max)
884 {
885     if(max == min) return max;
886 
887     float x = M_Random() / 256.0f; // Never reaches 1.
888     return (int) (min + x * (max - min) + x);
889 }
890 
XG_RandomPercentFloat(float value,int percent)891 float XG_RandomPercentFloat(float value, int percent)
892 {
893     float i = (2 * M_Random() / 255.0f - 1) * percent / 100.0f;
894     return value * (1 + i);
895 }
896 
findXLThinker(thinker_t * th,void * context)897 int findXLThinker(thinker_t *th, void *context)
898 {
899     xlthinker_t *xl = (xlthinker_t *) th;
900 
901     if(xl->line == (Line *) context)
902         return true; // Stop iteration, we've found it.
903 
904     return false; // Continue iteration.
905 }
906 
XL_SetLineType(Line * line,int id)907 void XL_SetLineType(Line *line, int id)
908 {
909     LOG_AS("XL_SetLineType");
910 
911     xline_t *xline = P_ToXLine(line);
912 
913     if(XL_GetType(id))
914     {
915         xline->special = id;
916 
917         // Allocate memory for the line type data.
918         if(!xline->xg)
919         {
920             xline->xg = (xgline_t *)Z_Calloc(sizeof(xgline_t), PU_MAP, 0);
921         }
922 
923         // Init the extended line state.
924         xline->xg->disabled    = false;
925         xline->xg->timer       = 0;
926         xline->xg->tickerTimer = 0;
927         std::memcpy(&xline->xg->info, &typebuffer, sizeof(linetype_t));
928 
929         // Initial active state.
930         xline->xg->active      = (typebuffer.flags & LTF_ACTIVE) != 0;
931         xline->xg->activator   = dummyThing;
932 
933         LOG_MAP_MSG_XGDEVONLY2("Line %i (%s), ID %i",
934                 P_ToIndex(line)
935                 << xgClasses[xline->xg->info.lineClass].className
936                 << id);
937 
938         // If there is not already an xlthinker for this line, create one.
939         if(!Thinker_Iterate(XL_Thinker, findXLThinker, line))
940         {
941             // Not created one yet.
942             /*xlthinker_t *xl = (xlthinker_t *)Z_Calloc(sizeof(*xl), PU_MAP, 0);
943 
944             xl->thinker.function = XL_Thinker;*/
945 
946             ThinkerT<xlthinker_t> xl(Thinker::AllocateMemoryZone);
947             xl.function = XL_Thinker;
948             xl->line = line;
949 
950             Thinker_Add(xl.Thinker::take());
951         }
952     }
953     else if(id)
954     {
955         LOG_MAP_MSG_XGDEVONLY2("Line %i, type %i NOT DEFINED", P_ToIndex(line) << id);
956     }
957 }
958 
XL_Init()959 void XL_Init()
960 {
961     dummyThing.Thinker::zap();
962 
963     // Clients rely on the server, they don't do XG themselves.
964     if(IS_CLIENT) return;
965 
966     for(int i = 0; i < numlines; ++i)
967     {
968         Line *line = (Line *)P_ToPtr(DMU_LINE, i);
969         P_ToXLine(line)->xg = 0;
970 
971         XL_SetLineType(line, P_ToXLine(line)->special);
972     }
973 }
974 
XL_TraversePlanes(Line * line,int refType,int ref,void * data,void * context,dd_bool travsectors,mobj_t * activator,PlaneTraverserFunc func)975 int XL_TraversePlanes(Line *line, int refType, int ref, void *data, void *context,
976     dd_bool travsectors, mobj_t *activator, PlaneTraverserFunc func)
977 {
978     LOG_AS(travsectors? "XL_TraverseSectors" : "XL_TraversePlanes");
979 
980     int tag = 0;
981     mobj_t *mo = NULL;
982     dd_bool ok, findSecTagged;
983 
984     if(xgDev)
985     {
986         char buff[50];
987 
988         if(ref)
989         {
990             sprintf(buff, ": %i", ref);
991         }
992 
993         LOG_MAP_MSG_XGDEVONLY2("Line %i, ref (%s%s)",
994                P_ToIndex(line)
995                << (travsectors? LSREFTYPESTR(refType) : LPREFTYPESTR(refType))
996                << (ref? buff: ""));
997     }
998 
999     if(refType == LPREF_NONE)
1000         return false; // This is not a reference!
1001 
1002     Sector *frontSec = (Sector *)P_GetPtrp(line, DMU_FRONT_SECTOR);
1003     Sector *backSec  = (Sector *)P_GetPtrp(line, DMU_BACK_SECTOR);
1004 
1005     // References to a single plane
1006     if(refType == LPREF_MY_FLOOR || refType == LPREF_MY_CEILING)
1007     {
1008         if(frontSec)
1009         {
1010             return func(frontSec, refType == LPREF_MY_CEILING? true:false,
1011                         data, context, activator);
1012         }
1013 
1014         LOG_MAP_MSG_XGDEVONLY2("Line %i has no front sector!", P_ToIndex(line));
1015     }
1016 
1017     if(refType == LPREF_BACK_FLOOR || refType == LPREF_BACK_CEILING)
1018     {
1019         if(backSec)
1020         {
1021             return func(backSec, refType == LPREF_BACK_CEILING? true:false,
1022                         data, context, activator);
1023         }
1024 
1025         LOG_MAP_MSG_XGDEVONLY2("Line %i has no back sector!", P_ToIndex(line));
1026     }
1027 
1028     if(refType == LPREF_INDEX_FLOOR)
1029     {
1030         return func((Sector *)P_ToPtr(DMU_SECTOR, ref), false, data, context,
1031                     activator);
1032     }
1033 
1034     if(refType == LPREF_INDEX_CEILING)
1035     {
1036         return func((Sector *)P_ToPtr(DMU_SECTOR, ref), true, data, context,
1037                     activator);
1038     }
1039 
1040     // Can we use the tagged sector lists?
1041     findSecTagged = false;
1042     if(refType == LPREF_TAGGED_FLOORS || refType == LPREF_TAGGED_CEILINGS)
1043     {
1044         findSecTagged = true;
1045         tag = ref;
1046     }
1047     else if(refType == LPREF_LINE_TAGGED_FLOORS ||
1048             refType == LPREF_LINE_TAGGED_CEILINGS)
1049     {
1050         findSecTagged = true;
1051         tag = P_ToXLine(line)->tag;
1052     }
1053 
1054     // References to multiple planes
1055     if(findSecTagged)
1056     {
1057         // Use tagged sector lists for these (speed).
1058         if(iterlist_t *list = P_GetSectorIterListForTag(tag, false))
1059         {
1060             // Find the first sector with the tag.
1061             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
1062             IterList_RewindIterator(list);
1063 
1064             Sector *sec;
1065             while((sec = (Sector *)IterList_MoveIterator(list)) != NULL)
1066             {
1067                 //xsector_t *xsec = P_ToXSector(sec);
1068 
1069                 if(refType == LPREF_TAGGED_FLOORS || refType == LPREF_TAGGED_CEILINGS)
1070                 {
1071                     if(!func(sec, refType == LPREF_TAGGED_CEILINGS, data,
1072                              context, activator))
1073                     {
1074                         return false;
1075                     }
1076                 }
1077 
1078                 if(refType == LPREF_LINE_TAGGED_FLOORS ||
1079                    refType == LPREF_LINE_TAGGED_CEILINGS)
1080                 {
1081                     if(!func(sec, refType == LPREF_LINE_TAGGED_CEILINGS,
1082                              data, context, activator))
1083                     {
1084                         return false;
1085                     }
1086                 }
1087             }
1088         }
1089     }
1090     else
1091     {
1092         for(int i = 0; i < numsectors; ++i)
1093         {
1094             Sector *sec     = (Sector *)P_ToPtr(DMU_SECTOR, i);
1095             xsector_t *xsec = P_ToXSector(sec);
1096 
1097             if(refType == LPREF_ALL_FLOORS || refType == LPREF_ALL_CEILINGS)
1098             {
1099                 if(!func(sec, refType == LPREF_ALL_CEILINGS, data,
1100                          context, activator))
1101                 {
1102                     return false;
1103                 }
1104             }
1105 
1106             if((refType == LPREF_ACT_TAGGED_FLOORS ||
1107                 refType == LPREF_ACT_TAGGED_CEILINGS) && xsec->xg &&
1108                 xsec->xg->info.actTag == ref)
1109             {
1110                 if(!func(sec, refType == LPREF_ACT_TAGGED_CEILINGS, data,
1111                    context, activator))
1112                 {
1113                    return false;
1114                 }
1115             }
1116 
1117             // Reference all sectors with (at least) one mobj of specified
1118             // type inside.
1119             if(refType == LPREF_THING_EXIST_FLOORS ||
1120                refType == LPREF_THING_EXIST_CEILINGS)
1121             {
1122                 ok = true;
1123 
1124                 for(mo = (mobj_t *)P_GetPtrp(sec, DMT_MOBJS); ok && mo; mo = mo->sNext)
1125                 {
1126                     if(mo->type == P_ToXLine(line)->xg->info.aparm[9])
1127                     {
1128                         LOG_MAP_MSG_XGDEVONLY2("Thing of type %i found in sector id %i",
1129                                P_ToXLine(line)->xg->info.aparm[9] << i);
1130 
1131                         if(!func(sec, refType == LPREF_THING_EXIST_CEILINGS,
1132                                  data, context, activator))
1133                         {
1134                             return false;
1135                         }
1136 
1137                         ok = false;
1138                     }
1139                 }
1140             }
1141 
1142             // Reference all sectors with NONE of the specified mobj type
1143             // inside.
1144             if(refType == LPREF_THING_NOEXIST_FLOORS ||
1145                refType == LPREF_THING_NOEXIST_CEILINGS)
1146             {
1147                 ok = true;
1148 
1149                 for(mo = (mobj_t *)P_GetPtrp(sec, DMT_MOBJS); ok && mo; mo = mo->sNext)
1150                 {
1151                     if(mo->type != P_ToXLine(line)->xg->info.aparm[9])
1152                     {
1153                         continue;
1154                     }
1155 
1156                     ok = false;
1157                 }
1158 
1159                 if(ok)
1160                 {
1161                     LOG_MAP_MSG_XGDEVONLY2("No things of type %i found in sector id %i",
1162                            P_ToXLine(line)->xg->info.aparm[9] << i);
1163 
1164                     if(!func(sec, refType == LPREF_THING_NOEXIST_CEILINGS,
1165                              data, context, activator))
1166                     {
1167                         return false;
1168                     }
1169                 }
1170             }
1171         }
1172     }
1173 
1174     return true;
1175 }
1176 
XL_TraverseLines(Line * line,int rtype,int ref,void * data,void * context,mobj_t * activator,LineTraverserFunc func)1177 int XL_TraverseLines(Line* line, int rtype, int ref, void* data,
1178                      void* context, mobj_t* activator, LineTraverserFunc func)
1179 {
1180     LOG_AS("XL_TraverseLines");
1181 
1182     int i;
1183     int tag = 0;
1184     int reftype = rtype;
1185     char buff[50];
1186     Line *iter = NULL;
1187     dd_bool findLineTagged;
1188 
1189     // Binary XG data from DD_XGDATA uses the old flag values.
1190     // Add one to the ref type.
1191     if(xgDataLumps)
1192         reftype+= 1;
1193 
1194     if(ref)
1195         sprintf(buff," : %i",ref);
1196 
1197     LOG_MAP_MSG_XGDEVONLY2("Line %i, ref (%s%s)",
1198            P_ToIndex(line) << LREFTYPESTR(reftype) << (ref? buff : ""));
1199 
1200     if(reftype == LREF_NONE)
1201         return func(NULL, true, data, context, activator); // Not a real reference
1202 
1203     // References to single lines
1204     if(reftype == LREF_SELF) // Traversing self is simple.
1205         return func(line, true, data, context, activator);
1206 
1207     if(reftype == LREF_INDEX)
1208         return func((Line *)P_ToPtr(DMU_LINE, ref), true, data, context, activator);
1209 
1210     // Can we use the tagged line lists?
1211     findLineTagged = false;
1212     if(reftype == LREF_TAGGED)
1213     {
1214         findLineTagged = true;
1215         tag = ref;
1216     }
1217     else if(reftype == LREF_LINE_TAGGED)
1218     {
1219         findLineTagged = true;
1220         tag = P_ToXLine(line)->tag;
1221     }
1222 
1223     // References to multiple lines
1224     if(findLineTagged)
1225     {
1226         // Use tagged line lists for these (speed).
1227         if(iterlist_t *list = P_GetLineIterListForTag(tag, false))
1228         {
1229             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
1230             IterList_RewindIterator(list);
1231             while((iter = (Line *)IterList_MoveIterator(list)) != NULL)
1232             {
1233                 if(reftype == LREF_TAGGED)
1234                 {
1235                     if(!func(iter, true, data, context, activator))
1236                         return false;
1237                 }
1238                 else if(reftype == LREF_LINE_TAGGED)
1239                 {
1240                     // Ref is true if line itself should be excluded.
1241                     if(!ref || iter != line)
1242                     {
1243                         if(!func(iter, true, data, context, activator))
1244                             return false;
1245                     }
1246                 }
1247             }
1248         }
1249     }
1250     else
1251     {
1252         for(i = 0; i < numlines; ++i)
1253         {
1254             iter = (Line *)P_ToPtr(DMU_LINE, i);
1255             if(reftype == LREF_ALL)
1256             {
1257                 if(!func(iter, true, data, context, activator))
1258                     return false;
1259             }
1260             else if(reftype == LREF_ACT_TAGGED)
1261             {
1262                 xline_t *xl = P_ToXLine(iter);
1263 
1264                 if(xl->xg && xl->xg->info.actTag == ref)
1265                 {
1266                     if(!func(iter, true, data, context, activator))
1267                         return false;
1268                 }
1269             }
1270         }
1271     }
1272     return true;
1273 }
1274 
1275 /**
1276  * @return  Value as determined by the reference type from the specified line, using data
1277  *          from either the line itself or context (will always be linetype_t).
1278  */
XL_ValidateLineRef(Line * line,int reftype,void *,char const * parmname)1279 int XL_ValidateLineRef(Line *line, int reftype, void * /*context*/, char const *parmname)
1280 {
1281     LOG_AS("XL_ValidateLineRef");
1282 
1283     int answer = 0;
1284     Side *side;
1285 
1286     switch(reftype)
1287     {
1288     case LDREF_ID: // Line ID.
1289         answer = P_ToIndex(line);
1290         LOG_MAP_MSG_XGDEVONLY2("Using Line ID (%i) as %s", answer << parmname);
1291         break;
1292 
1293     case LDREF_SPECIAL: // Line Special.
1294         answer = P_ToXLine(line)->special;
1295         LOG_MAP_MSG_XGDEVONLY2("Using Line Special (%i) as %s", answer << parmname);
1296         break;
1297 
1298     case LDREF_TAG: // Line Tag.
1299         answer = P_ToXLine(line)->tag;
1300         LOG_MAP_MSG_XGDEVONLY2("Using Line Tag (%i) as %s", answer << parmname);
1301         break;
1302 
1303     case LDREF_ACTTAG: // Line ActTag.
1304         if(!P_ToXLine(line)->xg)
1305         {
1306             LOG_MAP_MSG_XGDEVONLY("REFERENCE NOT AN XG LINE");
1307             break;
1308         }
1309 
1310         if(!P_ToXLine(line)->xg->info.actTag)
1311         {
1312             LOG_MAP_MSG_XGDEVONLY("REFERENCE DOESNT HAVE AN ACT TAG");
1313             break;
1314         }
1315 
1316         answer = P_ToXLine(line)->xg->info.actTag;
1317         LOG_MAP_MSG_XGDEVONLY2("Using Line ActTag (%i) as %s", answer << parmname);
1318         break;
1319 
1320     case LDREF_COUNT: // Line count.
1321         if(!P_ToXLine(line)->xg)
1322         {
1323             LOG_MAP_MSG_XGDEVONLY("REFERENCE NOT AN XG LINE");
1324             break;
1325         }
1326 
1327         answer = P_ToXLine(line)->xg->info.actCount;
1328         LOG_MAP_MSG_XGDEVONLY2("Using Line Count (%i) as %s", answer << parmname);
1329         break;
1330 
1331     case LDREF_ANGLE: { // Line angle.
1332         coord_t d1[2];
1333 
1334         P_GetDoublepv(line, DMU_DXY, d1);
1335         answer = M_PointXYToAngle2(0, 0, d1[0], d1[1]) / (float) ANGLE_MAX * 360;
1336         LOG_MAP_MSG_XGDEVONLY2("Using Line Angle (%i) as %s", answer << parmname);
1337         break; }
1338 
1339     case LDREF_LENGTH: // Line length.
1340         // Answer should be in map units.
1341         answer = P_GetFixedp(line, DMU_LENGTH) >> FRACBITS;
1342 
1343         LOG_MAP_MSG_XGDEVONLY2("Using Line Length (%i) as %s", answer << parmname);
1344         break;
1345 
1346     case LDREF_TOP_OFFSETX:
1347         // Can this ever fail?
1348         side = (Side *)P_GetPtrp(line, DMU_FRONT);
1349         if(!side)
1350         {
1351             LOG_MAP_MSG_XGDEVONLY("REFERENCE MISSING FRONT SIDE!");
1352             break;
1353         }
1354 
1355         answer = P_GetIntp(side, DMU_TOP_MATERIAL_OFFSET_X);
1356 
1357         LOG_MAP_MSG_XGDEVONLY2("Using Line Top X Offset (%i) as %s", answer << parmname);
1358         break;
1359 
1360     case LDREF_TOP_OFFSETY:
1361         // Can this ever fail? (yes -dj)
1362         side = (Side *)P_GetPtrp(line, DMU_FRONT);
1363         if(!side)
1364         {
1365             LOG_MAP_MSG_XGDEVONLY("REFERENCE MISSING FRONT SIDE!");
1366             break;
1367         }
1368 
1369         answer = P_GetIntp(side, DMU_TOP_MATERIAL_OFFSET_Y);
1370 
1371         LOG_MAP_MSG_XGDEVONLY2("Using Line Top Y Offset (%i) as %s", answer << parmname);
1372         break;
1373 
1374     case LDREF_MIDDLE_OFFSETX:
1375         // Can this ever fail? (yes -dj)
1376         side = (Side *)P_GetPtrp(line, DMU_FRONT);
1377         if(!side)
1378         {
1379             LOG_MAP_MSG_XGDEVONLY("REFERENCE MISSING FRONT SIDE!");
1380             break;
1381         }
1382 
1383         answer = P_GetIntp(side, DMU_MIDDLE_MATERIAL_OFFSET_X);
1384 
1385         LOG_MAP_MSG_XGDEVONLY2("Using Line Middle X Offset (%i) as %s", answer << parmname);
1386         break;
1387 
1388     case LDREF_MIDDLE_OFFSETY:
1389         // Can this ever fail? (yes -dj)
1390         side = (Side *)P_GetPtrp(line, DMU_FRONT);
1391         if(!side)
1392         {
1393             LOG_MAP_MSG_XGDEVONLY("REFERENCE MISSING FRONT SIDE!");
1394             break;
1395         }
1396 
1397         answer = P_GetIntp(side, DMU_MIDDLE_MATERIAL_OFFSET_Y);
1398 
1399         LOG_MAP_MSG_XGDEVONLY2("Using Line Middle Y Offset (%i) as %s", answer << parmname);
1400         break;
1401 
1402     case LDREF_BOTTOM_OFFSETX:
1403         // Can this ever fail? (yes -dj)
1404         side = (Side *)P_GetPtrp(line, DMU_FRONT);
1405         if(!side)
1406         {
1407             LOG_MAP_MSG_XGDEVONLY("REFERENCE MISSING FRONT SIDE!");
1408             break;
1409         }
1410 
1411         answer = P_GetIntp(side, DMU_BOTTOM_MATERIAL_OFFSET_X);
1412 
1413         LOG_MAP_MSG_XGDEVONLY2("Using Line Bottom X Offset (%i) as %s", answer << parmname);
1414         break;
1415 
1416     case LDREF_BOTTOM_OFFSETY:
1417         // Can this ever fail? (yes -dj)
1418         side = (Side *)P_GetPtrp(line, DMU_FRONT);
1419         if(!side)
1420         {
1421             LOG_MAP_MSG_XGDEVONLY("REFERENCE MISSING FRONT SIDE!");
1422             break;
1423         }
1424 
1425         answer = P_GetIntp(side, DMU_BOTTOM_MATERIAL_OFFSET_Y);
1426 
1427         LOG_MAP_MSG_XGDEVONLY2("Using Line Bottom Y Offset (%i) as %s", answer << parmname);
1428         break;
1429 
1430     default:    // Could be explicit, return the actual int value
1431         answer = reftype;
1432         break;
1433     }
1434 
1435     return answer;
1436 }
1437 
1438 /**
1439  * Executes the lines' function as defined by its class.
1440  */
XL_DoFunction(linetype_t * info,Line * line,int sideNum,mobj_t * actThing,int evType)1441 void XL_DoFunction(linetype_t *info, Line *line, int sideNum, mobj_t *actThing, int evType)
1442 {
1443     DENG2_ASSERT(info && line);
1444     DENG2_ASSERT(info->lineClass >= 0 && info->lineClass < NUMXGCLASSES);
1445     LOG_AS("XL_DoFunction");
1446 
1447     xgclass_t *xgClass = &xgClasses[info->lineClass];
1448 
1449     LOG_MAP_MSG_XGDEVONLY2("Line %i, side %i, activator id %i, event %s",
1450             P_ToIndex(line) << sideNum << (actThing ? actThing->thinker.id : 0)
1451             << EVTYPESTR(evType));
1452     LOG_MAP_MSG_XGDEVONLY2("Executing class: %s (0x%X)...", xgClass->className << info->lineClass);
1453 
1454     // Does this class only work with certain events?
1455     if(xgClass->evTypeFlags > 0)
1456     {
1457         if(!(xgClass->evTypeFlags & evType))
1458         {
1459             LOG_MAP_MSG_XGDEVONLY2("THIS CLASS DOES NOT SUPPORT %s EVENTS!", EVTYPESTR(evType));
1460             return;
1461         }
1462     }
1463 
1464     // Does this class have an init function?
1465     if(xgClass->initFunc)
1466         xgClass->initFunc(line);
1467 
1468     // Does this class have a do function?
1469     if(xgClass->doFunc)
1470     {
1471         // Do we need to traverse?
1472         switch(xgClass->traverse)
1473         {
1474             case TRAV_NONE: // No need for traversal, call the doFunc
1475                 de::function_cast<LineTraverserFunc>(xgClass->doFunc)(line, true, line, info, actThing);
1476                 break;
1477             case TRAV_LINES: // Traverse lines, executing doFunc for each
1478                 XL_TraverseLines(line, info->iparm[xgClass->travRef],
1479                                  info->iparm[xgClass->travData], line,
1480                                  info, actThing, de::function_cast<LineTraverserFunc>(xgClass->doFunc));
1481                 break;
1482             case TRAV_PLANES: // Traverse planes, executing doFunc for each
1483             case TRAV_SECTORS:
1484                 XL_TraversePlanes(line, info->iparm[xgClass->travRef],
1485                                   info->iparm[xgClass->travData], line,
1486                                   info, xgClass->traverse == TRAV_SECTORS? true : false,
1487                                   actThing, de::function_cast<PlaneTraverserFunc>(xgClass->doFunc));
1488                 break;
1489         }
1490     }
1491 }
1492 
XLTrav_QuickActivate(Line * line,dd_bool,void * context,void *,mobj_t *)1493 int XLTrav_QuickActivate(Line *line, dd_bool /*ceiling*/, void *context, void * /*context2*/,
1494     mobj_t * /*activator*/)
1495 {
1496     if(line)
1497     {
1498         xline_t *xline = P_ToXLine(line);
1499         if(xline->xg)
1500         {
1501             xline->xg->active = (context? true : false);
1502             xline->xg->timer  = XLTIMER_STOPPED; // Stop timer.
1503         }
1504     }
1505 
1506     return true; // Continue iteration.
1507 }
1508 
1509 /**
1510  * @return  @c true, if the line is active.
1511  */
XLTrav_CheckLine(Line * line,dd_bool,void * context,void *,mobj_t *)1512 int XLTrav_CheckLine(Line *line, dd_bool /*ceiling*/, void *context,
1513     void * /*context2*/, mobj_t * /*activator*/)
1514 {
1515     if(line)
1516     {
1517         xline_t *xline = P_ToXLine(line);
1518 
1519         if(!xline->xg)
1520         {
1521             return false; // Stop checking!
1522         }
1523 
1524         return CPP_BOOL(xline->xg->active) == (context? true : false);
1525     }
1526 
1527     return true; // Continue iteration.
1528 }
1529 
1530 /**
1531  * @param data  If @c true, the line will receive a chain event if inactive.
1532  *              If @c false, the line will receive a chain event if active.
1533  */
XLTrav_SmartActivate(Line * line,dd_bool,void * context,void * context2,mobj_t *)1534 int XLTrav_SmartActivate(Line *line, dd_bool /*ceiling*/, void *context,
1535     void *context2, mobj_t * /*activator*/)
1536 {
1537     if(line)
1538     {
1539         xline_t *xline = P_ToXLine(line);
1540 
1541         if(xline->xg)
1542         {
1543             if(CPP_BOOL(xline->xg->active) != (context? true : false))
1544             {
1545                 XL_LineEvent(XLE_CHAIN, 0, line, 0, context2);
1546             }
1547         }
1548     }
1549 
1550     return true; // Continue iteration.
1551 }
1552 
1553 //
1554 // XG Line Type Classes which don't require traversal
1555 //
1556 
XL_DoChainSequence(Line * line,dd_bool,void *,void * context2,mobj_t *)1557 int XL_DoChainSequence(Line *line, dd_bool /*ceiling*/, void * /*context*/,
1558     void *context2, mobj_t * /*activator*/)
1559 {
1560     if(line)
1561     {
1562         xline_t *xline = P_ToXLine(line);
1563 
1564         if(xline->xg)
1565         {
1566             linetype_t *info = static_cast<linetype_t *>(context2);
1567 
1568             xline->xg->chIdx = 1; // This is the first.
1569             // Start counting the first interval.
1570             xline->xg->chTimer =
1571                 XG_RandomPercentFloat(info->fparm[1], info->fparm[0]);
1572         }
1573     }
1574 
1575     return true;
1576 }
1577 
XL_DoDamage(Line *,dd_bool,void *,void * context2,mobj_t * activator)1578 int XL_DoDamage(Line * /*line*/, dd_bool /*ceiling*/, void * /*context*/, void *context2,
1579     mobj_t *activator)
1580 {
1581     linetype_t *info = static_cast<linetype_t *>(context2);
1582 
1583     if(!activator)
1584     {
1585 /*        if(evtype == XLE_FUNC)
1586             LOG_MAP_MSG_XGDEVONLY("Sector Functions don't have activators! Use a Sector Chain instead");
1587         else*/
1588             LOG_MAP_MSG_XGDEVONLY("No activator! Can't damage anything");
1589 
1590         return false;
1591     }
1592 
1593     if(activator->health > info->iparm[2])
1594     {
1595         // Iparms define the min and max damage to inflict.
1596         // The real amount is random.
1597         int i = XG_RandomInt(info->iparm[0], info->iparm[1]);
1598         if(i > 0)
1599         {
1600             P_DamageMobj(activator, 0, 0, i, false);
1601         }
1602         else if(i < 0 && activator->health < info->iparm[3])
1603         {
1604             int origHealth = activator->health;
1605             // Don't go above a given level.
1606             activator->health = MIN_OF(activator->health-i, info->iparm[3]);
1607             // Need to signal an update?
1608             if(activator->player && activator->health != origHealth)
1609             {
1610                 activator->player->health = activator->health;
1611                 activator->player->update |= PSF_HEALTH;
1612             }
1613         }
1614     }
1615 
1616     return true;
1617 }
1618 
XL_DoPower(Line *,dd_bool,void *,void * context2,mobj_t * activator)1619 int XL_DoPower(Line * /*line*/, dd_bool /*ceiling*/, void * /*context*/,
1620     void *context2, mobj_t *activator)
1621 {
1622     linetype_t *info = static_cast<linetype_t *>(context2);
1623     DENG2_ASSERT(info);
1624     player_t *player = activator? activator->player : nullptr;
1625 
1626     // Only players have armor.
1627     if(!player)
1628     {
1629 /*        if(evtype == XLE_FUNC)
1630             LOG_MAP_MSG_XGDEVONLY("Sector Functions don't have activators! Use a Sector Chain instead");
1631         else*/
1632             LOG_MAP_MSG_XGDEVONLY("Activator MUST be a player...");
1633 
1634         return false;
1635     }
1636 
1637     int delta = XG_RandomInt(info->iparm[0], info->iparm[1]);
1638     if(delta > 0)
1639     {
1640         if(player->armorPoints + delta >= info->iparm[3])
1641             delta = de::max(0, info->iparm[3] - player->armorPoints);
1642     }
1643     else
1644     {
1645         if(player->armorPoints + delta <= info->iparm[2])
1646             delta = de::min(0, info->iparm[2] - player->armorPoints);
1647     }
1648 
1649     if(delta)
1650     {
1651         if(!player->armorType)
1652             P_PlayerSetArmorType(player, 1);
1653         P_PlayerGiveArmorBonus(player, delta);
1654     }
1655 
1656     return true;
1657 }
1658 
XL_DoKey(Line *,dd_bool,void *,void * context2,mobj_t * activator)1659 int XL_DoKey(Line * /*line*/, dd_bool /*ceiling*/, void * /*context*/,
1660     void *context2, mobj_t *activator)
1661 {
1662     linetype_t *info = static_cast<linetype_t *>(context2);
1663     player_t *player = (activator? activator->player : 0);
1664 
1665     if(!player)  // Must be a player.
1666     {
1667 /*        if(evtype == XLE_FUNC)
1668             LOG_MAP_MSG_XGDEVONLY("Sector Functions don't have activators! Use a Sector Chain instead");
1669         else*/
1670             LOG_MAP_MSG_XGDEVONLY("Activator MUST be a player...");
1671 
1672         return false;
1673     }
1674 
1675     for(int i = 0; i < NUM_KEY_TYPES; ++i)
1676     {
1677         if(info->iparm[0] & (1 << i))
1678             P_GiveKey(player, keytype_t(i));
1679         if(info->iparm[1] & (1 << i))
1680             player->keys[i] = false;
1681     }
1682 
1683     return true;
1684 }
1685 
XL_DoExplode(Line *,dd_bool,void *,void *,mobj_t * activator)1686 int XL_DoExplode(Line * /*line*/, dd_bool /*ceiling*/, void * /*context*/,
1687     void * /*context2*/, mobj_t *activator)
1688 {
1689     if(!activator)
1690     {
1691 /*        if(evtype == XLE_FUNC)
1692             LOG_MAP_MSG_XGDEVONLY("Sector Functions don't have activators! Use a Sector Chain instead");
1693         else*/
1694             LOG_MAP_MSG_XGDEVONLY("No activator! Can't explode anything");
1695         return false;
1696     }
1697 
1698     P_ExplodeMissile(activator);
1699     return true;
1700 }
1701 
XL_DoCommand(Line *,dd_bool,void *,void * context2,mobj_t *)1702 int XL_DoCommand(Line * /*line*/, dd_bool /*ceiling*/, void * /*context*/,
1703     void *context2, mobj_t * /*activator*/)
1704 {
1705     linetype_t *info = static_cast<linetype_t *>(context2);
1706 
1707     DD_Execute(true, info->sparm[0]);
1708     return true;
1709 }
1710 
1711 //
1712 // Following classes require traversal hence "Trav_"
1713 //
1714 
XLTrav_ChangeLineType(Line * line,dd_bool,void *,void * context2,mobj_t *)1715 int XLTrav_ChangeLineType(Line *line, dd_bool /*ceiling*/, void * /*context*/,
1716     void *context2, mobj_t * /*activator*/)
1717 {
1718     if(line)
1719     {
1720         linetype_t *info = static_cast<linetype_t *>(context2);
1721         XL_SetLineType(line, info->iparm[2]);
1722     }
1723 
1724     return true; // Keep looking.
1725 }
1726 
lineSideIfSector(Line & line,bool back=false)1727 static Side *lineSideIfSector(Line &line, bool back = false)
1728 {
1729     if(P_GetPtrp(&line, back? DMU_BACK_SECTOR : DMU_FRONT_SECTOR))
1730     {
1731         return (Side *)P_GetPtrp(&line, back? DMU_BACK : DMU_FRONT);
1732     }
1733     return nullptr;
1734 }
1735 
XLTrav_ChangeWallMaterial(Line * line,dd_bool,void *,void * context2,mobj_t *)1736 int XLTrav_ChangeWallMaterial(Line *line, dd_bool /*ceiling*/, void * /*context*/,
1737     void *context2, mobj_t * /*activator*/)
1738 {
1739     LOG_AS("XLTrav_ChangeWallMaterial");
1740 
1741     linetype_t *info = static_cast<linetype_t *>(context2);
1742     if(!line) return true; // Continue iteration.
1743 
1744     // i2: sidenum
1745     // i3: top material (zero if no change)
1746     // i4: mid material (zero if no change, -1 to remove)
1747     // i5: bottom material (zero if no change)
1748     // i6: (true/false) set midtexture even if previously zero
1749     // i7: sdf_* flags
1750     // i8: mid blendmode
1751     // i9: top red
1752     // i10: top green
1753     // i11: top blue
1754     // i12: mid red
1755     // i13: mid green
1756     // i14: mid blue
1757     // i15: mid alpha
1758     // i16: bottom red
1759     // i17: bottom green
1760     // i18: bottom blue
1761 
1762     Side *side = lineSideIfSector(*line, CPP_BOOL(info->iparm[2]));
1763     if(!side) return true; // Continue iteration.
1764 
1765     LOG_MAP_MSG_XGDEVONLY2("Line %i", P_ToIndex(line));
1766 
1767     XL_ChangeMaterial(line, info->iparm[2], LWS_UPPER,
1768                       (world_Material *)P_ToPtr(DMU_MATERIAL, info->iparm[3]),
1769                       BM_NORMAL, Vector3f(info->iparm[9], info->iparm[10], info->iparm[11]) / 255.f,
1770                       info->iparm[7]);
1771 
1772     world_Material *mat = 0;
1773     if(info->iparm[4] && (P_GetPtrp(side, DMU_MIDDLE_MATERIAL) || info->iparm[6]))
1774     {
1775         if(!P_GetPtrp(line, DMU_BACK_SECTOR) && info->iparm[4] == -1)
1776             mat = 0;
1777         else
1778             mat = (world_Material *)P_ToPtr(DMU_MATERIAL, info->iparm[4]);
1779     }
1780 
1781     XL_ChangeMaterial(line, info->iparm[2], LWS_MID, mat,
1782                       blendmode_t(info->iparm[8]),
1783                       Vector4f(info->iparm[12], info->iparm[13], info->iparm[14], info->iparm[15]) / 255.f,
1784                       info->iparm[7]);
1785 
1786     XL_ChangeMaterial(line, info->iparm[2], LWS_LOWER,
1787                       (world_Material *)P_ToPtr(DMU_MATERIAL, info->iparm[5]),
1788                       BM_NORMAL, Vector3f(info->iparm[16], info->iparm[17], info->iparm[18]) / 255.f,
1789                       info->iparm[7]);
1790 
1791     return true;
1792 }
1793 
XLTrav_Activate(Line * line,dd_bool,void *,void *,mobj_t * activator)1794 int XLTrav_Activate(Line *line, dd_bool /*ceiling*/, void * /*context*/,
1795     void * /*context2*/, mobj_t *activator)
1796 {
1797     if(line)
1798     {
1799         XL_LineEvent(XLE_CHAIN, 0, line, 0, activator);
1800     }
1801     return true; // Keep looking.
1802 }
1803 
XLTrav_LineCount(Line * line,dd_bool,void *,void * context2,mobj_t *)1804 int XLTrav_LineCount(Line *line, dd_bool /*ceiling*/, void * /*context*/,
1805     void *context2, mobj_t * /*activator*/)
1806 {
1807     linetype_t *info = static_cast<linetype_t *>(context2);
1808 
1809     if(line)
1810     {
1811         xline_t *xline = P_ToXLine(line);
1812         if(xline->xg)
1813         {
1814             if(info->iparm[2])
1815             {
1816                 xline->xg->info.actCount = info->iparm[3];
1817             }
1818             else
1819             {
1820                 xline->xg->info.actCount += info->iparm[3];
1821             }
1822         }
1823     }
1824 
1825     return true;
1826 }
1827 
XLTrav_Music(Line * line,dd_bool,void *,void * context2,mobj_t *)1828 int XLTrav_Music(Line *line, dd_bool /*ceiling*/, void * /*context*/,
1829     void *context2, mobj_t * /*activator*/)
1830 {
1831     LOG_AS("XLTrav_Music");
1832 
1833     linetype_t *info = static_cast<linetype_t *>(context2);
1834 
1835     int song = 0;
1836     int temp = 0;
1837 
1838     if(info->iparm[2] == LREF_NONE)
1839     {    // The old format, use ip0 explicitly.
1840         song = info->iparm[0];
1841     }
1842     else // We might possibly have a data reference to evaluate.
1843     {
1844         if(info->iparm[2] == LREF_NONE) // (ip0) will be used to determine next map.
1845         {
1846             song = info->iparm[0];
1847         }
1848         else if(line)
1849         {
1850             // Evaluate ip0 for a data reference.
1851             temp = XL_ValidateLineRef(line, info->iparm[0], context2, "Music ID");
1852             if(!temp)
1853             {
1854                 LOG_MAP_MSG_XGDEVONLY("Reference data not valid. Song not changed");
1855             }
1856             else
1857             {
1858                 song = temp;
1859             }
1860         }
1861     }
1862 
1863     //// @todo Add code to validate song id here.
1864 
1865     if(song)
1866     {
1867         LOG_MAP_MSG_XGDEVONLY2("Play Music ID (%i)%s",
1868                 song << (info->iparm[1]? " looped" : ""));
1869         S_StartMusicNum(song, info->iparm[1]);
1870     }
1871 
1872     return false; // Only do this once!
1873 }
1874 
XLTrav_LineTeleport(Line * newLine,dd_bool,void * context,void * context2,mobj_t * mobj)1875 int XLTrav_LineTeleport(Line *newLine, dd_bool /*ceiling*/, void *context,
1876     void *context2, mobj_t *mobj)
1877 {
1878 // Maximum units to move object to avoid hiccups.
1879 #define FUDGEFACTOR         10
1880 
1881     LOG_AS("XLTrav_LineTeleport");
1882 
1883     int fudge = FUDGEFACTOR;
1884     int side = 0, stepDown;
1885     uint an;
1886     mobj_t *flash;
1887     Line *line = (Line *) context;
1888     linetype_t *info = (linetype_t *) context2;
1889     Vertex /* *newV1,*/ *newV2, *oldV1/*, *oldV2*/;
1890     Sector *newFrontSec, *newBackSec;
1891     coord_t newPos[3], pos, s, c;
1892     coord_t oldLineDelta[2], newLineDelta[2];
1893     angle_t angle;
1894 
1895     // Don't teleport things marked noteleport!
1896     if(mobj->flags2 & MF2_NOTELEPORT)
1897     {
1898         LOG_MAP_MSG_XGDEVONLY("Activator can't be teleported (THING is unteleportable)");
1899         return false; // No point continuing...
1900     }
1901 
1902     if(!line)
1903         return true; // Continue iteration.
1904 
1905     // We shouldn't be trying to teleport to the same line
1906     if(newLine == line)
1907     {
1908         LOG_MAP_MSG_XGDEVONLY("Target == Origin. Continuing search...");
1909         return true; // Keep looking
1910     }
1911 
1912     // Retrieve a few properties to make this look neater.
1913     oldV1 = (Vertex *)P_GetPtrp(line, DMU_VERTEX0);
1914     //oldV2 = (Vertex *)P_GetPtrp(line, DMU_VERTEX1);
1915     P_GetDoublepv(line, DMU_DXY, oldLineDelta);
1916 
1917     //newV1 = (Vertex *)P_GetPtrp(newLine, DMU_VERTEX0);
1918     newV2 = (Vertex *)P_GetPtrp(newLine, DMU_VERTEX1);
1919     P_GetDoublepv(newLine, DMU_DXY, newLineDelta);
1920     newFrontSec = (Sector *)P_GetPtrp(newLine, DMU_FRONT_SECTOR);
1921     newBackSec  = (Sector *)P_GetPtrp(newLine, DMU_BACK_SECTOR);
1922 
1923     // i2: 1 = Spawn Fog
1924     // i3: Sound = Sound to play
1925     // i4: 1 = reversed
1926     // i5: 1 = always telestomp
1927 
1928     LOG_MAP_MSG_XGDEVONLY2("%s, %s, %s",
1929            (   info->iparm[2]? "Spawn Flash" : "No Flash")
1930            << (info->iparm[3]? "Play Sound"  : "Silent")
1931            << (info->iparm[4]? "Reversed"    : "Normal."));
1932 
1933     // Spawn flash at the old position?
1934     if(info->iparm[2])
1935     {
1936         if((flash = P_SpawnMobj(MT_TFOG, mobj->origin, mobj->angle + ANG180, 0)))
1937         {
1938             // Play a sound?
1939             if(info->iparm[3])
1940                 S_StartSound(info->iparm[3], flash);
1941         }
1942     }
1943 
1944     // Get the thing's position along the source line
1945     if(fabs(oldLineDelta[0]) > fabs(oldLineDelta[1]))
1946         pos = (mobj->origin[VX] - P_GetDoublep(oldV1, DMU_X)) / oldLineDelta[0];
1947     else
1948         pos = (mobj->origin[VY] - P_GetDoublep(oldV1, DMU_Y)) / oldLineDelta[1];
1949 
1950     // Get the angle between the two lines, for rotating orientation and
1951     // momentum. Rotate 180 degrees, and flip the position across the exit
1952     // line, if reversed.
1953     angle = (info->iparm[4] ? pos = 1 - pos, 0 : ANG180) +
1954              M_PointXYToAngle2(0, 0, newLineDelta[0], newLineDelta[1]) -
1955              M_PointXYToAngle2(0, 0, oldLineDelta[0], oldLineDelta[1]);
1956 
1957     // Interpolate position across the exit line.
1958     newPos[VX] = P_GetDoublep(newV2, DMU_X) - (pos * newLineDelta[0]);
1959     newPos[VY] = P_GetDoublep(newV2, DMU_Y) - (pos * newLineDelta[1]);
1960 
1961     // Sine, cosine of angle adjustment
1962     s = FIX2FLT(finesine[angle >> ANGLETOFINESHIFT]);
1963     c = FIX2FLT(finecosine[angle >> ANGLETOFINESHIFT]);
1964 
1965     // Whether walking towards first side of exit line steps down
1966     if(P_GetDoublep(newFrontSec, DMU_FLOOR_HEIGHT) <
1967        P_GetDoublep(newBackSec, DMU_FLOOR_HEIGHT))
1968         stepDown = true;
1969     else
1970         stepDown = false;
1971 
1972     // Height of thing above ground.
1973     newPos[VZ] = mobj->origin[VZ] - mobj->floorZ;
1974 
1975     /**
1976      * Side to exit the line on positionally.
1977      *
1978      * @note
1979      * This flag concerns exit position, not momentum. Due to potential for
1980      * round-off error, the thing can land on either the left or the right
1981      * side of the exit line, and steps must be taken to make sure it
1982      * does not end up on the wrong side.
1983      *
1984      * Exit momentum is always towards side 1 in a reversed teleporter, and
1985      * always towards side 0 otherwise.
1986      *
1987      * Exiting positionally on side 1 is always safe, as far as avoiding
1988      * oscillations and stuck-in-wall problems, but may not be optimum for
1989      * non-reversed teleporters.
1990      *
1991      * Exiting on side 0 can cause oscillations if momentum is towards side
1992      * 1, as it is with reversed teleporters.
1993      *
1994      * Exiting on side 1 slightly improves player viewing when going down a
1995      * step on a non-reversed teleporter.
1996      */
1997 
1998     if(!info->iparm[4] || (mobj->player && stepDown))
1999         side = 1;
2000 
2001     // Make sure we are on correct side of exit line.
2002     while((Line_PointOnSide(newLine, newPos) < 0) != side && --fudge >= 0)
2003     {
2004         if(fabs(newLineDelta[0]) > fabs(newLineDelta[1]))
2005             newPos[VY] -= FIX2FLT(((newLineDelta[0] < 0) != side)? -1 : 1);
2006         else
2007             newPos[VX] += FIX2FLT(((newLineDelta[1] < 0) != side)? -1 : 1);
2008     }
2009 
2010     // Do the Teleport
2011     if(!P_TeleportMove(mobj, newPos[VX], newPos[VY], (info->iparm[5] > 0? true : false)))
2012     {
2013         LOG_MAP_MSG_XGDEVONLY("Something went horribly wrong... aborting.");
2014         return false;
2015     }
2016 
2017     // Adjust z position to be same height above ground as before. Ground
2018     // level at the exit is measured as the higher of the two floor heights
2019     // at the exit line.
2020     if(stepDown)
2021         mobj->origin[VZ] = newPos[VZ] + P_GetDoublep(newFrontSec, DMU_FLOOR_HEIGHT);
2022     else
2023         mobj->origin[VZ] = newPos[VZ] + P_GetDoublep(newBackSec, DMU_FLOOR_HEIGHT);
2024 
2025     // Rotate mobj's orientation according to difference in line angles.
2026     mobj->angle += angle;
2027 
2028     // Update momentum of mobj crossing teleporter line?
2029     newPos[VX] = mobj->mom[MX];
2030     newPos[VY] = mobj->mom[MY];
2031 
2032     // Rotate mobj's momentum to come out of exit just like it entered.
2033     mobj->mom[MX] = (newPos[VX] * c) - (newPos[VY] * s);
2034     mobj->mom[MY] = (newPos[VY] * c) + (newPos[VX] * s);
2035 
2036     // Feet clipped?
2037     if(mobj->flags2 & MF2_FLOORCLIP)
2038     {
2039         mobj->floorClip = 0;
2040 
2041         if(FEQUAL(mobj->origin[VZ], P_GetDoublep(Mobj_Sector(mobj), DMU_FLOOR_HEIGHT)))
2042         {
2043             terraintype_t const *tt = P_MobjFloorTerrain(mobj);
2044             if(tt->flags & TTF_FLOORCLIP)
2045             {
2046                 mobj->floorClip = 10;
2047             }
2048         }
2049     }
2050 
2051     // Spawn flash at the new position?
2052     if (!info->iparm[2])
2053     {
2054         an = mobj->angle >> ANGLETOFINESHIFT;
2055         if((flash = P_SpawnMobjXYZ(MT_TFOG,
2056                                    mobj->origin[VX] + 24 * FIX2FLT(finecosine[an]),
2057                                    mobj->origin[VY] + 24 * FIX2FLT(finesine[an]),
2058                                    mobj->origin[VZ], mobj->angle + ANG180, 0)))
2059         {
2060             // Play a sound?
2061             if(info->iparm[3])
2062                 S_StartSound(info->iparm[3], flash);
2063         }
2064     }
2065 
2066     // Adjust the player's view, incase there has been a height change
2067     if(mobj->player)
2068     {
2069         mobj->player->viewZ = mobj->origin[VZ] + mobj->player->viewHeight;
2070         mobj->dPlayer->flags |= DDPF_FIXANGLES | DDPF_FIXORIGIN | DDPF_FIXMOM;
2071     }
2072 
2073     return false; // Do this only once!
2074 
2075 #undef FUDGEFACTOR
2076 }
2077 
XLTrav_LeaveMap(Line * line,dd_bool,void *,void * context2,mobj_t *)2078 int XLTrav_LeaveMap(Line *line, dd_bool /*ceiling*/, void * /*context*/, void *context2,
2079                     mobj_t * /*activator*/)
2080 {
2081     LOG_AS("XLTrav_LeaveMap");
2082 
2083     linetype_t *info = static_cast<linetype_t *>(context2);
2084 
2085     // Is this a secret exit?
2086     if(info->iparm[0] > 0)
2087     {
2088         G_SetGameActionMapCompleted(gfw_Session()->mapUriForNamedExit("secret"), 0, true);
2089         return false;
2090     }
2091 
2092     de::Uri newMapUri;
2093     if(info->iparm[1] == LREF_NONE)
2094     {
2095         // (ip3) will be used to determine next map (1-based).
2096         if(info->iparm[3])
2097         {
2098             newMapUri = G_ComposeMapUri(gfw_Session()->episodeId().toInt() - 1, info->iparm[3] - 1);
2099             LOG_MAP_MSG_XGDEVONLY2("Next map set to \"%s\"", newMapUri);
2100         }
2101     }
2102     // We might possibly have a data reference to evaluate.
2103     else if(line)
2104     {
2105         int const oldMapNumber = XL_ValidateLineRef(line, info->iparm[3], context2, "Map Number");
2106         if(oldMapNumber > 0)
2107         {
2108             newMapUri = G_ComposeMapUri(gfw_Session()->episodeId().toInt() - 1, oldMapNumber - 1);
2109         }
2110     }
2111 
2112     if(newMapUri.isEmpty())
2113     {
2114         newMapUri = gfw_Session()->mapUriForNamedExit("next");
2115         LOG_MAP_MSG_XGDEVONLY("Next map set to default for the 'next' exit");
2116     }
2117 
2118     // Check that the map truly exists.
2119     if(!P_MapExists(newMapUri.compose().toUtf8().constData()))
2120     {
2121         // Backward compatibility dictates that invalid refs be interpreted to mean the start map
2122         // of the current episode (which is known to always exist).
2123         newMapUri = de::makeUri(gfw_Session()->episodeDef()->gets("startMap"));
2124     }
2125 
2126     G_SetGameActionMapCompleted(newMapUri);
2127     return false; // Only do this once!
2128 }
2129 
XLTrav_DisableLine(Line * line,dd_bool,void * context,void *,mobj_t *)2130 int XLTrav_DisableLine(Line *line, dd_bool /*ceiling*/, void *context,
2131     void * /*context2*/, mobj_t * /*activator*/)
2132 {
2133     if(line)
2134     {
2135         xline_t *xline = P_ToXLine(line);
2136 
2137         if(xline->xg)
2138         {
2139             xline_t *origLine = P_ToXLine((Line *) context);
2140 
2141             xline->xg->disabled = origLine->xg->active;
2142         }
2143     }
2144 
2145     return true; // Keep looking...
2146 }
2147 
XLTrav_EnableLine(Line * line,dd_bool,void * context,void *,mobj_t *)2148 int XLTrav_EnableLine(Line *line, dd_bool /*ceiling*/, void *context,
2149     void * /*context2*/, mobj_t * /*activator*/)
2150 {
2151     if(line)
2152     {
2153         xline_t *xline = P_ToXLine(line);
2154         if(xline->xg)
2155         {
2156             xline_t *origLine = P_ToXLine((Line*) context);
2157 
2158             xline->xg->disabled = !origLine->xg->active;
2159         }
2160     }
2161 
2162     return true; // Keep looking...
2163 }
2164 
2165 /**
2166  * Checks if the given lines are active or inactive.
2167  *
2168  * @return  @c true if all are in the specified state.
2169  */
XL_CheckLineStatus(Line * line,int reftype,int ref,int active,mobj_t * activator)2170 dd_bool XL_CheckLineStatus(Line *line, int reftype, int ref, int active,
2171                            mobj_t *activator)
2172 {
2173     return XL_TraverseLines(line, reftype, ref, &active, 0, activator, XLTrav_CheckLine);
2174 }
2175 
XL_CheckMobjGone(thinker_t * th,void * context)2176 int XL_CheckMobjGone(thinker_t *th, void *context)
2177 {
2178     LOG_AS("XL_CheckMobjGone");
2179 
2180     mobj_t *mo    = (mobj_t *) th;
2181     int thingtype = *static_cast<int *>(context);
2182 
2183     if(mo->type == thingtype && mo->health > 0)
2184     {
2185         // Not dead.
2186         LOG_MAP_MSG_XGDEVONLY2("Thing type %i: Found mo id=%i, health=%i, pos=%s",
2187                thingtype << mo->thinker.id << mo->health << Vector4d(mo->origin).asText());
2188         return true; // Stop iteration.
2189     }
2190 
2191     return false; // Continue iteration.
2192 }
2193 
XL_SwapSwitchTextures(Line * line,int snum)2194 void XL_SwapSwitchTextures(Line *line, int snum)
2195 {
2196     LOG_AS("XL_SwapSwitchTextures");
2197 
2198     if(line)
2199     {
2200         Side *side = (Side *)P_GetPtrp(line, snum? DMU_BACK : DMU_FRONT);
2201 
2202         if(side && P_ToggleSwitch(side, SFX_NONE, true, 0))
2203         {
2204             LOG_MAP_MSG_XGDEVONLY2("Line %i, side %i",
2205                    P_ToIndex(line) << P_ToIndex(side));
2206         }
2207     }
2208 }
2209 
2210 /**
2211  * Changes material of the given line.
2212  */
XL_ChangeMaterial(Line * line,int sidenum,int section,world_Material * mat,blendmode_t blendmode,Vector4f const & tintColor,int flags)2213 void XL_ChangeMaterial(Line *line, int sidenum, int section, world_Material *mat,
2214     blendmode_t blendmode, Vector4f const &tintColor, int flags)
2215 {
2216     Side *side = (Side *)P_GetPtrp(line, sidenum? DMU_BACK:DMU_FRONT);
2217     if(!side) return;
2218 
2219     LOG_MAP_MSG_XGDEVONLY2("Line:%i side:%i section:%i material:%i tintColor:%s blendmode:%i",
2220            P_ToIndex(line) << sidenum << section << P_ToIndex(mat) << tintColor.asText() << blendmode);
2221 
2222     // Which wall section are we working on?
2223     if(section == LWS_MID)
2224     {
2225         // Are we removing the middle texture?
2226         if(mat == (world_Material *) -1)
2227         {
2228             P_SetPtrp(side, DMU_MIDDLE_MATERIAL, NULL);
2229         }
2230         else if(mat)
2231         {
2232             P_SetPtrp(side, DMU_MIDDLE_MATERIAL, mat);
2233         }
2234 
2235         // Are we changing the blendmode?
2236         if(blendmode)
2237         {
2238             P_SetIntp(side, DMU_MIDDLE_BLENDMODE, blendmode);
2239         }
2240 
2241         // Are we changing the surface color?
2242         for(int i = 0; i < 4; ++i)
2243         {
2244             if(!de::fequal(tintColor[i], 0))
2245                 P_SetFloatp(side, TO_DMU_MIDDLE_COLOR(i), tintColor[i]);
2246         }
2247     }
2248     else if(section == LWS_UPPER)
2249     {
2250         if(mat)
2251         {
2252             P_SetPtrp(side, DMU_TOP_MATERIAL, mat);
2253         }
2254 
2255         for(int i = 0; i < 3; ++i)
2256         {
2257             if(!de::fequal(tintColor[i], 0))
2258                 P_SetFloatp(side, TO_DMU_TOP_COLOR(i), tintColor[i]);
2259         }
2260     }
2261     else if(section == LWS_LOWER)
2262     {
2263         if(mat)
2264         {
2265             P_SetPtrp(side, DMU_BOTTOM_MATERIAL, mat);
2266         }
2267 
2268         for(int i = 0; i < 3; ++i)
2269         {
2270             if(!de::fequal(tintColor[i], 0))
2271                 P_SetFloatp(side, TO_DMU_BOTTOM_COLOR(i), tintColor[i]);
2272         }
2273     }
2274 
2275     // Adjust the side's flags
2276     P_SetIntp(side, DMU_FLAGS, P_GetIntp(side, DMU_FLAGS) | flags);
2277 }
2278 
XL_Message(mobj_t * act,char * msg,dd_bool global)2279 void XL_Message(mobj_t *act, char *msg, dd_bool global)
2280 {
2281     LOG_AS("XL_Message");
2282 
2283     player_t *pl;
2284     int i;
2285 
2286     if(!msg || !msg[0]) return;
2287 
2288     if(global)
2289     {
2290         LOG_MAP_MSG_XGDEVONLY2("GLOBAL '%s'", msg);
2291         // Send to all players in the game.
2292         for(i = 0; i < MAXPLAYERS; ++i)
2293         {
2294             if(players[i].plr->inGame)
2295                 P_SetMessage(&players[i], msg);
2296         }
2297         return;
2298     }
2299 
2300     if(act->player)
2301     {
2302         pl = act->player;
2303     }
2304     else if((act->flags & MF_MISSILE) && act->target && act->target->player)
2305     {
2306         // Originator of the missile.
2307         pl = act->target->player;
2308     }
2309     else
2310     {
2311         // We don't know whom to send the message.
2312         LOG_MAP_MSG_XGDEVONLY2("'%s'\nNO DESTINATION, MESSAGE DISCARDED", msg);
2313         return;
2314     }
2315     P_SetMessage(pl, msg);
2316 }
2317 
XL_ActivateLine(dd_bool activating,linetype_t * info,Line * line,int sidenum,mobj_t * activator,int evtype)2318 void XL_ActivateLine(dd_bool activating, linetype_t *info, Line *line, int sidenum,
2319                      mobj_t *activator, int evtype)
2320 {
2321     DENG2_ASSERT(line);
2322     LOG_AS("XL_ActivateLine");
2323 
2324     xline_t *xline = P_ToXLine(line);
2325     if(!xline) return; // huh?
2326 
2327     LOG_MAP_MSG_XGDEVONLY2("%s line %i, side %i, type %i",
2328            (activating? "Activating" : "Deactivating") << P_ToIndex(line)
2329            << sidenum << xline->special);
2330 
2331     DENG2_ASSERT(xline->xg);
2332     xgline_t &xgline = *xline->xg;
2333     if(xgline.disabled)
2334     {
2335         LOG_MAP_MSG_XGDEVONLY("LINE DISABLED, ABORTING");
2336         return; // The line is disabled.
2337     }
2338 
2339     if((activating && xgline.active) || (!activating && !xgline.active))
2340     {
2341         LOG_MAP_MSG_XGDEVONLY2("Line is ALREADY %s, ABORTING", (activating ? "ACTIVE" : "INACTIVE"));
2342         return; // Do nothing (can't activate if already active!).
2343     }
2344 
2345     // Activation should happen on the front side.
2346     // Let the line know who's activating it.
2347     xgline.activator = activator;
2348 
2349     // Process (de)activation chains. Chains always pass as an activation
2350     // method, but the other requirements of the chained type must be met.
2351     if(activating && info->actChain)
2352     {
2353         LOG_MAP_MSG_XGDEVONLY2("Line has Act Chain (type %i) - It will be processed first...", info->actChain);
2354         XL_LineEvent(XLE_CHAIN, info->actChain, line, sidenum, activator);
2355     }
2356     else if(!activating && info->deactChain)
2357     {
2358         LOG_MAP_MSG_XGDEVONLY2("Line has Deact Chain (type %i) - It will be processed first...", info->deactChain);
2359         XL_LineEvent(XLE_CHAIN, info->deactChain, line, sidenum, activator);
2360     }
2361 
2362     // Automatically swap any SW* textures.
2363     if(xgline.active != activating)
2364     {
2365         XL_SwapSwitchTextures(line, sidenum);
2366     }
2367 
2368     // Change the state of the line.
2369     xgline.active = activating;
2370     xgline.timer  = 0; // Reset timer.
2371 
2372     // Activate lines with a matching tag with Group Activation.
2373     if( (activating && (info->flags2 & LTF2_GROUP_ACT)) ||
2374        (!activating && (info->flags2 & LTF2_GROUP_DEACT)))
2375     {
2376         XL_TraverseLines(line, LREF_LINE_TAGGED, true, &activating, 0, activator,
2377                          XLTrav_SmartActivate);
2378     }
2379 
2380     // For lines flagged Multiple, quick-(de)activate other lines that have
2381     // the same line tag.
2382     if(info->flags2 & LTF2_MULTIPLE)
2383     {
2384         XL_TraverseLines(line, LREF_LINE_TAGGED, true, &activating, 0, activator,
2385                          XLTrav_QuickActivate);
2386     }
2387 
2388     // Should we apply the function of the line? Functions are defined by
2389     // the class of the line type.
2390     if(  (activating && (info->flags2 & LTF2_WHEN_ACTIVATED)) ||
2391         (!activating && (info->flags2 & LTF2_WHEN_DEACTIVATED)))
2392     {
2393         if(!(info->flags2 & LTF2_WHEN_LAST) || info->actCount == 1)
2394         {
2395             XL_DoFunction(info, line, sidenum, activator, evtype);
2396         }
2397         else
2398         {
2399             LOG_MAP_MSG_XGDEVONLY2("Line %i FUNCTION TEST FAILED", P_ToIndex(line));
2400         }
2401     }
2402     else if(activating)
2403     {
2404         LOG_MAP_MSG_XGDEVONLY2("Line %i has no activation function", P_ToIndex(line));
2405     }
2406     else
2407     {
2408         LOG_MAP_MSG_XGDEVONLY2("Line %i has no deactivation function", P_ToIndex(line));
2409     }
2410 
2411     // Now do any secondary actions that should happen AFTER
2412     // the function of the line (regardless if one was applied or not)
2413     if(activating)
2414     {
2415         XL_Message(activator, info->actMsg,
2416                    (info->flags2 & LTF2_GLOBAL_A_MSG) != 0);
2417 
2418         if(info->actSound)
2419         {
2420             S_SectorSound((Sector *)P_GetPtrp(line, DMU_FRONT_SECTOR), info->actSound);
2421         }
2422 
2423         // Change the texture of the line if asked to.
2424         if(info->wallSection && info->actMaterial != NOMATERIALID)
2425         {
2426             XL_ChangeMaterial(line, sidenum, info->wallSection,
2427                               (world_Material *)P_ToPtr(DMU_MATERIAL, info->actMaterial));
2428         }
2429 
2430         // Change the class of the line if asked to
2431         if(info->actLineType)
2432         {
2433             XL_SetLineType(line, info->actLineType);
2434         }
2435     }
2436     else
2437     {
2438         XL_Message(activator, info->deactMsg, (info->flags2 & LTF2_GLOBAL_D_MSG) != 0);
2439 
2440         if(info->deactSound)
2441         {
2442             S_SectorSound((Sector *)P_GetPtrp(line, DMU_FRONT_SECTOR), info->deactSound);
2443         }
2444 
2445         // Change the texture of the line if asked to.
2446         if(info->wallSection && info->deactMaterial != NOMATERIALID)
2447         {
2448             XL_ChangeMaterial(line, sidenum, info->wallSection,
2449                               (world_Material *)P_ToPtr(DMU_MATERIAL, info->deactMaterial));
2450         }
2451 
2452         // Change the class of the line if asked to.
2453         if(info->deactLineType)
2454         {
2455             XL_SetLineType(line, info->deactLineType);
2456         }
2457     }
2458 }
2459 
2460 /**
2461  * XL_CheckKeys
2462  */
XL_CheckKeys(mobj_t * mo,int flags2,dd_bool doMsg,dd_bool doSfx)2463 dd_bool XL_CheckKeys(mobj_t* mo, int flags2, dd_bool doMsg, dd_bool doSfx)
2464 {
2465     player_t*           act = mo->player;
2466 #if __JDOOM__ || __JDOOM64__
2467     int                 num = 6;
2468     int*                keys = (int *) act->keys;
2469     int                 badsound = SFX_OOF;
2470 #elif __JHERETIC__
2471     int                 num = 3;
2472     dd_bool*            keys = act->keys;
2473     int                 badsound = SFX_PLROOF;
2474 #elif __JSTRIFE__
2475 //// @todo FIXME!!!
2476     int                 num = 3;
2477     int*                keys = (int *) act->keys;
2478     int                 badsound = SFX_NONE;
2479 #endif
2480     int                 i;
2481 
2482     for(i = 0; i < num; ++i)
2483     {
2484         if((flags2 & LTF2_KEY(i)) && !keys[i])
2485         {   // This key is missing!
2486 
2487             // Show a message?
2488             if(doMsg)
2489             {
2490                 sprintf(msgbuf, "YOU NEED A %s.", GET_TXT(TXT_KEY1 + i));
2491                 XL_Message(mo, msgbuf, false);
2492             }
2493 
2494             // Play a sound?
2495             if(doSfx)
2496                 S_ConsoleSound(badsound, mo, act - players);
2497 
2498             return false;
2499         }
2500     }
2501 
2502     return true;
2503 }
2504 
2505 /**
2506  * Decides if the event leads to (de)activation.
2507  * Line must be extended.
2508  * Most conditions use AND (act method, game mode and difficult use OR).
2509  *
2510  * @return              @c true, iff the event is processed.
2511  */
XL_LineEvent(int evtype,int linetype,Line * line,int sidenum,void * data)2512 int XL_LineEvent(int evtype, int linetype, Line* line, int sidenum,
2513                  void* data)
2514 {
2515     LOG_AS("XL_LineEvent");
2516 
2517     int                 i;
2518     xline_t*            xline;
2519     xgline_t*           xg;
2520     linetype_t*         info;
2521     dd_bool             active;
2522     mobj_t*             activator_thing = (mobj_t *) data;
2523     player_t*           activator = 0;
2524     dd_bool             anyTrigger = false;
2525 
2526     // Clients rely on the server, they don't do XG themselves.
2527     if(IS_CLIENT)
2528         return false;
2529 
2530     xline = P_ToXLine(line);
2531     xg = xline->xg;
2532     info = &xg->info;
2533     active = xg->active;
2534 
2535     if(activator_thing)
2536         activator = activator_thing->player;
2537 
2538 #if __JDOOM__ || __JDOOM64__
2539     // BOOM intergration
2540     if((xline->flags & ML_ALLTRIGGER) && !(info->flags2 & LTF2_OVERRIDE_ANY))
2541         anyTrigger = true;
2542 #endif
2543 
2544     LOG_MAP_MSG_XGDEVONLY2("%s line %i, side %i (chained type %i)%s",
2545            EVTYPESTR(evtype) << P_ToIndex(line) << sidenum << linetype
2546            << (anyTrigger? " ANY Trigger" : ""));
2547 
2548     if(xg->disabled)
2549     {
2550         LOG_MAP_MSG_XGDEVONLY("LINE IS DISABLED, ABORTING EVENT");
2551         return false; // The line is disabled.
2552     }
2553 
2554     // This is a chained event.
2555     if(linetype)
2556     {
2557         if(!XL_GetType(linetype))
2558             return false;
2559         info = &typebuffer;
2560     }
2561 
2562     // Process chained event first. It takes precedence.
2563     if(info->evChain)
2564     {
2565         if(XL_LineEvent(evtype, info->evChain, line, sidenum, data))
2566         {
2567             LOG_MAP_MSG_XGDEVONLY2("Event %s, line %i, side %i OVERRIDDEN BY EVENT CHAIN %i",
2568                    EVTYPESTR(evtype) << P_ToIndex(line) << sidenum << info->evChain);
2569             return true;
2570         }
2571     }
2572 
2573     // Check restrictions and conditions that will prevent processing
2574     // the event.
2575     if((active && info->actType == LTACT_COUNTED_OFF) ||
2576        (!active && info->actType == LTACT_COUNTED_ON))
2577     {
2578         // Can't be processed at this time.
2579         LOG_MAP_MSG_XGDEVONLY2("Line %i: Active=%i, type=%i ABORTING EVENT",
2580                P_ToIndex(line) << active << info->actType);
2581         return false;
2582     }
2583 
2584     // Check the type of the event vs. the requirements of the line.
2585     if(evtype == XLE_CHAIN || evtype == XLE_FUNC)
2586         goto type_passes;
2587     if(evtype == XLE_USE &&
2588        ((((info->flags & LTF_PLAYER_USE_A) && activator && !active) ||
2589          ((info->flags & LTF_OTHER_USE_A) && !activator && !active) ||
2590          ((info->flags & LTF_PLAYER_USE_D) && activator && active) ||
2591          ((info->flags & LTF_OTHER_USE_D) && !activator && active)) || anyTrigger))
2592         goto type_passes;
2593     if(evtype == XLE_SHOOT &&
2594        ((((info->flags & LTF_PLAYER_SHOOT_A) && activator && !active) ||
2595          ((info->flags & LTF_OTHER_SHOOT_A) && !activator && !active) ||
2596          ((info->flags & LTF_PLAYER_SHOOT_D) && activator && active) ||
2597          ((info->flags & LTF_OTHER_SHOOT_D) && !activator && active)) || anyTrigger))
2598         goto type_passes;
2599     if(evtype == XLE_CROSS &&
2600        ((((info->flags & LTF_PLAYER_CROSS_A) && activator && !active) ||
2601          ((info->flags & LTF_MONSTER_CROSS_A) &&
2602           (activator_thing && (activator_thing->flags & MF_COUNTKILL)) && !active) ||
2603          ((info->flags & LTF_MISSILE_CROSS_A) &&
2604           (activator_thing && (activator_thing->flags & MF_MISSILE)) && !active) ||
2605          ((info->flags & LTF_ANY_CROSS_A) && !active) ||
2606          ((info->flags & LTF_PLAYER_CROSS_D) && activator && active) ||
2607          ((info->flags & LTF_MONSTER_CROSS_D) &&
2608           (activator_thing && (activator_thing->flags & MF_COUNTKILL)) && active) ||
2609          ((info->flags & LTF_MISSILE_CROSS_D) &&
2610           (activator_thing && (activator_thing->flags & MF_MISSILE)) && active) ||
2611          ((info->flags & LTF_ANY_CROSS_D) && active)) || anyTrigger))
2612         goto type_passes;
2613     if(evtype == XLE_HIT &&
2614        ((((info->flags & LTF_PLAYER_HIT_A) && activator && !active) ||
2615          ((info->flags & LTF_OTHER_HIT_A) && !activator && !active) ||
2616          ((info->flags & LTF_MONSTER_HIT_A) &&
2617           (activator_thing && (activator_thing->flags & MF_COUNTKILL)) && !active) ||
2618          ((info->flags & LTF_MISSILE_HIT_A) &&
2619           (activator_thing && (activator_thing->flags & MF_MISSILE)) && !active) ||
2620          ((info->flags & LTF_ANY_HIT_A) && !active) ||
2621          ((info->flags & LTF_PLAYER_HIT_D) && activator && active) ||
2622          ((info->flags & LTF_OTHER_HIT_D) && !activator && active) ||
2623          ((info->flags & LTF_MONSTER_HIT_D) &&
2624           (activator_thing && (activator_thing->flags & MF_COUNTKILL)) && active) ||
2625          ((info->flags & LTF_MISSILE_HIT_D) &&
2626            (activator_thing && (activator_thing->flags & MF_MISSILE)) && active) ||
2627          ((info->flags & LTF_ANY_HIT_D) && active)) || anyTrigger))
2628         goto type_passes;
2629     if(evtype == XLE_TICKER &&
2630        (((info->flags & LTF_TICKER_A) && !active) ||
2631         ((info->flags & LTF_TICKER_D) && active)))
2632         goto type_passes;
2633 
2634     // Type doesn't pass, sorry.
2635     LOG_MAP_MSG_XGDEVONLY2("Line %i: ACT REQUIREMENTS NOT FULFILLED, ABORTING EVENT", P_ToIndex(line));
2636     return false;
2637 
2638   type_passes:
2639 
2640     if(info->flags & LTF_NO_OTHER_USE_SECRET)
2641     {
2642         // Non-players can't use this line if line is flagged secret.
2643         if(evtype == XLE_USE && !activator && (xline->flags & ML_SECRET))
2644         {
2645             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to no_other_use_secret", P_ToIndex(line));
2646             return false;
2647         }
2648     }
2649 
2650     if(info->flags & LTF_MOBJ_GONE)
2651     {
2652         if(Thinker_Iterate(P_MobjThinker, XL_CheckMobjGone, &info->aparm[9]))
2653             return false;
2654     }
2655 
2656     if(info->flags & LTF_ACTIVATOR_TYPE)
2657     {
2658         // Check the activator's type.
2659         if(!activator_thing || activator_thing->type != info->aparm[9])
2660         {
2661             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to activator type", P_ToIndex(line));
2662             return false;
2663         }
2664     }
2665 
2666     if((evtype == XLE_USE || evtype == XLE_SHOOT || evtype == XLE_CROSS) &&
2667        !(info->flags2 & LTF2_TWOSIDED))
2668     {
2669         // Only allow (de)activation from the front side.
2670         if(sidenum != 0)
2671         {
2672             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to line side test", P_ToIndex(line));
2673             return false;
2674         }
2675     }
2676 
2677     // Check counting.
2678     if(!info->actCount)
2679     {
2680         LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to Count = 0", P_ToIndex(line));
2681         return false;
2682     }
2683 
2684     // More requirements.
2685     if((info->flags2 & LTF2_HEALTH_ABOVE) && activator_thing &&
2686        activator_thing->health <= info->aparm[0])
2687         return false;
2688     if((info->flags2 & LTF2_HEALTH_BELOW) && activator_thing &&
2689        activator_thing->health >= info->aparm[1])
2690         return false;
2691     if((info->flags2 & LTF2_POWER_ABOVE) &&
2692        (!activator || activator->armorPoints <= info->aparm[2]))
2693         return false;
2694     if((info->flags2 & LTF2_POWER_BELOW) &&
2695        (!activator || activator->armorPoints >= info->aparm[3]))
2696         return false;
2697     if(info->flags2 & LTF2_LINE_ACTIVE)
2698         if(!XL_CheckLineStatus(line, info->aparm[4], info->aparm[5], true,
2699                                activator_thing))
2700         {
2701             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to line_active test", P_ToIndex(line));
2702             return false;
2703         }
2704     if(info->flags2 & LTF2_LINE_INACTIVE)
2705         if(!XL_CheckLineStatus(line, info->aparm[6], info->aparm[7], false,
2706                                activator_thing))
2707         {
2708             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to line_inactive test", P_ToIndex(line));
2709             return false;
2710         }
2711     // Check game mode.
2712     if (IS_NETGAME)
2713     {
2714         const int netFlags = info->flags2 & (LTF2_COOPERATIVE | LTF2_DEATHMATCH);
2715         if (!netFlags)
2716         {
2717             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to netgame mode", P_ToIndex(line));
2718             return false;
2719         }
2720         if (netFlags != (LTF2_COOPERATIVE | LTF2_DEATHMATCH))
2721         {
2722             // Need to check which game mode.
2723             const auto netType = gfw_Rule(deathmatch);
2724             if (((netFlags & LTF2_COOPERATIVE) && netType != 0) ||
2725                 ((netFlags & LTF2_DEATHMATCH) && netType == 0))
2726             {
2727                 LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to non-matching deathmatch/coop flag", P_ToIndex(line));
2728                 return false;
2729             }
2730         }
2731     }
2732     else
2733     {
2734         if(!(info->flags2 & LTF2_SINGLEPLAYER))
2735         {
2736             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to game mode (1p)", P_ToIndex(line));
2737             return false;
2738         }
2739     }
2740 
2741     // Check skill level.
2742     // SM_NOTHINGS will be interpreted as SM_BABY.
2743     if(gfw_Rule(skill) < 1)
2744         i = 1;
2745     else if(gfw_Rule(skill) > 3)
2746         i = 4;
2747     else
2748         i = 1 << (gfw_Rule(skill) - 1);
2749 
2750     if(!(info->flags2 & (i << LTF2_SKILL_SHIFT)))
2751     {
2752         LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to skill level (%i)",
2753                P_ToIndex(line) << gfw_Rule(skill));
2754         return false;
2755     }
2756 
2757     // Check activator color.
2758     if(info->flags2 & LTF2_COLOR)
2759     {
2760         if(!activator)
2761             return false;
2762         if(cfg.playerColor[activator - players] != info->aparm[8])
2763         {
2764             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to activator color (%i)",
2765                    P_ToIndex(line) << cfg.playerColor[activator-players]);
2766             return false;
2767         }
2768     }
2769 
2770     // Keys require that the activator is a player.
2771     if(info->
2772        flags2 & (LTF2_KEY1 | LTF2_KEY2 | LTF2_KEY3 | LTF2_KEY4 | LTF2_KEY5 |
2773                  LTF2_KEY6))
2774     {
2775         // Check keys.
2776         if(!activator)
2777         {
2778             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to missing key (no activator)", P_ToIndex(line));
2779             return false;
2780         }
2781 
2782         // Check that all the flagged keys are present.
2783         if(!XL_CheckKeys(activator_thing, info->flags2, true,
2784                          (evtype == XLE_USE? true : false)))
2785         {
2786             LOG_MAP_MSG_XGDEVONLY2("Line %i: ABORTING EVENT due to missing key", P_ToIndex(line));
2787             return false;        // Keys missing!
2788         }
2789     }
2790 
2791     // All tests passed, use this event.
2792     if(info->actCount > 0 && evtype != XLE_CHAIN && evtype != XLE_FUNC)
2793     {
2794         // Decrement counter.
2795         info->actCount--;
2796 
2797         LOG_MAP_MSG_XGDEVONLY2("Line %i: Decrementing counter, now %i",
2798                P_ToIndex(line) << info->actCount);
2799     }
2800 
2801     XL_ActivateLine(!active, info, line, sidenum, activator_thing, evtype);
2802     return true;
2803 }
2804 
2805 /**
2806  * @return              @c true, if the event was processed.
2807  */
XL_CrossLine(Line * line,int sidenum,mobj_t * thing)2808 int XL_CrossLine(Line* line, int sidenum, mobj_t* thing)
2809 {
2810     if(!line || !P_ToXLine(line)->xg)
2811         return false;
2812 
2813     return XL_LineEvent(XLE_CROSS, 0, line, sidenum, thing);
2814 }
2815 
2816 /**
2817  * @return              @c true, if the event was processed.
2818  */
XL_UseLine(Line * line,int sidenum,mobj_t * thing)2819 int XL_UseLine(Line* line, int sidenum, mobj_t* thing)
2820 {
2821     if(!line || !P_ToXLine(line)->xg)
2822         return false;
2823 
2824     return XL_LineEvent(XLE_USE, 0, line, sidenum, thing);
2825 }
2826 
2827 /**
2828  * @return              @c true, if the event was processed.
2829  */
XL_ShootLine(Line * line,int sidenum,mobj_t * thing)2830 int XL_ShootLine(Line *line, int sidenum, mobj_t *thing)
2831 {
2832     if(!line || !P_ToXLine(line)->xg)
2833         return false;
2834 
2835     return XL_LineEvent(XLE_SHOOT, 0, line, sidenum, thing);
2836 }
2837 
XL_HitLine(Line * line,int sidenum,mobj_t * thing)2838 int XL_HitLine(Line *line, int sidenum, mobj_t *thing)
2839 {
2840     if(!line || !P_ToXLine(line)->xg)
2841         return false;
2842 
2843     return XL_LineEvent(XLE_HIT, 0, line, sidenum, thing);
2844 }
2845 
XL_DoChain(Line * line,int chain,dd_bool activating,mobj_t * actThing)2846 void XL_DoChain(Line *line, int chain, dd_bool activating, mobj_t *actThing)
2847 {
2848     LOG_AS("XL_DoChain");
2849 
2850     // We'll use a dummy for the chain.
2851     Line *dummyLineDef     = P_AllocDummyLine();
2852     xline_t *xdummyLineDef = P_ToXLine(dummyLineDef);
2853 
2854     xdummyLineDef->xg = (xgline_t *)Z_Calloc(sizeof(xgline_t), PU_MAP, 0);
2855 
2856     P_SetPtrp(dummyLineDef, DMU_FRONT_SECTOR, P_GetPtrp(line, DMU_FRONT_SECTOR));
2857     if(0 != P_GetPtrp(line, DMU_BACK))
2858     {
2859         P_SetPtrp(dummyLineDef, DMU_BACK_SECTOR, P_GetPtrp(line, DMU_BACK_SECTOR));
2860     }
2861 
2862     LOG_MAP_MSG_XGDEVONLY2("Line %i, chained type %i", P_ToIndex(line) << chain);
2863     LOG_MAP_MSG_XGDEVONLY2("(dummy line will show up as %i)", P_ToIndex(dummyLineDef));
2864 
2865     // Copy all properties to the dummies.
2866     P_CopyLine(dummyLineDef, line);
2867 
2868     xdummyLineDef->xg->active = !activating;
2869 
2870     // Make the chain event
2871     XL_LineEvent(XLE_CHAIN, chain, dummyLineDef, 0, actThing);
2872 
2873     Z_Free(xdummyLineDef->xg);
2874     P_FreeDummyLine(dummyLineDef);
2875 }
2876 
2877 /**
2878  * XG lines get to think.
2879  */
XL_Thinker(void * xlThinkerPtr)2880 void XL_Thinker(void *xlThinkerPtr)
2881 {
2882     DENG2_ASSERT(xlThinkerPtr);
2883     LOG_AS("XL_Thinker");
2884 
2885     xlthinker_t *xl = static_cast<xlthinker_t *>(xlThinkerPtr);
2886     Line *line      = xl->line;
2887 
2888     // Clients rely on the server, they don't do XG themselves.
2889     if(IS_CLIENT) return;
2890 
2891     if(!xl->line) return;
2892 
2893     // Not an xline? Most perculiar...
2894     xline_t *xline = P_ToXLine(line);
2895     if(!xline) return;
2896 
2897     // Not an extended line?
2898     xgline_t *xg = xline->xg;
2899     if(!xg) return;
2900 
2901     // If disabled do nothing.
2902     if(xg->disabled) return;
2903 
2904     linetype_t *info = &xg->info;
2905     float levtime = TIC2FLT(mapTime);
2906 
2907     // Increment time.
2908     if(xg->timer >= 0)
2909     {
2910         xg->timer++;
2911         xg->tickerTimer++;
2912     }
2913 
2914     // Activation by ticker.
2915     if((info->tickerEnd <= 0 ||
2916         (levtime >= info->tickerStart && levtime <= info->tickerEnd)) &&
2917        xg->tickerTimer > info->tickerInterval)
2918     {
2919         if(info->flags & LTF_TICKER)
2920         {
2921             xg->tickerTimer = 0;
2922             XL_LineEvent(XLE_TICKER, 0, line, 0, XG_DummyThing());
2923         }
2924 
2925         // How about some forced functions?
2926         if((((info->flags2 & LTF2_WHEN_ACTIVE) && xg->active) ||
2927             ((info->flags2 & LTF2_WHEN_INACTIVE) && !xg->active)) &&
2928             (!(info->flags2 & LTF2_WHEN_LAST) || info->actCount == 1))
2929         {
2930             XL_DoFunction(info, line, 0, (mobj_t *)xg->activator, XLE_FORCED);
2931         }
2932     }
2933 
2934     // Only process active chain sequences.
2935     if(xg->active && info->lineClass == LTC_CHAIN_SEQUENCE)
2936     {
2937         xg->chTimer -= TIC2FLT(1);
2938 
2939         // idata = current pos
2940         // fdata = count down seconds
2941 
2942         // i1..i19: line types
2943         // f0: interval randomness (100 means real interval can be 0%..200%).
2944         // f1..f19: intervals (seconds)
2945 
2946         // If the counter goes to zero, it's time to execute the chain.
2947         if(xg->chTimer < 0)
2948         {
2949             LOG_MAP_MSG_XGDEVONLY2("Line %i, executing...", P_ToIndex(line));
2950 
2951             // Are there any more chains?
2952             if(xg->chIdx < DDLT_MAX_PARAMS && info->iparm[xg->chIdx])
2953             {
2954                 // Only send activation events.
2955                 XL_DoChain(line, info->iparm[xg->chIdx], true, (mobj_t *)xg->activator);
2956 
2957                 // Advance to the next one.
2958                 xg->chIdx++;
2959 
2960                 // Are we out of chains?
2961                 if((xg->chIdx == DDLT_MAX_PARAMS || !info->iparm[xg->chIdx]) &&
2962                    (info->iparm[0] & CHSF_LOOP))
2963                 {
2964                     // Loop back to beginning.
2965                     xg->chIdx = 1;
2966                 }
2967 
2968                 // If there are more chains, get the next interval.
2969                 if(xg->chIdx < DDLT_MAX_PARAMS && info->iparm[xg->chIdx])
2970                 {
2971                     // Start counting it down.
2972                     xg->chTimer =
2973                         XG_RandomPercentFloat(info->fparm[xg->chIdx],
2974                                               info->fparm[0]);
2975                 }
2976             }
2977             else if(info->iparm[0] & CHSF_DEACTIVATE_WHEN_DONE)
2978             {
2979                 // The sequence has been completed.
2980                 XL_ActivateLine(false, info, line, 0, (mobj_t *)xg->activator, XLE_CHAIN);
2981             }
2982         }
2983     }
2984 
2985     // Check for automatical (de)activation.
2986     if(((info->actType == LTACT_COUNTED_OFF ||
2987          info->actType == LTACT_FLIP_COUNTED_OFF) && xg->active) ||
2988        ((info->actType == LTACT_COUNTED_ON ||
2989          info->actType == LTACT_FLIP_COUNTED_ON) && !xg->active))
2990     {
2991         if(info->actTime >= 0 && xg->timer > FLT2TIC(info->actTime))
2992         {
2993             LOG_MAP_MSG_XGDEVONLY2("Line %i, timed to go %s",
2994                     P_ToIndex(line)
2995                     << (xg->active ? "INACTIVE" : "ACTIVE"));
2996 
2997             // Swap line state without any checks.
2998             XL_ActivateLine(!xg->active, info, line, 0, dummyThing, XLE_AUTO);
2999         }
3000     }
3001 
3002     if(info->materialMoveSpeed)
3003     {
3004         // The texture should be moved. Calculate the offsets.
3005         coord_t current[2]; // The current offset.
3006         Side* side;
3007         float spd = info->materialMoveSpeed;
3008         coord_t offset[2];
3009         angle_t ang = ((angle_t) (ANGLE_MAX * (info->materialMoveAngle / 360))) >> ANGLETOFINESHIFT;
3010 
3011         offset[VX] = -(FIX2FLT(finecosine[ang]) * spd);
3012         offset[VY] = FIX2FLT(finesine[ang]) * spd;
3013 
3014         /**
3015          * Apply to both sides of the line.
3016          * These are group offsets. All surfaces on a given side are moved
3017          * using the same texmove speed/angle.
3018          *
3019          * @todo Implement per-surface texture movement also which would
3020          *        be added to each independantly.
3021          */
3022 
3023         // Front side.
3024         side = (Side *)P_GetPtrp(line, DMU_FRONT);
3025         if(side)
3026         {
3027             P_GetDoublepv(side, DMU_TOP_MATERIAL_OFFSET_XY, current);
3028             current[VX] += offset[VX];
3029             current[VY] += offset[VY];
3030             P_SetDoublepv(side, DMU_TOP_MATERIAL_OFFSET_XY, current);
3031 
3032             P_GetDoublepv(side, DMU_MIDDLE_MATERIAL_OFFSET_XY, current);
3033             current[VX] += offset[VX];
3034             current[VY] += offset[VY];
3035             P_SetDoublepv(side, DMU_MIDDLE_MATERIAL_OFFSET_XY, current);
3036 
3037             P_GetDoublepv(side, DMU_BOTTOM_MATERIAL_OFFSET_XY, current);
3038             current[VX] += offset[VX];
3039             current[VY] += offset[VY];
3040             P_SetDoublepv(side, DMU_BOTTOM_MATERIAL_OFFSET_XY, current);
3041         }
3042 
3043         // Back side.
3044         side = (Side *)P_GetPtrp(line, DMU_BACK);
3045         if(side)
3046         {
3047             P_GetDoublepv(side, DMU_TOP_MATERIAL_OFFSET_XY, current);
3048             current[VX] += offset[VX];
3049             current[VY] += offset[VY];
3050             P_SetDoublepv(side, DMU_TOP_MATERIAL_OFFSET_XY, current);
3051 
3052             P_GetDoublepv(side, DMU_MIDDLE_MATERIAL_OFFSET_XY, current);
3053             current[VX] += offset[VX];
3054             current[VY] += offset[VY];
3055             P_SetDoublepv(side, DMU_MIDDLE_MATERIAL_OFFSET_XY, current);
3056 
3057             P_GetDoublepv(side, DMU_BOTTOM_MATERIAL_OFFSET_XY, current);
3058             current[VX] += offset[VX];
3059             current[VY] += offset[VY];
3060             P_SetDoublepv(side, DMU_BOTTOM_MATERIAL_OFFSET_XY, current);
3061         }
3062     }
3063 }
3064 
3065 /**
3066  * During update, definitions are re-read, so the pointers need to be
3067  * updated. However, this is a bit messy operation, prone to errors.
3068  * Instead, we just disable XG...
3069  */
XL_Update(void)3070 void XL_Update(void)
3071 {
3072     int i;
3073     xline_t *xline;
3074 
3075     // It's all PU_MAP memory, so we can just lose it.
3076     for(i = 0; i < numlines; ++i)
3077     {
3078         xline = P_GetXLine(i);
3079 
3080         if(xline->xg)
3081         {
3082             xline->xg = NULL;
3083             xline->special = 0;
3084         }
3085     }
3086 }
3087 
3088 #endif
3089