1 /*
2 ** p_lnspec.cpp
3 ** Handles line specials
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2007 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 ** Each function returns true if it caused something to happen
34 ** or false if it could not perform the desired action.
35 */
36 
37 #include "doomstat.h"
38 #include "p_local.h"
39 #include "p_lnspec.h"
40 #include "p_enemy.h"
41 #include "g_level.h"
42 #include "v_palette.h"
43 #include "tables.h"
44 #include "i_system.h"
45 #include "a_sharedglobal.h"
46 #include "a_lightning.h"
47 #include "statnums.h"
48 #include "s_sound.h"
49 #include "templates.h"
50 #include "a_keys.h"
51 #include "gi.h"
52 #include "m_random.h"
53 #include "p_conversation.h"
54 #include "a_strifeglobal.h"
55 #include "r_data/r_translate.h"
56 #include "p_3dmidtex.h"
57 #include "d_net.h"
58 #include "d_event.h"
59 #include "gstrings.h"
60 #include "r_data/colormaps.h"
61 
62 #define FUNC(a) static int a (line_t *ln, AActor *it, bool backSide, \
63 	int arg0, int arg1, int arg2, int arg3, int arg4)
64 
65 #define SPEED(a)		((a)*(FRACUNIT/8))
66 #define TICS(a)			(((a)*TICRATE)/35)
67 #define OCTICS(a)		(((a)*TICRATE)/8)
68 #define	BYTEANGLE(a)	((angle_t)((a)<<24))
69 #define CRUSHTYPE(a)	((a)==1? false : (a)==2? true : gameinfo.gametype == GAME_Hexen)
70 
71 static FRandom pr_glass ("GlassBreak");
72 
73 // There are aliases for the ACS specials that take names instead of numbers.
74 // This table maps them onto the real number-based specials.
75 BYTE NamedACSToNormalACS[7] =
76 {
77 	ACS_Execute,
78 	ACS_Suspend,
79 	ACS_Terminate,
80 	ACS_LockedExecute,
81 	ACS_LockedExecuteDoor,
82 	ACS_ExecuteWithResult,
83 	ACS_ExecuteAlways,
84 };
85 
MODtoDamageType(int mod)86 FName MODtoDamageType (int mod)
87 {
88 	switch (mod)
89 	{
90 	default:	return NAME_None;			break;
91 	case 9:		return NAME_BFGSplash;		break;
92 	case 12:	return NAME_Drowning;		break;
93 	case 13:	return NAME_Slime;			break;
94 	case 14:	return NAME_Fire;			break;
95 	case 15:	return NAME_Crush;			break;
96 	case 16:	return NAME_Telefrag;		break;
97 	case 17:	return NAME_Falling;		break;
98 	case 18:	return NAME_Suicide;		break;
99 	case 20:	return NAME_Exit;			break;
100 	case 22:	return NAME_Melee;			break;
101 	case 23:	return NAME_Railgun;		break;
102 	case 24:	return NAME_Ice;			break;
103 	case 25:	return NAME_Disintegrate;	break;
104 	case 26:	return NAME_Poison;			break;
105 	case 27:	return NAME_Electric;		break;
106 	case 1000:	return NAME_Massacre;		break;
107 	}
108 }
109 
FUNC(LS_NOP)110 FUNC(LS_NOP)
111 {
112 	return false;
113 }
114 
FUNC(LS_Polyobj_RotateLeft)115 FUNC(LS_Polyobj_RotateLeft)
116 // Polyobj_RotateLeft (po, speed, angle)
117 {
118 	return EV_RotatePoly (ln, arg0, arg1, arg2, 1, false);
119 }
120 
FUNC(LS_Polyobj_RotateRight)121 FUNC(LS_Polyobj_RotateRight)
122 // Polyobj_rotateRight (po, speed, angle)
123 {
124 	return EV_RotatePoly (ln, arg0, arg1, arg2, -1, false);
125 }
126 
FUNC(LS_Polyobj_Move)127 FUNC(LS_Polyobj_Move)
128 // Polyobj_Move (po, speed, angle, distance)
129 {
130 	return EV_MovePoly (ln, arg0, SPEED(arg1), BYTEANGLE(arg2), arg3 * FRACUNIT, false);
131 }
132 
FUNC(LS_Polyobj_MoveTimes8)133 FUNC(LS_Polyobj_MoveTimes8)
134 // Polyobj_MoveTimes8 (po, speed, angle, distance)
135 {
136 	return EV_MovePoly (ln, arg0, SPEED(arg1), BYTEANGLE(arg2), arg3 * FRACUNIT * 8, false);
137 }
138 
FUNC(LS_Polyobj_MoveTo)139 FUNC(LS_Polyobj_MoveTo)
140 // Polyobj_MoveTo (po, speed, x, y)
141 {
142 	return EV_MovePolyTo (ln, arg0, SPEED(arg1), arg2 << FRACBITS, arg3 << FRACBITS, false);
143 }
144 
FUNC(LS_Polyobj_MoveToSpot)145 FUNC(LS_Polyobj_MoveToSpot)
146 // Polyobj_MoveToSpot (po, speed, tid)
147 {
148 	FActorIterator iterator (arg2);
149 	AActor *spot = iterator.Next();
150 	if (spot == NULL) return false;
151 	return EV_MovePolyTo (ln, arg0, SPEED(arg1), spot->X(), spot->Y(), false);
152 }
153 
FUNC(LS_Polyobj_DoorSwing)154 FUNC(LS_Polyobj_DoorSwing)
155 // Polyobj_DoorSwing (po, speed, angle, delay)
156 {
157 	return EV_OpenPolyDoor (ln, arg0, arg1, BYTEANGLE(arg2), arg3, 0, PODOOR_SWING);
158 }
159 
FUNC(LS_Polyobj_DoorSlide)160 FUNC(LS_Polyobj_DoorSlide)
161 // Polyobj_DoorSlide (po, speed, angle, distance, delay)
162 {
163 	return EV_OpenPolyDoor (ln, arg0, SPEED(arg1), BYTEANGLE(arg2), arg4, arg3*FRACUNIT, PODOOR_SLIDE);
164 }
165 
FUNC(LS_Polyobj_OR_RotateLeft)166 FUNC(LS_Polyobj_OR_RotateLeft)
167 // Polyobj_OR_RotateLeft (po, speed, angle)
168 {
169 	return EV_RotatePoly (ln, arg0, arg1, arg2, 1, true);
170 }
171 
FUNC(LS_Polyobj_OR_RotateRight)172 FUNC(LS_Polyobj_OR_RotateRight)
173 // Polyobj_OR_RotateRight (po, speed, angle)
174 {
175 	return EV_RotatePoly (ln, arg0, arg1, arg2, -1, true);
176 }
177 
FUNC(LS_Polyobj_OR_Move)178 FUNC(LS_Polyobj_OR_Move)
179 // Polyobj_OR_Move (po, speed, angle, distance)
180 {
181 	return EV_MovePoly (ln, arg0, SPEED(arg1), BYTEANGLE(arg2), arg3 * FRACUNIT, true);
182 }
183 
FUNC(LS_Polyobj_OR_MoveTimes8)184 FUNC(LS_Polyobj_OR_MoveTimes8)
185 // Polyobj_OR_MoveTimes8 (po, speed, angle, distance)
186 {
187 	return EV_MovePoly (ln, arg0, SPEED(arg1), BYTEANGLE(arg2), arg3 * FRACUNIT * 8, true);
188 }
189 
FUNC(LS_Polyobj_OR_MoveTo)190 FUNC(LS_Polyobj_OR_MoveTo)
191 // Polyobj_OR_MoveTo (po, speed, x, y)
192 {
193 	return EV_MovePolyTo (ln, arg0, SPEED(arg1), arg2 << FRACBITS, arg3 << FRACBITS, true);
194 }
195 
FUNC(LS_Polyobj_OR_MoveToSpot)196 FUNC(LS_Polyobj_OR_MoveToSpot)
197 // Polyobj_OR_MoveToSpot (po, speed, tid)
198 {
199 	FActorIterator iterator (arg2);
200 	AActor *spot = iterator.Next();
201 	if (spot == NULL) return false;
202 	return EV_MovePolyTo (ln, arg0, SPEED(arg1), spot->X(), spot->Y(), true);
203 }
204 
FUNC(LS_Polyobj_Stop)205 FUNC(LS_Polyobj_Stop)
206 // Polyobj_Stop (po)
207 {
208 	return EV_StopPoly (arg0);
209 }
210 
FUNC(LS_Door_Close)211 FUNC(LS_Door_Close)
212 // Door_Close (tag, speed, lighttag)
213 {
214 	return EV_DoDoor (DDoor::doorClose, ln, it, arg0, SPEED(arg1), 0, 0, arg2);
215 }
216 
FUNC(LS_Door_Open)217 FUNC(LS_Door_Open)
218 // Door_Open (tag, speed, lighttag)
219 {
220 	return EV_DoDoor (DDoor::doorOpen, ln, it, arg0, SPEED(arg1), 0, 0, arg2);
221 }
222 
FUNC(LS_Door_Raise)223 FUNC(LS_Door_Raise)
224 // Door_Raise (tag, speed, delay, lighttag)
225 {
226 	return EV_DoDoor (DDoor::doorRaise, ln, it, arg0, SPEED(arg1), TICS(arg2), 0, arg3);
227 }
228 
FUNC(LS_Door_LockedRaise)229 FUNC(LS_Door_LockedRaise)
230 // Door_LockedRaise (tag, speed, delay, lock, lighttag)
231 {
232 	return EV_DoDoor (arg2 ? DDoor::doorRaise : DDoor::doorOpen, ln, it,
233 					  arg0, SPEED(arg1), TICS(arg2), arg3, arg4);
234 }
235 
FUNC(LS_Door_CloseWaitOpen)236 FUNC(LS_Door_CloseWaitOpen)
237 // Door_CloseWaitOpen (tag, speed, delay, lighttag)
238 {
239 	return EV_DoDoor (DDoor::doorCloseWaitOpen, ln, it, arg0, SPEED(arg1), OCTICS(arg2), 0, arg3);
240 }
241 
FUNC(LS_Door_Animated)242 FUNC(LS_Door_Animated)
243 // Door_Animated (tag, speed, delay, lock)
244 {
245 	if (arg3 != 0 && !P_CheckKeys (it, arg3, arg0 != 0))
246 		return false;
247 
248 	return EV_SlidingDoor (ln, it, arg0, arg1, arg2);
249 }
250 
FUNC(LS_Generic_Door)251 FUNC(LS_Generic_Door)
252 // Generic_Door (tag, speed, kind, delay, lock)
253 {
254 	int tag, lightTag;
255 	DDoor::EVlDoor type;
256 	bool boomgen = false;
257 
258 	switch (arg2 & 63)
259 	{
260 		case 0: type = DDoor::doorRaise;			break;
261 		case 1: type = DDoor::doorOpen;				break;
262 		case 2: type = DDoor::doorCloseWaitOpen;	break;
263 		case 3: type = DDoor::doorClose;			break;
264 		default: return false;
265 	}
266 	// Boom doesn't allow manual generalized doors to be activated while they move
267 	if (arg2 & 64) boomgen = true;
268 	if (arg2 & 128)
269 	{
270 		// New for 2.0.58: Finally support BOOM's local door light effect
271 		tag = 0;
272 		lightTag = arg0;
273 	}
274 	else
275 	{
276 		tag = arg0;
277 		lightTag = 0;
278 	}
279 	return EV_DoDoor (type, ln, it, tag, SPEED(arg1), OCTICS(arg3), arg4, lightTag, boomgen);
280 }
281 
FUNC(LS_Floor_LowerByValue)282 FUNC(LS_Floor_LowerByValue)
283 // Floor_LowerByValue (tag, speed, height)
284 {
285 	return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, -1, 0, false);
286 }
287 
FUNC(LS_Floor_LowerToLowest)288 FUNC(LS_Floor_LowerToLowest)
289 // Floor_LowerToLowest (tag, speed)
290 {
291 	return EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, -1, 0, false);
292 }
293 
FUNC(LS_Floor_LowerToHighest)294 FUNC(LS_Floor_LowerToHighest)
295 // Floor_LowerToHighest (tag, speed, adjust, hereticlower)
296 {
297 	return EV_DoFloor (DFloor::floorLowerToHighest, ln, arg0, SPEED(arg1), (arg2-128)*FRACUNIT, -1, 0, false, arg3==1);
298 }
299 
FUNC(LS_Floor_LowerToNearest)300 FUNC(LS_Floor_LowerToNearest)
301 // Floor_LowerToNearest (tag, speed)
302 {
303 	return EV_DoFloor (DFloor::floorLowerToNearest, ln, arg0, SPEED(arg1), 0, -1, 0, false);
304 }
305 
FUNC(LS_Floor_RaiseByValue)306 FUNC(LS_Floor_RaiseByValue)
307 // Floor_RaiseByValue (tag, speed, height)
308 {
309 	return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2, -1, 0, false);
310 }
311 
FUNC(LS_Floor_RaiseToHighest)312 FUNC(LS_Floor_RaiseToHighest)
313 // Floor_RaiseToHighest (tag, speed)
314 {
315 	return EV_DoFloor (DFloor::floorRaiseToHighest, ln, arg0, SPEED(arg1), 0, -1, 0, false);
316 }
317 
FUNC(LS_Floor_RaiseToNearest)318 FUNC(LS_Floor_RaiseToNearest)
319 // Floor_RaiseToNearest (tag, speed)
320 {
321 	return EV_DoFloor (DFloor::floorRaiseToNearest, ln, arg0, SPEED(arg1), 0, -1, 0, false);
322 }
323 
FUNC(LS_Floor_RaiseAndCrush)324 FUNC(LS_Floor_RaiseAndCrush)
325 // Floor_RaiseAndCrush (tag, speed, crush, crushmode)
326 {
327 	return EV_DoFloor (DFloor::floorRaiseAndCrush, ln, arg0, SPEED(arg1), 0, arg2, 0, CRUSHTYPE(arg3));
328 }
329 
FUNC(LS_Floor_RaiseAndCrushDoom)330 FUNC(LS_Floor_RaiseAndCrushDoom)
331 // Floor_RaiseAndCrushDoom (tag, speed, crush, crushmode)
332 {
333 	return EV_DoFloor (DFloor::floorRaiseAndCrushDoom, ln, arg0, SPEED(arg1), 0, arg2, 0, CRUSHTYPE(arg3));
334 }
335 
FUNC(LS_Floor_RaiseByValueTimes8)336 FUNC(LS_Floor_RaiseByValueTimes8)
337 // FLoor_RaiseByValueTimes8 (tag, speed, height)
338 {
339 	return EV_DoFloor (DFloor::floorRaiseByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, -1, 0, false);
340 }
341 
FUNC(LS_Floor_LowerByValueTimes8)342 FUNC(LS_Floor_LowerByValueTimes8)
343 // Floor_LowerByValueTimes8 (tag, speed, height)
344 {
345 	return EV_DoFloor (DFloor::floorLowerByValue, ln, arg0, SPEED(arg1), FRACUNIT*arg2*8, -1, 0, false);
346 }
347 
FUNC(LS_Floor_CrushStop)348 FUNC(LS_Floor_CrushStop)
349 // Floor_CrushStop (tag)
350 {
351 	return EV_FloorCrushStop (arg0);
352 }
353 
FUNC(LS_Floor_LowerInstant)354 FUNC(LS_Floor_LowerInstant)
355 // Floor_LowerInstant (tag, unused, height)
356 {
357 	return EV_DoFloor (DFloor::floorLowerInstant, ln, arg0, 0, arg2*FRACUNIT*8, -1, 0, false);
358 }
359 
FUNC(LS_Floor_RaiseInstant)360 FUNC(LS_Floor_RaiseInstant)
361 // Floor_RaiseInstant (tag, unused, height)
362 {
363 	return EV_DoFloor (DFloor::floorRaiseInstant, ln, arg0, 0, arg2*FRACUNIT*8, -1, 0, false);
364 }
365 
FUNC(LS_Floor_MoveToValueTimes8)366 FUNC(LS_Floor_MoveToValueTimes8)
367 // Floor_MoveToValueTimes8 (tag, speed, height, negative)
368 {
369 	return EV_DoFloor (DFloor::floorMoveToValue, ln, arg0, SPEED(arg1),
370 					   arg2*FRACUNIT*8*(arg3?-1:1), -1, 0, false);
371 }
372 
FUNC(LS_Floor_MoveToValue)373 FUNC(LS_Floor_MoveToValue)
374 // Floor_MoveToValue (tag, speed, height, negative)
375 {
376 	return EV_DoFloor (DFloor::floorMoveToValue, ln, arg0, SPEED(arg1),
377 					   arg2*FRACUNIT*(arg3?-1:1), -1, 0, false);
378 }
379 
FUNC(LS_Floor_RaiseToLowestCeiling)380 FUNC(LS_Floor_RaiseToLowestCeiling)
381 // Floor_RaiseToLowestCeiling (tag, speed)
382 {
383 	return EV_DoFloor (DFloor::floorRaiseToLowestCeiling, ln, arg0, SPEED(arg1), 0, -1, 0, false);
384 }
385 
FUNC(LS_Floor_RaiseByTexture)386 FUNC(LS_Floor_RaiseByTexture)
387 // Floor_RaiseByTexture (tag, speed)
388 {
389 	return EV_DoFloor (DFloor::floorRaiseByTexture, ln, arg0, SPEED(arg1), 0, -1, 0, false);
390 }
391 
FUNC(LS_Floor_RaiseByValueTxTy)392 FUNC(LS_Floor_RaiseByValueTxTy)
393 // Floor_RaiseByValueTxTy (tag, speed, height)
394 {
395 	return EV_DoFloor (DFloor::floorRaiseAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, -1, 0, false);
396 }
397 
FUNC(LS_Floor_LowerToLowestTxTy)398 FUNC(LS_Floor_LowerToLowestTxTy)
399 // Floor_LowerToLowestTxTy (tag, speed)
400 {
401 	return EV_DoFloor (DFloor::floorLowerAndChange, ln, arg0, SPEED(arg1), arg2*FRACUNIT, -1, 0, false);
402 }
403 
FUNC(LS_Floor_Waggle)404 FUNC(LS_Floor_Waggle)
405 // Floor_Waggle (tag, amplitude, frequency, delay, time)
406 {
407 	return EV_StartWaggle (arg0, ln, arg1, arg2, arg3, arg4, false);
408 }
409 
FUNC(LS_Ceiling_Waggle)410 FUNC(LS_Ceiling_Waggle)
411 // Ceiling_Waggle (tag, amplitude, frequency, delay, time)
412 {
413 	return EV_StartWaggle (arg0, ln, arg1, arg2, arg3, arg4, true);
414 }
415 
FUNC(LS_Floor_TransferTrigger)416 FUNC(LS_Floor_TransferTrigger)
417 // Floor_TransferTrigger (tag)
418 {
419 	return EV_DoChange (ln, trigChangeOnly, arg0);
420 }
421 
FUNC(LS_Floor_TransferNumeric)422 FUNC(LS_Floor_TransferNumeric)
423 // Floor_TransferNumeric (tag)
424 {
425 	return EV_DoChange (ln, numChangeOnly, arg0);
426 }
427 
FUNC(LS_Floor_Donut)428 FUNC(LS_Floor_Donut)
429 // Floor_Donut (pillartag, pillarspeed, slimespeed)
430 {
431 	return EV_DoDonut (arg0, ln, SPEED(arg1), SPEED(arg2));
432 }
433 
FUNC(LS_Generic_Floor)434 FUNC(LS_Generic_Floor)
435 // Generic_Floor (tag, speed, height, target, change/model/direct/crush)
436 {
437 	DFloor::EFloor type;
438 
439 	if (arg4 & 8)
440 	{
441 		switch (arg3)
442 		{
443 			case 1: type = DFloor::floorRaiseToHighest;			break;
444 			case 2: type = DFloor::floorRaiseToLowest;			break;
445 			case 3: type = DFloor::floorRaiseToNearest;			break;
446 			case 4: type = DFloor::floorRaiseToLowestCeiling;	break;
447 			case 5: type = DFloor::floorRaiseToCeiling;			break;
448 			case 6: type = DFloor::floorRaiseByTexture;			break;
449 			default:type = DFloor::floorRaiseByValue;			break;
450 		}
451 	}
452 	else
453 	{
454 		switch (arg3)
455 		{
456 			case 1: type = DFloor::floorLowerToHighest;			break;
457 			case 2: type = DFloor::floorLowerToLowest;			break;
458 			case 3: type = DFloor::floorLowerToNearest;			break;
459 			case 4: type = DFloor::floorLowerToLowestCeiling;	break;
460 			case 5: type = DFloor::floorLowerToCeiling;			break;
461 			case 6: type = DFloor::floorLowerByTexture;			break;
462 			default:type = DFloor::floorLowerByValue;			break;
463 		}
464 	}
465 
466 	return EV_DoFloor (type, ln, arg0, SPEED(arg1), arg2*FRACUNIT,
467 					   (arg4 & 16) ? 20 : -1, arg4 & 7, false);
468 
469 }
470 
FUNC(LS_Stairs_BuildDown)471 FUNC(LS_Stairs_BuildDown)
472 // Stair_BuildDown (tag, speed, height, delay, reset)
473 {
474 	return EV_BuildStairs (arg0, DFloor::buildDown, ln,
475 						   arg2 * FRACUNIT, SPEED(arg1), TICS(arg3), arg4, 0, 1);
476 }
477 
FUNC(LS_Stairs_BuildUp)478 FUNC(LS_Stairs_BuildUp)
479 // Stairs_BuildUp (tag, speed, height, delay, reset)
480 {
481 	return EV_BuildStairs (arg0, DFloor::buildUp, ln,
482 						   arg2 * FRACUNIT, SPEED(arg1), TICS(arg3), arg4, 0, 1);
483 }
484 
FUNC(LS_Stairs_BuildDownSync)485 FUNC(LS_Stairs_BuildDownSync)
486 // Stairs_BuildDownSync (tag, speed, height, reset)
487 {
488 	return EV_BuildStairs (arg0, DFloor::buildDown, ln,
489 						   arg2 * FRACUNIT, SPEED(arg1), 0, arg3, 0, 2);
490 }
491 
FUNC(LS_Stairs_BuildUpSync)492 FUNC(LS_Stairs_BuildUpSync)
493 // Stairs_BuildUpSync (tag, speed, height, reset)
494 {
495 	return EV_BuildStairs (arg0, DFloor::buildUp, ln,
496 						   arg2 * FRACUNIT, SPEED(arg1), 0, arg3, 0, 2);
497 }
498 
FUNC(LS_Stairs_BuildUpDoom)499 FUNC(LS_Stairs_BuildUpDoom)
500 // Stairs_BuildUpDoom (tag, speed, height, delay, reset)
501 {
502 	return EV_BuildStairs (arg0, DFloor::buildUp, ln,
503 						   arg2 * FRACUNIT, SPEED(arg1), TICS(arg3), arg4, 0, 0);
504 }
505 
FUNC(LS_Generic_Stairs)506 FUNC(LS_Generic_Stairs)
507 // Generic_Stairs (tag, speed, step, dir/igntxt, reset)
508 {
509 	DFloor::EStair type = (arg3 & 1) ? DFloor::buildUp : DFloor::buildDown;
510 	bool res = EV_BuildStairs (arg0, type, ln,
511 							   arg2 * FRACUNIT, SPEED(arg1), 0, arg4, arg3 & 2, 0);
512 
513 	if (res && ln && (ln->flags & ML_REPEAT_SPECIAL) && ln->special == Generic_Stairs)
514 		// Toggle direction of next activation of repeatable stairs
515 		ln->args[3] ^= 1;
516 
517 	return res;
518 }
519 
FUNC(LS_Pillar_Build)520 FUNC(LS_Pillar_Build)
521 // Pillar_Build (tag, speed, height)
522 {
523 	return EV_DoPillar (DPillar::pillarBuild, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, -1, false);
524 }
525 
FUNC(LS_Pillar_BuildAndCrush)526 FUNC(LS_Pillar_BuildAndCrush)
527 // Pillar_BuildAndCrush (tag, speed, height, crush, crushtype)
528 {
529 	return EV_DoPillar (DPillar::pillarBuild, ln, arg0, SPEED(arg1), arg2*FRACUNIT, 0, arg3, CRUSHTYPE(arg4));
530 }
531 
FUNC(LS_Pillar_Open)532 FUNC(LS_Pillar_Open)
533 // Pillar_Open (tag, speed, f_height, c_height)
534 {
535 	return EV_DoPillar (DPillar::pillarOpen, ln, arg0, SPEED(arg1), arg2*FRACUNIT, arg3*FRACUNIT, -1, false);
536 }
537 
FUNC(LS_Ceiling_LowerByValue)538 FUNC(LS_Ceiling_LowerByValue)
539 // Ceiling_LowerByValue (tag, speed, height)
540 {
541 	return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, -1, 0, 0, false);
542 }
543 
FUNC(LS_Ceiling_RaiseByValue)544 FUNC(LS_Ceiling_RaiseByValue)
545 // Ceiling_RaiseByValue (tag, speed, height)
546 {
547 	return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT, -1, 0, 0, false);
548 }
549 
FUNC(LS_Ceiling_LowerByValueTimes8)550 FUNC(LS_Ceiling_LowerByValueTimes8)
551 // Ceiling_LowerByValueTimes8 (tag, speed, height)
552 {
553 	return EV_DoCeiling (DCeiling::ceilLowerByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, 0, false);
554 }
555 
FUNC(LS_Ceiling_RaiseByValueTimes8)556 FUNC(LS_Ceiling_RaiseByValueTimes8)
557 // Ceiling_RaiseByValueTimes8 (tag, speed, height)
558 {
559 	return EV_DoCeiling (DCeiling::ceilRaiseByValue, ln, arg0, SPEED(arg1), 0, arg2*FRACUNIT*8, -1, 0, 0, false);
560 }
561 
FUNC(LS_Ceiling_CrushAndRaise)562 FUNC(LS_Ceiling_CrushAndRaise)
563 // Ceiling_CrushAndRaise (tag, speed, crush, crushtype)
564 {
565 	return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 0, arg2, 0, 0, CRUSHTYPE(arg3));
566 }
567 
FUNC(LS_Ceiling_LowerAndCrush)568 FUNC(LS_Ceiling_LowerAndCrush)
569 // Ceiling_LowerAndCrush (tag, speed, crush, crushtype)
570 {
571 	return EV_DoCeiling (DCeiling::ceilLowerAndCrush, ln, arg0, SPEED(arg1), SPEED(arg1), 0, arg2, 0, 0, CRUSHTYPE(arg3));
572 }
573 
FUNC(LS_Ceiling_LowerAndCrushDist)574 FUNC(LS_Ceiling_LowerAndCrushDist)
575 // Ceiling_LowerAndCrush (tag, speed, crush, dist, crushtype)
576 {
577 	return EV_DoCeiling (DCeiling::ceilLowerAndCrushDist, ln, arg0, SPEED(arg1), SPEED(arg1), arg3*FRACUNIT, arg2, 0, 0, CRUSHTYPE(arg4));
578 }
579 
FUNC(LS_Ceiling_CrushStop)580 FUNC(LS_Ceiling_CrushStop)
581 // Ceiling_CrushStop (tag)
582 {
583 	return EV_CeilingCrushStop (arg0);
584 }
585 
FUNC(LS_Ceiling_CrushRaiseAndStay)586 FUNC(LS_Ceiling_CrushRaiseAndStay)
587 // Ceiling_CrushRaiseAndStay (tag, speed, crush, crushtype)
588 {
589 	return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg1)/2, 0, arg2, 0, 0, CRUSHTYPE(arg3));
590 }
591 
FUNC(LS_Ceiling_MoveToValueTimes8)592 FUNC(LS_Ceiling_MoveToValueTimes8)
593 // Ceiling_MoveToValueTimes8 (tag, speed, height, negative)
594 {
595 	return EV_DoCeiling (DCeiling::ceilMoveToValue, ln, arg0, SPEED(arg1), 0,
596 						 arg2*FRACUNIT*8*((arg3) ? -1 : 1), -1, 0, 0, false);
597 }
598 
FUNC(LS_Ceiling_MoveToValue)599 FUNC(LS_Ceiling_MoveToValue)
600 // Ceiling_MoveToValue (tag, speed, height, negative)
601 {
602 	return EV_DoCeiling (DCeiling::ceilMoveToValue, ln, arg0, SPEED(arg1), 0,
603 						 arg2*FRACUNIT*((arg3) ? -1 : 1), -1, 0, 0, false);
604 }
605 
FUNC(LS_Ceiling_LowerToHighestFloor)606 FUNC(LS_Ceiling_LowerToHighestFloor)
607 // Ceiling_LowerToHighestFloor (tag, speed)
608 {
609 	return EV_DoCeiling (DCeiling::ceilLowerToHighestFloor, ln, arg0, SPEED(arg1), 0, 0, -1, 0, 0, false);
610 }
611 
FUNC(LS_Ceiling_LowerInstant)612 FUNC(LS_Ceiling_LowerInstant)
613 // Ceiling_LowerInstant (tag, unused, height)
614 {
615 	return EV_DoCeiling (DCeiling::ceilLowerInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, -1, 0, 0, false);
616 }
617 
FUNC(LS_Ceiling_RaiseInstant)618 FUNC(LS_Ceiling_RaiseInstant)
619 // Ceiling_RaiseInstant (tag, unused, height)
620 {
621 	return EV_DoCeiling (DCeiling::ceilRaiseInstant, ln, arg0, 0, 0, arg2*FRACUNIT*8, -1, 0, 0, false);
622 }
623 
FUNC(LS_Ceiling_CrushRaiseAndStayA)624 FUNC(LS_Ceiling_CrushRaiseAndStayA)
625 // Ceiling_CrushRaiseAndStayA (tag, dnspeed, upspeed, damage, crushtype)
626 {
627 	return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4));
628 }
629 
FUNC(LS_Ceiling_CrushRaiseAndStaySilA)630 FUNC(LS_Ceiling_CrushRaiseAndStaySilA)
631 // Ceiling_CrushRaiseAndStaySilA (tag, dnspeed, upspeed, damage, crushtype)
632 {
633 	return EV_DoCeiling (DCeiling::ceilCrushRaiseAndStay, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4));
634 }
635 
FUNC(LS_Ceiling_CrushAndRaiseA)636 FUNC(LS_Ceiling_CrushAndRaiseA)
637 // Ceiling_CrushAndRaiseA (tag, dnspeed, upspeed, damage, crushtype)
638 {
639 	return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 0, 0, CRUSHTYPE(arg4));
640 }
641 
FUNC(LS_Ceiling_CrushAndRaiseDist)642 FUNC(LS_Ceiling_CrushAndRaiseDist)
643 // Ceiling_CrushAndRaiseDist (tag, dist, speed, damage, crushtype)
644 {
645 	return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 0, 0, CRUSHTYPE(arg4));
646 }
647 
FUNC(LS_Ceiling_CrushAndRaiseSilentA)648 FUNC(LS_Ceiling_CrushAndRaiseSilentA)
649 // Ceiling_CrushAndRaiseSilentA (tag, dnspeed, upspeed, damage, crushtype)
650 {
651 	return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1), SPEED(arg2), 0, arg3, 1, 0, CRUSHTYPE(arg4));
652 }
653 
FUNC(LS_Ceiling_CrushAndRaiseSilentDist)654 FUNC(LS_Ceiling_CrushAndRaiseSilentDist)
655 // Ceiling_CrushAndRaiseSilentDist (tag, dist, upspeed, damage, crushtype)
656 {
657 	return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg2), SPEED(arg2), arg1*FRACUNIT, arg3, 1, 0, CRUSHTYPE(arg4));
658 }
659 
FUNC(LS_Ceiling_RaiseToNearest)660 FUNC(LS_Ceiling_RaiseToNearest)
661 // Ceiling_RaiseToNearest (tag, speed)
662 {
663 	return EV_DoCeiling (DCeiling::ceilRaiseToNearest, ln, arg0, SPEED(arg1), 0, 0, -1, 0, 0, false);
664 }
665 
FUNC(LS_Ceiling_LowerToLowest)666 FUNC(LS_Ceiling_LowerToLowest)
667 // Ceiling_LowerToLowest (tag, speed)
668 {
669 	return EV_DoCeiling (DCeiling::ceilLowerToLowest, ln, arg0, SPEED(arg1), 0, 0, -1, 0, 0, false);
670 }
671 
FUNC(LS_Ceiling_LowerToFloor)672 FUNC(LS_Ceiling_LowerToFloor)
673 // Ceiling_LowerToFloor (tag, speed)
674 {
675 	return EV_DoCeiling (DCeiling::ceilLowerToFloor, ln, arg0, SPEED(arg1), 0, 0, -1, 0, 0, false);
676 }
677 
FUNC(LS_Generic_Ceiling)678 FUNC(LS_Generic_Ceiling)
679 // Generic_Ceiling (tag, speed, height, target, change/model/direct/crush)
680 {
681 	DCeiling::ECeiling type;
682 
683 	if (arg4 & 8) {
684 		switch (arg3) {
685 			case 1:  type = DCeiling::ceilRaiseToHighest;		break;
686 			case 2:  type = DCeiling::ceilRaiseToLowest;		break;
687 			case 3:  type = DCeiling::ceilRaiseToNearest;		break;
688 			case 4:  type = DCeiling::ceilRaiseToHighestFloor;	break;
689 			case 5:  type = DCeiling::ceilRaiseToFloor;			break;
690 			case 6:  type = DCeiling::ceilRaiseByTexture;		break;
691 			default: type = DCeiling::ceilRaiseByValue;			break;
692 		}
693 	} else {
694 		switch (arg3) {
695 			case 1:  type = DCeiling::ceilLowerToHighest;		break;
696 			case 2:  type = DCeiling::ceilLowerToLowest;		break;
697 			case 3:  type = DCeiling::ceilLowerToNearest;		break;
698 			case 4:  type = DCeiling::ceilLowerToHighestFloor;	break;
699 			case 5:  type = DCeiling::ceilLowerToFloor;			break;
700 			case 6:  type = DCeiling::ceilLowerByTexture;		break;
701 			default: type = DCeiling::ceilLowerByValue;			break;
702 		}
703 	}
704 
705 	return EV_DoCeiling (type, ln, arg0, SPEED(arg1), SPEED(arg1), arg2*FRACUNIT,
706 						 (arg4 & 16) ? 20 : -1, 0, arg4 & 7, false);
707 }
708 
FUNC(LS_Generic_Crusher)709 FUNC(LS_Generic_Crusher)
710 // Generic_Crusher (tag, dnspeed, upspeed, silent, damage)
711 {
712 	return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1),
713 						 SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, false);
714 }
715 
FUNC(LS_Generic_Crusher2)716 FUNC(LS_Generic_Crusher2)
717 // Generic_Crusher2 (tag, dnspeed, upspeed, silent, damage)
718 {
719 	// same as above but uses Hexen's crushing method.
720 	return EV_DoCeiling (DCeiling::ceilCrushAndRaise, ln, arg0, SPEED(arg1),
721 						 SPEED(arg2), 0, arg4, arg3 ? 2 : 0, 0, true);
722 }
723 
FUNC(LS_Plat_PerpetualRaise)724 FUNC(LS_Plat_PerpetualRaise)
725 // Plat_PerpetualRaise (tag, speed, delay)
726 {
727 	return EV_DoPlat (arg0, ln, DPlat::platPerpetualRaise, 0, SPEED(arg1), TICS(arg2), 8, 0);
728 }
729 
FUNC(LS_Plat_PerpetualRaiseLip)730 FUNC(LS_Plat_PerpetualRaiseLip)
731 // Plat_PerpetualRaiseLip (tag, speed, delay, lip)
732 {
733 	return EV_DoPlat (arg0, ln, DPlat::platPerpetualRaise, 0, SPEED(arg1), TICS(arg2), arg3, 0);
734 }
735 
FUNC(LS_Plat_Stop)736 FUNC(LS_Plat_Stop)
737 // Plat_Stop (tag)
738 {
739 	EV_StopPlat (arg0);
740 	return true;
741 }
742 
FUNC(LS_Plat_DownWaitUpStay)743 FUNC(LS_Plat_DownWaitUpStay)
744 // Plat_DownWaitUpStay (tag, speed, delay)
745 {
746 	return EV_DoPlat (arg0, ln, DPlat::platDownWaitUpStay, 0, SPEED(arg1), TICS(arg2), 8, 0);
747 }
748 
FUNC(LS_Plat_DownWaitUpStayLip)749 FUNC(LS_Plat_DownWaitUpStayLip)
750 // Plat_DownWaitUpStayLip (tag, speed, delay, lip, floor-sound?)
751 {
752 	return EV_DoPlat (arg0, ln,
753 		arg4 ? DPlat::platDownWaitUpStayStone : DPlat::platDownWaitUpStay,
754 		0, SPEED(arg1), TICS(arg2), arg3, 0);
755 }
756 
FUNC(LS_Plat_DownByValue)757 FUNC(LS_Plat_DownByValue)
758 // Plat_DownByValue (tag, speed, delay, height)
759 {
760 	return EV_DoPlat (arg0, ln, DPlat::platDownByValue, FRACUNIT*arg3*8, SPEED(arg1), TICS(arg2), 0, 0);
761 }
762 
FUNC(LS_Plat_UpByValue)763 FUNC(LS_Plat_UpByValue)
764 // Plat_UpByValue (tag, speed, delay, height)
765 {
766 	return EV_DoPlat (arg0, ln, DPlat::platUpByValue, FRACUNIT*arg3*8, SPEED(arg1), TICS(arg2), 0, 0);
767 }
768 
FUNC(LS_Plat_UpWaitDownStay)769 FUNC(LS_Plat_UpWaitDownStay)
770 // Plat_UpWaitDownStay (tag, speed, delay)
771 {
772 	return EV_DoPlat (arg0, ln, DPlat::platUpWaitDownStay, 0, SPEED(arg1), TICS(arg2), 0, 0);
773 }
774 
FUNC(LS_Plat_UpNearestWaitDownStay)775 FUNC(LS_Plat_UpNearestWaitDownStay)
776 // Plat_UpNearestWaitDownStay (tag, speed, delay)
777 {
778 	return EV_DoPlat (arg0, ln, DPlat::platUpNearestWaitDownStay, 0, SPEED(arg1), TICS(arg2), 0, 0);
779 }
780 
FUNC(LS_Plat_RaiseAndStayTx0)781 FUNC(LS_Plat_RaiseAndStayTx0)
782 // Plat_RaiseAndStayTx0 (tag, speed, lockout)
783 {
784 	DPlat::EPlatType type;
785 
786 	switch (arg3)
787 	{
788 		case 1:
789 			type = DPlat::platRaiseAndStay;
790 			break;
791 		case 2:
792 			type = DPlat::platRaiseAndStayLockout;
793 			break;
794 		default:
795 			type = gameinfo.gametype == GAME_Heretic? DPlat::platRaiseAndStayLockout : DPlat::platRaiseAndStay;
796 			break;
797 	}
798 
799 
800 	return EV_DoPlat (arg0, ln, type, 0, SPEED(arg1), 0, 0, 1);
801 }
802 
FUNC(LS_Plat_UpByValueStayTx)803 FUNC(LS_Plat_UpByValueStayTx)
804 // Plat_UpByValueStayTx (tag, speed, height)
805 {
806 	return EV_DoPlat (arg0, ln, DPlat::platUpByValueStay, FRACUNIT*arg2*8, SPEED(arg1), 0, 0, 2);
807 }
808 
FUNC(LS_Plat_ToggleCeiling)809 FUNC(LS_Plat_ToggleCeiling)
810 // Plat_ToggleCeiling (tag)
811 {
812 	return EV_DoPlat (arg0, ln, DPlat::platToggle, 0, 0, 0, 0, 0);
813 }
814 
FUNC(LS_Generic_Lift)815 FUNC(LS_Generic_Lift)
816 // Generic_Lift (tag, speed, delay, target, height)
817 {
818 	DPlat::EPlatType type;
819 
820 	switch (arg3)
821 	{
822 		case 1:
823 			type = DPlat::platDownWaitUpStay;
824 			break;
825 		case 2:
826 			type = DPlat::platDownToNearestFloor;
827 			break;
828 		case 3:
829 			type = DPlat::platDownToLowestCeiling;
830 			break;
831 		case 4:
832 			type = DPlat::platPerpetualRaise;
833 			break;
834 		default:
835 			type = DPlat::platUpByValue;
836 			break;
837 	}
838 
839 	return EV_DoPlat (arg0, ln, type, arg4*8*FRACUNIT, SPEED(arg1), OCTICS(arg2), 0, 0);
840 }
841 
FUNC(LS_Exit_Normal)842 FUNC(LS_Exit_Normal)
843 // Exit_Normal (position)
844 {
845 	if (CheckIfExitIsGood (it, FindLevelInfo(G_GetExitMap())))
846 	{
847 		G_ExitLevel (arg0, false);
848 		return true;
849 	}
850 	return false;
851 }
852 
FUNC(LS_Exit_Secret)853 FUNC(LS_Exit_Secret)
854 // Exit_Secret (position)
855 {
856 	if (CheckIfExitIsGood (it, FindLevelInfo(G_GetSecretExitMap())))
857 	{
858 		G_SecretExitLevel (arg0);
859 		return true;
860 	}
861 	return false;
862 }
863 
FUNC(LS_Teleport_NewMap)864 FUNC(LS_Teleport_NewMap)
865 // Teleport_NewMap (map, position, keepFacing?)
866 {
867 	if (backSide == 0 || gameinfo.gametype == GAME_Strife)
868 	{
869 		level_info_t *info = FindLevelByNum (arg0);
870 
871 		if (info && CheckIfExitIsGood (it, info))
872 		{
873 			G_ChangeLevel(info->MapName, arg1, arg2 ? CHANGELEVEL_KEEPFACING : 0);
874 			return true;
875 		}
876 	}
877 	return false;
878 }
879 
FUNC(LS_Teleport)880 FUNC(LS_Teleport)
881 // Teleport (tid, sectortag, bNoSourceFog)
882 {
883 	int flags = TELF_DESTFOG;
884 	if (!arg2)
885 	{
886 		flags |= TELF_SOURCEFOG;
887 	}
888 	return EV_Teleport (arg0, arg1, ln, backSide, it, flags);
889 }
890 
FUNC(LS_Teleport_NoStop)891 FUNC( LS_Teleport_NoStop )
892 // Teleport_NoStop (tid, sectortag, bNoSourceFog)
893 {
894 	int flags = TELF_DESTFOG | TELF_KEEPVELOCITY;
895 	if (!arg2)
896 	{
897 		flags |= TELF_SOURCEFOG;
898 	}
899 	return EV_Teleport( arg0, arg1, ln, backSide, it, flags);
900 }
901 
FUNC(LS_Teleport_NoFog)902 FUNC(LS_Teleport_NoFog)
903 // Teleport_NoFog (tid, useang, sectortag, keepheight)
904 {
905 	int flags = 0;
906 	if (!arg1)
907 	{
908 		flags |= TELF_KEEPORIENTATION;
909 	}
910 	if (arg3)
911 	{
912 		flags |= TELF_KEEPHEIGHT;
913 	}
914 	return EV_Teleport (arg0, arg2, ln, backSide, it, flags);
915 }
916 
FUNC(LS_Teleport_ZombieChanger)917 FUNC(LS_Teleport_ZombieChanger)
918 // Teleport_ZombieChanger (tid, sectortag)
919 {
920 	// This is practically useless outside of Strife, but oh well.
921 	if (it != NULL)
922 	{
923 		EV_Teleport (arg0, arg1, ln, backSide, it, 0);
924 		if (it->health >= 0) it->SetState (it->FindState(NAME_Pain));
925 		return true;
926 	}
927 	return false;
928 }
929 
FUNC(LS_TeleportOther)930 FUNC(LS_TeleportOther)
931 // TeleportOther (other_tid, dest_tid, fog?)
932 {
933 	return EV_TeleportOther (arg0, arg1, arg2?true:false);
934 }
935 
FUNC(LS_TeleportGroup)936 FUNC(LS_TeleportGroup)
937 // TeleportGroup (group_tid, source_tid, dest_tid, move_source?, fog?)
938 {
939 	return EV_TeleportGroup (arg0, it, arg1, arg2, arg3?true:false, arg4?true:false);
940 }
941 
FUNC(LS_TeleportInSector)942 FUNC(LS_TeleportInSector)
943 // TeleportInSector (tag, source_tid, dest_tid, bFog, group_tid)
944 {
945 	return EV_TeleportSector (arg0, arg1, arg2, arg3?true:false, arg4);
946 }
947 
FUNC(LS_Teleport_EndGame)948 FUNC(LS_Teleport_EndGame)
949 // Teleport_EndGame ()
950 {
951 	if (!backSide && CheckIfExitIsGood (it, NULL))
952 	{
953 		G_ChangeLevel(NULL, 0, 0);
954 		return true;
955 	}
956 	return false;
957 }
958 
FUNC(LS_Teleport_Line)959 FUNC(LS_Teleport_Line)
960 // Teleport_Line (thisid, destid, reversed)
961 {
962 	return EV_SilentLineTeleport (ln, backSide, it, arg1, arg2);
963 }
964 
965 static void ThrustThingHelper (AActor *it, angle_t angle, int force, INTBOOL nolimit);
FUNC(LS_ThrustThing)966 FUNC(LS_ThrustThing)
967 // ThrustThing (angle, force, nolimit, tid)
968 {
969 	if (arg3 != 0)
970 	{
971 		FActorIterator iterator (arg3);
972 		while ((it = iterator.Next()) != NULL)
973 		{
974 			ThrustThingHelper (it, BYTEANGLE(arg0), arg1, arg2);
975 		}
976 		return true;
977 	}
978 	else if (it)
979 	{
980 		ThrustThingHelper (it, BYTEANGLE(arg0), arg1, arg2);
981 		return true;
982 	}
983 	return false;
984 }
985 
ThrustThingHelper(AActor * it,angle_t angle,int force,INTBOOL nolimit)986 static void ThrustThingHelper (AActor *it, angle_t angle, int force, INTBOOL nolimit)
987 {
988 	angle >>= ANGLETOFINESHIFT;
989 	it->velx += force * finecosine[angle];
990 	it->vely += force * finesine[angle];
991 	if (!nolimit)
992 	{
993 		it->velx = clamp<fixed_t> (it->velx, -MAXMOVE, MAXMOVE);
994 		it->vely = clamp<fixed_t> (it->vely, -MAXMOVE, MAXMOVE);
995 	}
996 }
997 
FUNC(LS_ThrustThingZ)998 FUNC(LS_ThrustThingZ)	// [BC]
999 // ThrustThingZ (tid, zthrust, down/up, set)
1000 {
1001 	AActor *victim;
1002 	fixed_t thrust = arg1*FRACUNIT/4;
1003 
1004 	// [BC] Up is default
1005 	if (arg2)
1006 		thrust = -thrust;
1007 
1008 	if (arg0 != 0)
1009 	{
1010 		FActorIterator iterator (arg0);
1011 
1012 		while ( (victim = iterator.Next ()) )
1013 		{
1014 			if (!arg3)
1015 				victim->velz = thrust;
1016 			else
1017 				victim->velz += thrust;
1018 		}
1019 		return true;
1020 	}
1021 	else if (it)
1022 	{
1023 		if (!arg3)
1024 			it->velz = thrust;
1025 		else
1026 			it->velz += thrust;
1027 		return true;
1028 	}
1029 	return false;
1030 }
1031 
FUNC(LS_Thing_SetSpecial)1032 FUNC(LS_Thing_SetSpecial)	// [BC]
1033 // Thing_SetSpecial (tid, special, arg1, arg2, arg3)
1034 // [RH] Use the SetThingSpecial ACS command instead.
1035 // It can set all args and not just the first three.
1036 {
1037 	if (arg0 == 0)
1038 	{
1039 		if (it != NULL)
1040 		{
1041 			it->special = arg1;
1042 			it->args[0] = arg2;
1043 			it->args[1] = arg3;
1044 			it->args[2] = arg4;
1045 		}
1046 	}
1047 	else
1048 	{
1049 		AActor *actor;
1050 		FActorIterator iterator (arg0);
1051 
1052 		while ( (actor = iterator.Next ()) )
1053 		{
1054 			actor->special = arg1;
1055 			actor->args[0] = arg2;
1056 			actor->args[1] = arg3;
1057 			actor->args[2] = arg4;
1058 		}
1059 	}
1060 	return true;
1061 }
1062 
FUNC(LS_Thing_ChangeTID)1063 FUNC(LS_Thing_ChangeTID)
1064 // Thing_ChangeTID (oldtid, newtid)
1065 {
1066 	if (arg0 == 0)
1067 	{
1068 		if (it != NULL && !(it->ObjectFlags & OF_EuthanizeMe))
1069 		{
1070 			it->RemoveFromHash ();
1071 			it->tid = arg1;
1072 			it->AddToHash ();
1073 		}
1074 	}
1075 	else
1076 	{
1077 		FActorIterator iterator (arg0);
1078 		AActor *actor, *next;
1079 
1080 		next = iterator.Next ();
1081 		while (next != NULL)
1082 		{
1083 			actor = next;
1084 			next = iterator.Next ();
1085 
1086 			if (!(actor->ObjectFlags & OF_EuthanizeMe))
1087 			{
1088 				actor->RemoveFromHash ();
1089 				actor->tid = arg1;
1090 				actor->AddToHash ();
1091 			}
1092 		}
1093 	}
1094 	return true;
1095 }
1096 
FUNC(LS_DamageThing)1097 FUNC(LS_DamageThing)
1098 // DamageThing (damage, mod)
1099 {
1100 	if (it)
1101 	{
1102 		if (arg0 < 0)
1103 		{ // Negative damages mean healing
1104 			if (it->player)
1105 			{
1106 				P_GiveBody (it, -arg0);
1107 			}
1108 			else
1109 			{
1110 				it->health -= arg0;
1111 				if (it->SpawnHealth() < it->health)
1112 					it->health = it->SpawnHealth();
1113 			}
1114 		}
1115 		else if (arg0 > 0)
1116 		{
1117 			P_DamageMobj (it, NULL, NULL, arg0, MODtoDamageType (arg1));
1118 		}
1119 		else
1120 		{ // If zero damage, guarantee a kill
1121 			P_DamageMobj (it, NULL, NULL, TELEFRAG_DAMAGE, MODtoDamageType (arg1));
1122 		}
1123 	}
1124 
1125 	return it ? true : false;
1126 }
1127 
FUNC(LS_HealThing)1128 FUNC(LS_HealThing)
1129 // HealThing (amount, max)
1130 {
1131 	if (it)
1132 	{
1133 		int max = arg1;
1134 
1135 		if (max == 0 || it->player == NULL)
1136 		{
1137 			P_GiveBody(it, arg0);
1138 			return true;
1139 		}
1140 		else if (max == 1)
1141 		{
1142 			max = deh.MaxSoulsphere;
1143 		}
1144 
1145 		// If health is already above max, do nothing
1146 		if (it->health < max)
1147 		{
1148 			it->health += arg0;
1149 			if (it->health > max && max > 0)
1150 			{
1151 				it->health = max;
1152 			}
1153 			if (it->player)
1154 			{
1155 				it->player->health = it->health;
1156 			}
1157 		}
1158 	}
1159 
1160 	return it ? true : false;
1161 }
1162 
1163 // So that things activated/deactivated by ACS or DECORATE *and* by
1164 // the BUMPSPECIAL or USESPECIAL flags work correctly both ways.
DoActivateThing(AActor * thing,AActor * activator)1165 void DoActivateThing(AActor * thing, AActor * activator)
1166 {
1167 	if (thing->activationtype & THINGSPEC_Activate)
1168 	{
1169 		thing->activationtype &= ~THINGSPEC_Activate; // Clear flag
1170 		if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching
1171 			thing->activationtype |= THINGSPEC_Deactivate;
1172 	}
1173 	thing->Activate (activator);
1174 }
1175 
DoDeactivateThing(AActor * thing,AActor * activator)1176 void DoDeactivateThing(AActor * thing, AActor * activator)
1177 {
1178 	if (thing->activationtype & THINGSPEC_Deactivate)
1179 	{
1180 		thing->activationtype &= ~THINGSPEC_Deactivate; // Clear flag
1181 		if (thing->activationtype & THINGSPEC_Switch) // Set other flag if switching
1182 			thing->activationtype |= THINGSPEC_Activate;
1183 	}
1184 	thing->Deactivate (activator);
1185 }
1186 
FUNC(LS_Thing_Activate)1187 FUNC(LS_Thing_Activate)
1188 // Thing_Activate (tid)
1189 {
1190 	if (arg0 != 0)
1191 	{
1192 		AActor *actor;
1193 		FActorIterator iterator (arg0);
1194 		int count = 0;
1195 
1196 		actor = iterator.Next ();
1197 		while (actor)
1198 		{
1199 			// Actor might remove itself as part of activation, so get next
1200 			// one before activating it.
1201 			AActor *temp = iterator.Next ();
1202 			DoActivateThing(actor, it);
1203 			actor = temp;
1204 			count++;
1205 		}
1206 
1207 		return count != 0;
1208 	}
1209 	else if (it != NULL)
1210 	{
1211 		DoActivateThing(it, it);
1212 		return true;
1213 	}
1214 	return false;
1215 }
1216 
FUNC(LS_Thing_Deactivate)1217 FUNC(LS_Thing_Deactivate)
1218 // Thing_Deactivate (tid)
1219 {
1220 	if (arg0 != 0)
1221 	{
1222 		AActor *actor;
1223 		FActorIterator iterator (arg0);
1224 		int count = 0;
1225 
1226 		actor = iterator.Next ();
1227 		while (actor)
1228 		{
1229 			// Actor might removes itself as part of deactivation, so get next
1230 			// one before we activate it.
1231 			AActor *temp = iterator.Next ();
1232 			DoDeactivateThing(actor, it);
1233 			actor = temp;
1234 			count++;
1235 		}
1236 
1237 		return count != 0;
1238 	}
1239 	else if (it != NULL)
1240 	{
1241 		DoDeactivateThing(it, it);
1242 		return true;
1243 	}
1244 	return false;
1245 }
1246 
FUNC(LS_Thing_Remove)1247 FUNC(LS_Thing_Remove)
1248 // Thing_Remove (tid)
1249 {
1250 	if (arg0 != 0)
1251 	{
1252 		FActorIterator iterator (arg0);
1253 		AActor *actor;
1254 
1255 		actor = iterator.Next ();
1256 		while (actor)
1257 		{
1258 			AActor *temp = iterator.Next ();
1259 
1260 			P_RemoveThing(actor);
1261 			actor = temp;
1262 		}
1263 	}
1264 	else if (it != NULL)
1265 	{
1266 		P_RemoveThing(it);
1267 	}
1268 
1269 	return true;
1270 }
1271 
FUNC(LS_Thing_Destroy)1272 FUNC(LS_Thing_Destroy)
1273 // Thing_Destroy (tid, extreme, tag)
1274 {
1275 	AActor *actor;
1276 
1277 	if (arg0 == 0 && arg2 == 0)
1278 	{
1279 		P_Massacre ();
1280 	}
1281 	else if (arg0 == 0)
1282 	{
1283 		TThinkerIterator<AActor> iterator;
1284 
1285 		actor = iterator.Next ();
1286 		while (actor)
1287 		{
1288 			AActor *temp = iterator.Next ();
1289 			if (actor->flags & MF_SHOOTABLE && tagManager.SectorHasTag(actor->Sector, arg2))
1290 				P_DamageMobj (actor, NULL, it, arg1 ? TELEFRAG_DAMAGE : actor->health, NAME_None);
1291 			actor = temp;
1292 		}
1293 	}
1294 	else
1295 	{
1296 		FActorIterator iterator (arg0);
1297 
1298 		actor = iterator.Next ();
1299 		while (actor)
1300 		{
1301 			AActor *temp = iterator.Next ();
1302 			if (actor->flags & MF_SHOOTABLE && (arg2 == 0 || tagManager.SectorHasTag(actor->Sector, arg2)))
1303 				P_DamageMobj (actor, NULL, it, arg1 ? TELEFRAG_DAMAGE : actor->health, NAME_None);
1304 			actor = temp;
1305 		}
1306 	}
1307 	return true;
1308 }
1309 
FUNC(LS_Thing_Damage)1310 FUNC(LS_Thing_Damage)
1311 // Thing_Damage (tid, amount, MOD)
1312 {
1313 	P_Thing_Damage (arg0, it, arg1, MODtoDamageType (arg2));
1314 	return true;
1315 }
1316 
FUNC(LS_Thing_Projectile)1317 FUNC(LS_Thing_Projectile)
1318 // Thing_Projectile (tid, type, angle, speed, vspeed)
1319 {
1320 	return P_Thing_Projectile (arg0, it, arg1, NULL, BYTEANGLE(arg2), arg3<<(FRACBITS-3),
1321 		arg4<<(FRACBITS-3), 0, NULL, 0, 0, false);
1322 }
1323 
FUNC(LS_Thing_ProjectileGravity)1324 FUNC(LS_Thing_ProjectileGravity)
1325 // Thing_ProjectileGravity (tid, type, angle, speed, vspeed)
1326 {
1327 	return P_Thing_Projectile (arg0, it, arg1, NULL, BYTEANGLE(arg2), arg3<<(FRACBITS-3),
1328 		arg4<<(FRACBITS-3), 0, NULL, 1, 0, false);
1329 }
1330 
FUNC(LS_Thing_Hate)1331 FUNC(LS_Thing_Hate)
1332 // Thing_Hate (hater, hatee, group/"xray"?)
1333 {
1334 	FActorIterator haterIt (arg0);
1335 	AActor *hater, *hatee = NULL;
1336 	FActorIterator hateeIt (arg1);
1337 	bool nothingToHate = false;
1338 
1339 	if (arg1 != 0)
1340 	{
1341 		while ((hatee = hateeIt.Next ()))
1342 		{
1343 			if (hatee->flags & MF_SHOOTABLE	&&	// can't hate nonshootable things
1344 				hatee->health > 0 &&			// can't hate dead things
1345 				!(hatee->flags2 & MF2_DORMANT))	// can't target dormant things
1346 			{
1347 				break;
1348 			}
1349 		}
1350 		if (hatee == NULL)
1351 		{ // Nothing to hate
1352 			nothingToHate = true;
1353 		}
1354 	}
1355 
1356 	if (arg0 == 0)
1357 	{
1358 		if (it != NULL && it->player != NULL)
1359 		{
1360 			// Players cannot have their attitudes set
1361 			return false;
1362 		}
1363 		else
1364 		{
1365 			hater = it;
1366 		}
1367 	}
1368 	else
1369 	{
1370 		while ((hater = haterIt.Next ()))
1371 		{
1372 			if (hater->health > 0 && hater->flags & MF_SHOOTABLE)
1373 			{
1374 				break;
1375 			}
1376 		}
1377 	}
1378 	while (hater != NULL)
1379 	{
1380 		// Can't hate if can't attack.
1381 		if (hater->SeeState != NULL)
1382 		{
1383 			// If hating a group of things, record the TID and NULL
1384 			// the target (if its TID doesn't match). A_Look will
1385 			// find an appropriate thing to go chase after.
1386 			if (arg2 != 0)
1387 			{
1388 				hater->TIDtoHate = arg1;
1389 				hater->LastLookActor = NULL;
1390 
1391 				// If the TID to hate is 0, then don't forget the target and
1392 				// lastenemy fields.
1393 				if (arg1 != 0)
1394 				{
1395 					if (hater->target != NULL && hater->target->tid != arg1)
1396 					{
1397 						hater->target = NULL;
1398 					}
1399 					if (hater->lastenemy != NULL && hater->lastenemy->tid != arg1)
1400 					{
1401 						hater->lastenemy = NULL;
1402 					}
1403 				}
1404 			}
1405 			// Hate types for arg2:
1406 			//
1407 			// 0 - Just hate one specific actor
1408 			// 1 - Hate actors with given TID and attack players when shot
1409 			// 2 - Same as 1, but will go after enemies without seeing them first
1410 			// 3 - Hunt actors with given TID and also players
1411 			// 4 - Same as 3, but will go after monsters without seeing them first
1412 			// 5 - Hate actors with given TID and ignore player attacks
1413 			// 6 - Same as 5, but will go after enemies without seeing them first
1414 
1415 			// Note here: If you use Thing_Hate (tid, 0, 2), you can make
1416 			// a monster go after a player without seeing him first.
1417 			if (arg2 == 2 || arg2 == 4 || arg2 == 6)
1418 			{
1419 				hater->flags3 |= MF3_NOSIGHTCHECK;
1420 			}
1421 			else
1422 			{
1423 				hater->flags3 &= ~MF3_NOSIGHTCHECK;
1424 			}
1425 			if (arg2 == 3 || arg2 == 4)
1426 			{
1427 				hater->flags3 |= MF3_HUNTPLAYERS;
1428 			}
1429 			else
1430 			{
1431 				hater->flags3 &= ~MF3_HUNTPLAYERS;
1432 			}
1433 			if (arg2 == 5 || arg2 == 6)
1434 			{
1435 				hater->flags4 |= MF4_NOHATEPLAYERS;
1436 			}
1437 			else
1438 			{
1439 				hater->flags4 &= ~MF4_NOHATEPLAYERS;
1440 			}
1441 
1442 			if (arg1 == 0)
1443 			{
1444 				hatee = it;
1445 			}
1446 			else if (nothingToHate)
1447 			{
1448 				hatee = NULL;
1449 			}
1450 			else if (arg2 != 0)
1451 			{
1452 				do
1453 				{
1454 					hatee = hateeIt.Next ();
1455 				}
1456 				while ( hatee == NULL ||
1457 						hatee == hater ||					// can't hate self
1458 						!(hatee->flags & MF_SHOOTABLE) ||	// can't hate nonshootable things
1459 						hatee->health <= 0 ||				// can't hate dead things
1460 						(hatee->flags2 & MF2_DORMANT));
1461 			}
1462 
1463 			if (hatee != NULL && hatee != hater && (arg2 == 0 || (hater->goal != NULL && hater->target != hater->goal)))
1464 			{
1465 				if (hater->target)
1466 				{
1467 					hater->lastenemy = hater->target;
1468 				}
1469 				hater->target = hatee;
1470 				if (!(hater->flags2 & MF2_DORMANT))
1471 				{
1472 					if (hater->health > 0) hater->SetState (hater->SeeState);
1473 				}
1474 			}
1475 		}
1476 		if (arg0 != 0)
1477 		{
1478 			while ((hater = haterIt.Next ()))
1479 			{
1480 				if (hater->health > 0 && hater->flags & MF_SHOOTABLE)
1481 				{
1482 					break;
1483 				}
1484 			}
1485 		}
1486 		else
1487 		{
1488 			hater = NULL;
1489 		}
1490 	}
1491 	return true;
1492 }
1493 
FUNC(LS_Thing_ProjectileAimed)1494 FUNC(LS_Thing_ProjectileAimed)
1495 // Thing_ProjectileAimed (tid, type, speed, target, newtid)
1496 {
1497 	return P_Thing_Projectile (arg0, it, arg1, NULL, 0, arg2<<(FRACBITS-3), 0, arg3, it, 0, arg4, false);
1498 }
1499 
FUNC(LS_Thing_ProjectileIntercept)1500 FUNC(LS_Thing_ProjectileIntercept)
1501 // Thing_ProjectileIntercept (tid, type, speed, target, newtid)
1502 {
1503 	return P_Thing_Projectile (arg0, it, arg1, NULL, 0, arg2<<(FRACBITS-3), 0, arg3, it, 0, arg4, true);
1504 }
1505 
1506 // [BC] added newtid for next two
FUNC(LS_Thing_Spawn)1507 FUNC(LS_Thing_Spawn)
1508 // Thing_Spawn (tid, type, angle, newtid)
1509 {
1510 	return P_Thing_Spawn (arg0, it, arg1, BYTEANGLE(arg2), true, arg3);
1511 }
1512 
FUNC(LS_Thing_SpawnNoFog)1513 FUNC(LS_Thing_SpawnNoFog)
1514 // Thing_SpawnNoFog (tid, type, angle, newtid)
1515 {
1516 	return P_Thing_Spawn (arg0, it, arg1, BYTEANGLE(arg2), false, arg3);
1517 }
1518 
FUNC(LS_Thing_SpawnFacing)1519 FUNC(LS_Thing_SpawnFacing)
1520 // Thing_SpawnFacing (tid, type, nofog, newtid)
1521 {
1522 	return P_Thing_Spawn (arg0, it, arg1, ANGLE_MAX, arg2 ? false : true, arg3);
1523 }
1524 
FUNC(LS_Thing_Raise)1525 FUNC(LS_Thing_Raise)
1526 // Thing_Raise(tid)
1527 {
1528 	AActor * target;
1529 	bool ok = false;
1530 
1531 	if (arg0==0)
1532 	{
1533 		ok = P_Thing_Raise (it,NULL);
1534 	}
1535 	else
1536 	{
1537 		TActorIterator<AActor> iterator (arg0);
1538 
1539 		while ( (target = iterator.Next ()) )
1540 		{
1541 			ok |= P_Thing_Raise(target,NULL);
1542 		}
1543 	}
1544 	return ok;
1545 }
1546 
FUNC(LS_Thing_Stop)1547 FUNC(LS_Thing_Stop)
1548 // Thing_Stop(tid)
1549 {
1550 	AActor * target;
1551 	bool ok = false;
1552 
1553 	if (arg0==0)
1554 	{
1555 		if (it != NULL)
1556 		{
1557 			it->velx = it->vely = it->velz = 0;
1558 			if (it->player != NULL) it->player->velx = it->player->vely = 0;
1559 			ok = true;
1560 		}
1561 	}
1562 	else
1563 	{
1564 		TActorIterator<AActor> iterator (arg0);
1565 
1566 		while ( (target = iterator.Next ()) )
1567 		{
1568 			target->velx = target->vely = target->velz = 0;
1569 			if (target->player != NULL) target->player->velx = target->player->vely = 0;
1570 			ok = true;
1571 		}
1572 	}
1573 	return ok;
1574 }
1575 
1576 
FUNC(LS_Thing_SetGoal)1577 FUNC(LS_Thing_SetGoal)
1578 // Thing_SetGoal (tid, goal, delay, chasegoal)
1579 {
1580 	TActorIterator<AActor> selfiterator (arg0);
1581 	NActorIterator goaliterator (NAME_PatrolPoint, arg1);
1582 	AActor *self;
1583 	AActor *goal = goaliterator.Next ();
1584 	bool ok = false;
1585 
1586 	while ( (self = selfiterator.Next ()) )
1587 	{
1588 		ok = true;
1589 		if (self->flags & MF_SHOOTABLE)
1590 		{
1591 			if (self->target == self->goal)
1592 			{ // Targeting a goal already? -> don't target it anymore.
1593 			  // A_Look will set it to the goal, presuming no real targets
1594 			  // come into view by then.
1595 				self->target = NULL;
1596 			}
1597 			self->goal = goal;
1598 			if (arg3 == 0)
1599 			{
1600 				self->flags5 &= ~MF5_CHASEGOAL;
1601 			}
1602 			else
1603 			{
1604 				self->flags5 |= MF5_CHASEGOAL;
1605 			}
1606 			if (self->target == NULL)
1607 			{
1608 				self->reactiontime = arg2 * TICRATE;
1609 			}
1610 		}
1611 	}
1612 
1613 	return ok;
1614 }
1615 
FUNC(LS_Thing_Move)1616 FUNC(LS_Thing_Move)		// [BC]
1617 // Thing_Move (tid, mapspot, nofog)
1618 {
1619 	return P_Thing_Move (arg0, it, arg1, arg2 ? false : true);
1620 }
1621 
1622 enum
1623 {
1624 	TRANSLATION_ICE = 0x100007
1625 };
1626 
FUNC(LS_Thing_SetTranslation)1627 FUNC(LS_Thing_SetTranslation)
1628 // Thing_SetTranslation (tid, range)
1629 {
1630 	TActorIterator<AActor> iterator (arg0);
1631 	int range;
1632 	AActor *target;
1633 	bool ok = false;
1634 
1635 	if (arg1 == -1 && it != NULL)
1636 	{
1637 		range = it->Translation;
1638 	}
1639 	else if (arg1 >= 1 && arg1 < MAX_ACS_TRANSLATIONS)
1640 	{
1641 		range = TRANSLATION(TRANSLATION_LevelScripted, (arg1-1));
1642 	}
1643 	else if (arg1 == TRANSLATION_ICE)
1644 	{
1645 		range = TRANSLATION(TRANSLATION_Standard, 7);
1646 	}
1647 	else
1648 	{
1649 		range = 0;
1650 	}
1651 
1652 	if (arg0 == 0)
1653 	{
1654 		if (it != NULL)
1655 		{
1656 			ok = true;
1657 			it->Translation = range==0? it->GetDefault()->Translation : range;
1658 		}
1659 	}
1660 	else
1661 	{
1662 		while ( (target = iterator.Next ()) )
1663 		{
1664 			ok = true;
1665 			target->Translation = range==0? target->GetDefault()->Translation : range;
1666 		}
1667 	}
1668 
1669 	return ok;
1670 }
1671 
FUNC(LS_ACS_Execute)1672 FUNC(LS_ACS_Execute)
1673 // ACS_Execute (script, map, s_arg1, s_arg2, s_arg3)
1674 {
1675 	level_info_t *info;
1676 	const char *mapname = NULL;
1677 	int args[3] = { arg2, arg3, arg4 };
1678 	int flags = (backSide ? ACS_BACKSIDE : 0);
1679 
1680 	if (arg1 == 0)
1681 	{
1682 		mapname = level.MapName;
1683 	}
1684 	else if ((info = FindLevelByNum(arg1)) != NULL)
1685 	{
1686 		mapname = info->MapName;
1687 	}
1688 	else
1689 	{
1690 		return false;
1691 	}
1692 	return P_StartScript(it, ln, arg0, mapname, args, 3, flags);
1693 }
1694 
FUNC(LS_ACS_ExecuteAlways)1695 FUNC(LS_ACS_ExecuteAlways)
1696 // ACS_ExecuteAlways (script, map, s_arg1, s_arg2, s_arg3)
1697 {
1698 	level_info_t *info;
1699 	const char *mapname = NULL;
1700 	int args[3] = { arg2, arg3, arg4 };
1701 	int flags = (backSide ? ACS_BACKSIDE : 0) | ACS_ALWAYS;
1702 
1703 	if (arg1 == 0)
1704 	{
1705 		mapname = level.MapName;
1706 	}
1707 	else if ((info = FindLevelByNum(arg1)) != NULL)
1708 	{
1709 		mapname = info->MapName;
1710 	}
1711 	else
1712 	{
1713 		return false;
1714 	}
1715 	return P_StartScript(it, ln, arg0, mapname, args, 3, flags);
1716 }
1717 
FUNC(LS_ACS_LockedExecute)1718 FUNC(LS_ACS_LockedExecute)
1719 // ACS_LockedExecute (script, map, s_arg1, s_arg2, lock)
1720 {
1721 	if (arg4 && !P_CheckKeys (it, arg4, true))
1722 		return false;
1723 	else
1724 		return LS_ACS_Execute (ln, it, backSide, arg0, arg1, arg2, arg3, 0);
1725 }
1726 
FUNC(LS_ACS_LockedExecuteDoor)1727 FUNC(LS_ACS_LockedExecuteDoor)
1728 // ACS_LockedExecuteDoor (script, map, s_arg1, s_arg2, lock)
1729 {
1730 	if (arg4 && !P_CheckKeys (it, arg4, false))
1731 		return false;
1732 	else
1733 		return LS_ACS_Execute (ln, it, backSide, arg0, arg1, arg2, arg3, 0);
1734 }
1735 
FUNC(LS_ACS_ExecuteWithResult)1736 FUNC(LS_ACS_ExecuteWithResult)
1737 // ACS_ExecuteWithResult (script, s_arg1, s_arg2, s_arg3, s_arg4)
1738 {
1739 	// This is like ACS_ExecuteAlways, except the script is always run on
1740 	// the current map, and the return value is whatever the script sets
1741 	// with SetResultValue.
1742 	int args[4] = { arg1, arg2, arg3, arg4 };
1743 	int flags = (backSide ? ACS_BACKSIDE : 0) | ACS_ALWAYS | ACS_WANTRESULT;
1744 
1745 	return P_StartScript (it, ln, arg0, level.MapName, args, 4, flags);
1746 }
1747 
FUNC(LS_ACS_Suspend)1748 FUNC(LS_ACS_Suspend)
1749 // ACS_Suspend (script, map)
1750 {
1751 	level_info_t *info;
1752 
1753 	if (arg1 == 0)
1754 		P_SuspendScript (arg0, level.MapName);
1755 	else if ((info = FindLevelByNum (arg1)) )
1756 		P_SuspendScript (arg0, info->MapName);
1757 
1758 	return true;
1759 }
1760 
FUNC(LS_ACS_Terminate)1761 FUNC(LS_ACS_Terminate)
1762 // ACS_Terminate (script, map)
1763 {
1764 	level_info_t *info;
1765 
1766 	if (arg1 == 0)
1767 		P_TerminateScript (arg0, level.MapName);
1768 	else if ((info = FindLevelByNum (arg1)) )
1769 		P_TerminateScript (arg0, info->MapName);
1770 
1771 	return true;
1772 }
1773 
FUNC(LS_FloorAndCeiling_LowerByValue)1774 FUNC(LS_FloorAndCeiling_LowerByValue)
1775 // FloorAndCeiling_LowerByValue (tag, speed, height)
1776 {
1777 	return EV_DoElevator (ln, DElevator::elevateLower, SPEED(arg1), arg2*FRACUNIT, arg0);
1778 }
1779 
FUNC(LS_FloorAndCeiling_RaiseByValue)1780 FUNC(LS_FloorAndCeiling_RaiseByValue)
1781 // FloorAndCeiling_RaiseByValue (tag, speed, height)
1782 {
1783 	return EV_DoElevator (ln, DElevator::elevateRaise, SPEED(arg1), arg2*FRACUNIT, arg0);
1784 }
1785 
FUNC(LS_FloorAndCeiling_LowerRaise)1786 FUNC(LS_FloorAndCeiling_LowerRaise)
1787 // FloorAndCeiling_LowerRaise (tag, fspeed, cspeed, boomemu)
1788 {
1789 	bool res = EV_DoCeiling (DCeiling::ceilRaiseToHighest, ln, arg0, SPEED(arg2), 0, 0, 0, 0, 0, false);
1790 	// The switch based Boom equivalents of FloorandCeiling_LowerRaise do incorrect checks
1791 	// which cause the floor only to move when the ceiling fails to do so.
1792 	// To avoid problems with maps that have incorrect args this only uses a
1793 	// more or less unintuitive value for the fourth arg to trigger Boom's broken behavior
1794 	if (arg3 != 1998 || !res)	// (1998 for the year in which Boom was released... :P)
1795 	{
1796 		res |= EV_DoFloor (DFloor::floorLowerToLowest, ln, arg0, SPEED(arg1), 0, -1, 0, false);
1797 	}
1798 	return res;
1799 }
1800 
FUNC(LS_Elevator_MoveToFloor)1801 FUNC(LS_Elevator_MoveToFloor)
1802 // Elevator_MoveToFloor (tag, speed)
1803 {
1804 	return EV_DoElevator (ln, DElevator::elevateCurrent, SPEED(arg1), 0, arg0);
1805 }
1806 
FUNC(LS_Elevator_RaiseToNearest)1807 FUNC(LS_Elevator_RaiseToNearest)
1808 // Elevator_RaiseToNearest (tag, speed)
1809 {
1810 	return EV_DoElevator (ln, DElevator::elevateUp, SPEED(arg1), 0, arg0);
1811 }
1812 
FUNC(LS_Elevator_LowerToNearest)1813 FUNC(LS_Elevator_LowerToNearest)
1814 // Elevator_LowerToNearest (tag, speed)
1815 {
1816 	return EV_DoElevator (ln, DElevator::elevateDown, SPEED(arg1), 0, arg0);
1817 }
1818 
FUNC(LS_Light_ForceLightning)1819 FUNC(LS_Light_ForceLightning)
1820 // Light_ForceLightning (mode)
1821 {
1822 	P_ForceLightning (arg0);
1823 	return true;
1824 }
1825 
FUNC(LS_Light_RaiseByValue)1826 FUNC(LS_Light_RaiseByValue)
1827 // Light_RaiseByValue (tag, value)
1828 {
1829 	EV_LightChange (arg0, arg1);
1830 	return true;
1831 }
1832 
FUNC(LS_Light_LowerByValue)1833 FUNC(LS_Light_LowerByValue)
1834 // Light_LowerByValue (tag, value)
1835 {
1836 	EV_LightChange (arg0, -arg1);
1837 	return true;
1838 }
1839 
FUNC(LS_Light_ChangeToValue)1840 FUNC(LS_Light_ChangeToValue)
1841 // Light_ChangeToValue (tag, value)
1842 {
1843 	EV_LightTurnOn (arg0, arg1);
1844 	return true;
1845 }
1846 
FUNC(LS_Light_Fade)1847 FUNC(LS_Light_Fade)
1848 // Light_Fade (tag, value, tics);
1849 {
1850 	EV_StartLightFading (arg0, arg1, TICS(arg2));
1851 	return true;
1852 }
1853 
FUNC(LS_Light_Glow)1854 FUNC(LS_Light_Glow)
1855 // Light_Glow (tag, upper, lower, tics)
1856 {
1857 	EV_StartLightGlowing (arg0, arg1, arg2, TICS(arg3));
1858 	return true;
1859 }
1860 
FUNC(LS_Light_Flicker)1861 FUNC(LS_Light_Flicker)
1862 // Light_Flicker (tag, upper, lower)
1863 {
1864 	EV_StartLightFlickering (arg0, arg1, arg2);
1865 	return true;
1866 }
1867 
FUNC(LS_Light_Strobe)1868 FUNC(LS_Light_Strobe)
1869 // Light_Strobe (tag, upper, lower, u-tics, l-tics)
1870 {
1871 	EV_StartLightStrobing (arg0, arg1, arg2, TICS(arg3), TICS(arg4));
1872 	return true;
1873 }
1874 
FUNC(LS_Light_StrobeDoom)1875 FUNC(LS_Light_StrobeDoom)
1876 // Light_StrobeDoom (tag, u-tics, l-tics)
1877 {
1878 	EV_StartLightStrobing (arg0, TICS(arg1), TICS(arg2));
1879 	return true;
1880 }
1881 
FUNC(LS_Light_MinNeighbor)1882 FUNC(LS_Light_MinNeighbor)
1883 // Light_MinNeighbor (tag)
1884 {
1885 	EV_TurnTagLightsOff (arg0);
1886 	return true;
1887 }
1888 
FUNC(LS_Light_MaxNeighbor)1889 FUNC(LS_Light_MaxNeighbor)
1890 // Light_MaxNeighbor (tag)
1891 {
1892 	EV_LightTurnOn (arg0, -1);
1893 	return true;
1894 }
1895 
FUNC(LS_Light_Stop)1896 FUNC(LS_Light_Stop)
1897 // Light_Stop (tag)
1898 {
1899 	EV_StopLightEffect (arg0);
1900 	return true;
1901 }
1902 
FUNC(LS_Radius_Quake)1903 FUNC(LS_Radius_Quake)
1904 // Radius_Quake (intensity, duration, damrad, tremrad, tid)
1905 {
1906 	return P_StartQuake (it, arg4, arg0, arg1, arg2*64, arg3*64, "world/quake");
1907 }
1908 
FUNC(LS_UsePuzzleItem)1909 FUNC(LS_UsePuzzleItem)
1910 // UsePuzzleItem (item, script)
1911 {
1912 	AInventory *item;
1913 
1914 	if (!it) return false;
1915 
1916 	// Check player's inventory for puzzle item
1917 	for (item = it->Inventory; item != NULL; item = item->Inventory)
1918 	{
1919 		if (item->IsKindOf (RUNTIME_CLASS(APuzzleItem)))
1920 		{
1921 			if (static_cast<APuzzleItem*>(item)->PuzzleItemNumber == arg0)
1922 			{
1923 				if (it->UseInventory (item))
1924 				{
1925 					return true;
1926 				}
1927 				break;
1928 			}
1929 		}
1930 	}
1931 
1932 	// [RH] Say "hmm" if you don't have the puzzle item
1933 	S_Sound (it, CHAN_VOICE, "*puzzfail", 1, ATTN_IDLE);
1934 	return false;
1935 }
1936 
FUNC(LS_Sector_ChangeSound)1937 FUNC(LS_Sector_ChangeSound)
1938 // Sector_ChangeSound (tag, sound)
1939 {
1940 	int secNum;
1941 	bool rtn;
1942 
1943 	if (!arg0)
1944 		return false;
1945 
1946 	rtn = false;
1947 	FSectorTagIterator itr(arg0);
1948 	while ((secNum = itr.Next()) >= 0)
1949 	{
1950 		sectors[secNum].seqType = arg1;
1951 		rtn = true;
1952 	}
1953 	return rtn;
1954 }
1955 
FUNC(LS_Sector_ChangeFlags)1956 FUNC(LS_Sector_ChangeFlags)
1957 // Sector_ChangeFlags (tag, set, clear)
1958 {
1959 	int secNum;
1960 	bool rtn;
1961 
1962 	if (!arg0)
1963 		return false;
1964 
1965 	rtn = false;
1966 	FSectorTagIterator itr(arg0);
1967 	// exclude protected flags
1968 	arg1 &= ~SECF_NOMODIFY;
1969 	arg2 &= ~SECF_NOMODIFY;
1970 	while ((secNum = itr.Next()) >= 0)
1971 	{
1972 		sectors[secNum].Flags = (sectors[secNum].Flags | arg1) & ~arg2;
1973 		rtn = true;
1974 	}
1975 	return rtn;
1976 }
1977 
1978 struct FThinkerCollection
1979 {
1980 	int RefNum;
1981 	DThinker *Obj;
1982 };
1983 
1984 static TArray<FThinkerCollection> Collection;
1985 
AdjustPusher(int tag,int magnitude,int angle,DPusher::EPusher type)1986 void AdjustPusher (int tag, int magnitude, int angle, DPusher::EPusher type)
1987 {
1988 	// Find pushers already attached to the sector, and change their parameters.
1989 	{
1990 		TThinkerIterator<DPusher> iterator;
1991 		FThinkerCollection collect;
1992 
1993 		while ( (collect.Obj = iterator.Next ()) )
1994 		{
1995 			if ((collect.RefNum = ((DPusher *)collect.Obj)->CheckForSectorMatch (type, tag)) >= 0)
1996 			{
1997 				((DPusher *)collect.Obj)->ChangeValues (magnitude, angle);
1998 				Collection.Push (collect);
1999 			}
2000 		}
2001 	}
2002 
2003 	size_t numcollected = Collection.Size ();
2004 	int secnum;
2005 
2006 	// Now create pushers for any sectors that don't already have them.
2007 	FSectorTagIterator itr(tag);
2008 	while ((secnum = itr.Next()) >= 0)
2009 	{
2010 		unsigned int i;
2011 		for (i = 0; i < numcollected; i++)
2012 		{
2013 			if (Collection[i].RefNum == sectors[secnum].sectornum)
2014 				break;
2015 		}
2016 		if (i == numcollected)
2017 		{
2018 			new DPusher (type, NULL, magnitude, angle, NULL, secnum);
2019 		}
2020 	}
2021 	Collection.Clear ();
2022 }
2023 
FUNC(LS_Sector_SetWind)2024 FUNC(LS_Sector_SetWind)
2025 // Sector_SetWind (tag, amount, angle)
2026 {
2027 	if (arg3)
2028 		return false;
2029 
2030 	AdjustPusher (arg0, arg1, arg2, DPusher::p_wind);
2031 	return true;
2032 }
2033 
FUNC(LS_Sector_SetCurrent)2034 FUNC(LS_Sector_SetCurrent)
2035 // Sector_SetCurrent (tag, amount, angle)
2036 {
2037 	if (arg3)
2038 		return false;
2039 
2040 	AdjustPusher (arg0, arg1, arg2, DPusher::p_current);
2041 	return true;
2042 }
2043 
FUNC(LS_Sector_SetFriction)2044 FUNC(LS_Sector_SetFriction)
2045 // Sector_SetFriction (tag, amount)
2046 {
2047 	P_SetSectorFriction (arg0, arg1, true);
2048 	return true;
2049 }
2050 
FUNC(LS_Sector_SetTranslucent)2051 FUNC(LS_Sector_SetTranslucent)
2052 // Sector_SetTranslucent (tag, plane, amount, type)
2053 {
2054 	if (arg0 != 0)
2055 	{
2056 		int secnum;
2057 		FSectorTagIterator itr(arg0);
2058 		while ((secnum = itr.Next()) >= 0)
2059 		{
2060 			sectors[secnum].SetAlpha(arg1, Scale(arg2, OPAQUE, 255));
2061 			sectors[secnum].ChangeFlags(arg1, ~PLANEF_ADDITIVE, arg3? PLANEF_ADDITIVE:0);
2062 		}
2063 		return true;
2064 	}
2065 	return false;
2066 }
2067 
FUNC(LS_Sector_SetLink)2068 FUNC(LS_Sector_SetLink)
2069 // Sector_SetLink (controltag, linktag, floor/ceiling, movetype)
2070 {
2071 	if (arg0 != 0)	// control tag == 0 is for static initialization and must not be handled here
2072 	{
2073 		int control = P_FindFirstSectorFromTag(arg0);
2074 		if (control >= 0)
2075 		{
2076 			return P_AddSectorLinks(&sectors[control], arg1, arg2, arg3);
2077 		}
2078 	}
2079 	return false;
2080 }
2081 
2082 
SetWallScroller(int id,int sidechoice,fixed_t dx,fixed_t dy,int Where)2083 static void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, int Where)
2084 {
2085 	Where &=7;
2086 	if (Where == 0) return;
2087 
2088 	if ((dx | dy) == 0)
2089 	{
2090 		// Special case: Remove the scroller, because the deltas are both 0.
2091 		TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
2092 		DScroller *scroller;
2093 
2094 		while ( (scroller = iterator.Next ()) )
2095 		{
2096 			int wallnum = scroller->GetWallNum ();
2097 
2098 			if (wallnum >= 0 && tagManager.LineHasID(sides[wallnum].linedef, id) &&
2099 				int(sides[wallnum].linedef->sidedef[sidechoice] - sides) == wallnum &&
2100 				Where == scroller->GetScrollParts())
2101 			{
2102 				scroller->Destroy ();
2103 			}
2104 		}
2105 	}
2106 	else
2107 	{
2108 		// Find scrollers already attached to the matching walls, and change
2109 		// their rates.
2110 		{
2111 			TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
2112 			FThinkerCollection collect;
2113 
2114 			while ( (collect.Obj = iterator.Next ()) )
2115 			{
2116 				if ((collect.RefNum = ((DScroller *)collect.Obj)->GetWallNum ()) != -1 &&
2117 					tagManager.LineHasID(sides[collect.RefNum].linedef, id) &&
2118 					int(sides[collect.RefNum].linedef->sidedef[sidechoice] - sides) == collect.RefNum &&
2119 					Where == ((DScroller *)collect.Obj)->GetScrollParts())
2120 				{
2121 					((DScroller *)collect.Obj)->SetRate (dx, dy);
2122 					Collection.Push (collect);
2123 				}
2124 			}
2125 		}
2126 
2127 		size_t numcollected = Collection.Size ();
2128 		int linenum;
2129 
2130 		// Now create scrollers for any walls that don't already have them.
2131 		FLineIdIterator itr(id);
2132 		while ((linenum = itr.Next()) >= 0)
2133 		{
2134 			if (lines[linenum].sidedef[sidechoice] != NULL)
2135 			{
2136 				int sidenum = int(lines[linenum].sidedef[sidechoice] - sides);
2137 				unsigned int i;
2138 				for (i = 0; i < numcollected; i++)
2139 				{
2140 					if (Collection[i].RefNum == sidenum)
2141 						break;
2142 				}
2143 				if (i == numcollected)
2144 				{
2145 					new DScroller (DScroller::sc_side, dx, dy, -1, sidenum, 0, Where);
2146 				}
2147 			}
2148 		}
2149 		Collection.Clear ();
2150 	}
2151 }
2152 
FUNC(LS_Scroll_Texture_Both)2153 FUNC(LS_Scroll_Texture_Both)
2154 // Scroll_Texture_Both (id, left, right, up, down)
2155 {
2156 	if (arg0 == 0)
2157 		return false;
2158 
2159 	fixed_t dx = (arg1 - arg2) * (FRACUNIT/64);
2160 	fixed_t dy = (arg4 - arg3) * (FRACUNIT/64);
2161 	int sidechoice;
2162 
2163 	if (arg0 < 0)
2164 	{
2165 		sidechoice = 1;
2166 		arg0 = -arg0;
2167 	}
2168 	else
2169 	{
2170 		sidechoice = 0;
2171 	}
2172 
2173 	SetWallScroller (arg0, sidechoice, dx, dy, 7);
2174 
2175 	return true;
2176 }
2177 
FUNC(LS_Scroll_Wall)2178 FUNC(LS_Scroll_Wall)
2179 // Scroll_Wall (id, x, y, side, flags)
2180 {
2181 	if (arg0 == 0)
2182 		return false;
2183 
2184 	SetWallScroller (arg0, !!arg3, arg1, arg2, arg4);
2185 	return true;
2186 }
2187 
SetScroller(int tag,DScroller::EScrollType type,fixed_t dx,fixed_t dy)2188 static void SetScroller (int tag, DScroller::EScrollType type, fixed_t dx, fixed_t dy)
2189 {
2190 	TThinkerIterator<DScroller> iterator (STAT_SCROLLER);
2191 	DScroller *scroller;
2192 	int i;
2193 
2194 	// Check if there is already a scroller for this tag
2195 	// If at least one sector with this tag is scrolling, then they all are.
2196 	// If the deltas are both 0, we don't remove the scroller, because a
2197 	// displacement/accelerative scroller might have been set up, and there's
2198 	// no way to create one after the level is fully loaded.
2199 	i = 0;
2200 	while ( (scroller = iterator.Next ()) )
2201 	{
2202 		if (scroller->IsType (type))
2203 		{
2204 			if (tagManager.SectorHasTag(scroller->GetAffectee (), tag))
2205 			{
2206 				i++;
2207 				scroller->SetRate (dx, dy);
2208 			}
2209 		}
2210 	}
2211 
2212 	if (i > 0 || (dx|dy) == 0)
2213 	{
2214 		return;
2215 	}
2216 
2217 	// Need to create scrollers for the sector(s)
2218 	FSectorTagIterator itr(tag);
2219 	while ((i = itr.Next()) >= 0)
2220 	{
2221 		new DScroller (type, dx, dy, -1, i, 0);
2222 	}
2223 }
2224 
2225 // NOTE: For the next two functions, x-move and y-move are
2226 // 0-based, not 128-based as they are if they appear on lines.
2227 // Note also that parameter ordering is different.
2228 
FUNC(LS_Scroll_Floor)2229 FUNC(LS_Scroll_Floor)
2230 // Scroll_Floor (tag, x-move, y-move, s/c)
2231 {
2232 	fixed_t dx = arg1 * FRACUNIT/32;
2233 	fixed_t dy = arg2 * FRACUNIT/32;
2234 
2235 	if (arg3 == 0 || arg3 == 2)
2236 	{
2237 		SetScroller (arg0, DScroller::sc_floor, -dx, dy);
2238 	}
2239 	else
2240 	{
2241 		SetScroller (arg0, DScroller::sc_floor, 0, 0);
2242 	}
2243 	if (arg3 > 0)
2244 	{
2245 		SetScroller (arg0, DScroller::sc_carry, dx, dy);
2246 	}
2247 	else
2248 	{
2249 		SetScroller (arg0, DScroller::sc_carry, 0, 0);
2250 	}
2251 	return true;
2252 }
2253 
FUNC(LS_Scroll_Ceiling)2254 FUNC(LS_Scroll_Ceiling)
2255 // Scroll_Ceiling (tag, x-move, y-move, 0)
2256 {
2257 	fixed_t dx = arg1 * FRACUNIT/32;
2258 	fixed_t dy = arg2 * FRACUNIT/32;
2259 
2260 	SetScroller (arg0, DScroller::sc_ceiling, -dx, dy);
2261 	return true;
2262 }
2263 
FUNC(LS_PointPush_SetForce)2264 FUNC(LS_PointPush_SetForce)
2265 // PointPush_SetForce ()
2266 {
2267 	return false;
2268 }
2269 
FUNC(LS_Sector_SetDamage)2270 FUNC(LS_Sector_SetDamage)
2271 // Sector_SetDamage (tag, amount, mod, interval, leaky)
2272 {
2273 	// The sector still stores the mod in its old format because
2274 	// adding an FName to the sector_t structure might cause
2275 	// problems by adding an unwanted constructor.
2276 	// Since it doesn't really matter whether the type is translated
2277 	// here or in P_PlayerInSpecialSector I think it's the best solution.
2278 	FSectorTagIterator itr(arg0);
2279 	int secnum;
2280 	while ((secnum = itr.Next()) >= 0)
2281 	{
2282 		if (arg3 <= 0)	// emulate old and hacky method to handle leakiness.
2283 		{
2284 			if (arg1 < 20)
2285 			{
2286 				arg4 = 0;
2287 				arg3 = 32;
2288 			}
2289 			else if (arg1 < 50)
2290 			{
2291 				arg4 = 5;
2292 				arg3 = 32;
2293 			}
2294 			else
2295 			{
2296 				arg4 = 256;
2297 				arg3 = 1;
2298 			}
2299 		}
2300 		sectors[secnum].damageamount = (short)arg1;
2301 		sectors[secnum].damagetype = MODtoDamageType(arg2);
2302 		sectors[secnum].damageinterval = (short)arg3;
2303 		sectors[secnum].leakydamage = (short)arg4;
2304 	}
2305 	return true;
2306 }
2307 
FUNC(LS_Sector_SetGravity)2308 FUNC(LS_Sector_SetGravity)
2309 // Sector_SetGravity (tag, intpart, fracpart)
2310 {
2311 	float gravity;
2312 
2313 	if (arg2 > 99)
2314 		arg2 = 99;
2315 	gravity = (float)arg1 + (float)arg2 * 0.01f;
2316 
2317 	FSectorTagIterator itr(arg0);
2318 	int secnum;
2319 	while ((secnum = itr.Next()) >= 0)
2320 		sectors[secnum].gravity = gravity;
2321 
2322 	return true;
2323 }
2324 
FUNC(LS_Sector_SetColor)2325 FUNC(LS_Sector_SetColor)
2326 // Sector_SetColor (tag, r, g, b, desaturate)
2327 {
2328 	FSectorTagIterator itr(arg0);
2329 	int secnum;
2330 	while ((secnum = itr.Next()) >= 0)
2331 	{
2332 		sectors[secnum].SetColor(arg1, arg2, arg3, arg4);
2333 	}
2334 
2335 	return true;
2336 }
2337 
FUNC(LS_Sector_SetFade)2338 FUNC(LS_Sector_SetFade)
2339 // Sector_SetFade (tag, r, g, b)
2340 {
2341 	FSectorTagIterator itr(arg0);
2342 	int secnum;
2343 	while ((secnum = itr.Next()) >= 0)
2344 	{
2345 		sectors[secnum].SetFade(arg1, arg2, arg3);
2346 	}
2347 	return true;
2348 }
2349 
FUNC(LS_Sector_SetCeilingPanning)2350 FUNC(LS_Sector_SetCeilingPanning)
2351 // Sector_SetCeilingPanning (tag, x-int, x-frac, y-int, y-frac)
2352 {
2353 	fixed_t xofs = arg1 * FRACUNIT + arg2 * (FRACUNIT/100);
2354 	fixed_t yofs = arg3 * FRACUNIT + arg4 * (FRACUNIT/100);
2355 
2356 	FSectorTagIterator itr(arg0);
2357 	int secnum;
2358 	while ((secnum = itr.Next()) >= 0)
2359 	{
2360 		sectors[secnum].SetXOffset(sector_t::ceiling, xofs);
2361 		sectors[secnum].SetYOffset(sector_t::ceiling, yofs);
2362 	}
2363 	return true;
2364 }
2365 
FUNC(LS_Sector_SetFloorPanning)2366 FUNC(LS_Sector_SetFloorPanning)
2367 // Sector_SetFloorPanning (tag, x-int, x-frac, y-int, y-frac)
2368 {
2369 	fixed_t xofs = arg1 * FRACUNIT + arg2 * (FRACUNIT/100);
2370 	fixed_t yofs = arg3 * FRACUNIT + arg4 * (FRACUNIT/100);
2371 
2372 	FSectorTagIterator itr(arg0);
2373 	int secnum;
2374 	while ((secnum = itr.Next()) >= 0)
2375 	{
2376 		sectors[secnum].SetXOffset(sector_t::floor, xofs);
2377 		sectors[secnum].SetYOffset(sector_t::floor, yofs);
2378 	}
2379 	return true;
2380 }
2381 
FUNC(LS_Sector_SetFloorScale)2382 FUNC(LS_Sector_SetFloorScale)
2383 // Sector_SetFloorScale (tag, x-int, x-frac, y-int, y-frac)
2384 {
2385 	fixed_t xscale = arg1 * FRACUNIT + arg2 * (FRACUNIT/100);
2386 	fixed_t yscale = arg3 * FRACUNIT + arg4 * (FRACUNIT/100);
2387 
2388 	if (xscale)
2389 		xscale = FixedDiv (FRACUNIT, xscale);
2390 	if (yscale)
2391 		yscale = FixedDiv (FRACUNIT, yscale);
2392 
2393 	FSectorTagIterator itr(arg0);
2394 	int secnum;
2395 	while ((secnum = itr.Next()) >= 0)
2396 	{
2397 		if (xscale)
2398 			sectors[secnum].SetXScale(sector_t::floor, xscale);
2399 		if (yscale)
2400 			sectors[secnum].SetYScale(sector_t::floor, yscale);
2401 	}
2402 	return true;
2403 }
2404 
FUNC(LS_Sector_SetCeilingScale)2405 FUNC(LS_Sector_SetCeilingScale)
2406 // Sector_SetCeilingScale (tag, x-int, x-frac, y-int, y-frac)
2407 {
2408 	fixed_t xscale = arg1 * FRACUNIT + arg2 * (FRACUNIT/100);
2409 	fixed_t yscale = arg3 * FRACUNIT + arg4 * (FRACUNIT/100);
2410 
2411 	if (xscale)
2412 		xscale = FixedDiv (FRACUNIT, xscale);
2413 	if (yscale)
2414 		yscale = FixedDiv (FRACUNIT, yscale);
2415 
2416 	FSectorTagIterator itr(arg0);
2417 	int secnum;
2418 	while ((secnum = itr.Next()) >= 0)
2419 	{
2420 		if (xscale)
2421 			sectors[secnum].SetXScale(sector_t::ceiling, xscale);
2422 		if (yscale)
2423 			sectors[secnum].SetYScale(sector_t::ceiling, yscale);
2424 	}
2425 	return true;
2426 }
2427 
FUNC(LS_Sector_SetFloorScale2)2428 FUNC(LS_Sector_SetFloorScale2)
2429 // Sector_SetFloorScale2 (tag, x-factor, y-factor)
2430 {
2431 	if (arg1)
2432 		arg1 = FixedDiv (FRACUNIT, arg1);
2433 	if (arg2)
2434 		arg2 = FixedDiv (FRACUNIT, arg2);
2435 
2436 	FSectorTagIterator itr(arg0);
2437 	int secnum;
2438 	while ((secnum = itr.Next()) >= 0)
2439 	{
2440 		if (arg1)
2441 			sectors[secnum].SetXScale(sector_t::floor, arg1);
2442 		if (arg2)
2443 			sectors[secnum].SetYScale(sector_t::floor, arg2);
2444 	}
2445 	return true;
2446 }
2447 
FUNC(LS_Sector_SetCeilingScale2)2448 FUNC(LS_Sector_SetCeilingScale2)
2449 // Sector_SetFloorScale2 (tag, x-factor, y-factor)
2450 {
2451 	if (arg1)
2452 		arg1 = FixedDiv (FRACUNIT, arg1);
2453 	if (arg2)
2454 		arg2 = FixedDiv (FRACUNIT, arg2);
2455 
2456 	FSectorTagIterator itr(arg0);
2457 	int secnum;
2458 	while ((secnum = itr.Next()) >= 0)
2459 	{
2460 		if (arg1)
2461 			sectors[secnum].SetXScale(sector_t::ceiling, arg1);
2462 		if (arg2)
2463 			sectors[secnum].SetYScale(sector_t::ceiling, arg2);
2464 	}
2465 	return true;
2466 }
2467 
FUNC(LS_Sector_SetRotation)2468 FUNC(LS_Sector_SetRotation)
2469 // Sector_SetRotation (tag, floor-angle, ceiling-angle)
2470 {
2471 	angle_t ceiling = arg2 * ANGLE_1;
2472 	angle_t floor = arg1 * ANGLE_1;
2473 
2474 	FSectorTagIterator itr(arg0);
2475 	int secnum;
2476 	while ((secnum = itr.Next()) >= 0)
2477 	{
2478 		sectors[secnum].SetAngle(sector_t::floor, floor);
2479 		sectors[secnum].SetAngle(sector_t::ceiling, ceiling);
2480 	}
2481 	return true;
2482 }
2483 
FUNC(LS_Line_AlignCeiling)2484 FUNC(LS_Line_AlignCeiling)
2485 // Line_AlignCeiling (lineid, side)
2486 {
2487 	bool ret = 0;
2488 
2489 	FLineIdIterator itr(arg0);
2490 	int line;
2491 	while ((line = itr.Next()) >= 0)
2492 	{
2493 		ret |= P_AlignFlat (line, !!arg1, 1);
2494 	}
2495 	return ret;
2496 }
2497 
FUNC(LS_Line_AlignFloor)2498 FUNC(LS_Line_AlignFloor)
2499 // Line_AlignFloor (lineid, side)
2500 {
2501 	bool ret = 0;
2502 
2503 	FLineIdIterator itr(arg0);
2504 	int line;
2505 	while ((line = itr.Next()) >= 0)
2506 	{
2507 		ret |= P_AlignFlat (line, !!arg1, 0);
2508 	}
2509 	return ret;
2510 }
2511 
FUNC(LS_Line_SetTextureOffset)2512 FUNC(LS_Line_SetTextureOffset)
2513 // Line_SetTextureOffset (id, x, y, side, flags)
2514 {
2515 	const fixed_t NO_CHANGE = 32767<<FRACBITS;
2516 
2517 	if (arg0 == 0 || arg3 < 0 || arg3 > 1)
2518 		return false;
2519 
2520 	FLineIdIterator itr(arg0);
2521 	int line;
2522 	while ((line = itr.Next()) >= 0)
2523 	{
2524 		side_t *side = lines[line].sidedef[arg3];
2525 		if (side != NULL)
2526 		{
2527 
2528 			if ((arg4&8)==0)
2529 			{
2530 				// set
2531 				if (arg1 != NO_CHANGE)
2532 				{
2533 					if (arg4&1) side->SetTextureXOffset(side_t::top, arg1);
2534 					if (arg4&2) side->SetTextureXOffset(side_t::mid, arg1);
2535 					if (arg4&4) side->SetTextureXOffset(side_t::bottom, arg1);
2536 				}
2537 				if (arg2 != NO_CHANGE)
2538 				{
2539 					if (arg4&1) side->SetTextureYOffset(side_t::top, arg2);
2540 					if (arg4&2) side->SetTextureYOffset(side_t::mid, arg2);
2541 					if (arg4&4) side->SetTextureYOffset(side_t::bottom, arg2);
2542 				}
2543 			}
2544 			else
2545 			{
2546 				// add
2547 				if (arg1 != NO_CHANGE)
2548 				{
2549 					if (arg4&1) side->AddTextureXOffset(side_t::top, arg1);
2550 					if (arg4&2) side->AddTextureXOffset(side_t::mid, arg1);
2551 					if (arg4&4) side->AddTextureXOffset(side_t::bottom, arg1);
2552 				}
2553 				if (arg2 != NO_CHANGE)
2554 				{
2555 					if (arg4&1) side->AddTextureYOffset(side_t::top, arg2);
2556 					if (arg4&2) side->AddTextureYOffset(side_t::mid, arg2);
2557 					if (arg4&4) side->AddTextureYOffset(side_t::bottom, arg2);
2558 				}
2559 			}
2560 		}
2561 	}
2562 	return true;
2563 }
2564 
FUNC(LS_Line_SetTextureScale)2565 FUNC(LS_Line_SetTextureScale)
2566 // Line_SetTextureScale (id, x, y, side, flags)
2567 {
2568 	const fixed_t NO_CHANGE = 32767<<FRACBITS;
2569 
2570 	if (arg0 == 0 || arg3 < 0 || arg3 > 1)
2571 		return false;
2572 
2573 	FLineIdIterator itr(arg0);
2574 	int line;
2575 	while ((line = itr.Next()) >= 0)
2576 	{
2577 		side_t *side = lines[line].sidedef[arg3];
2578 		if (side != NULL)
2579 		{
2580 			if ((arg4&8)==0)
2581 			{
2582 				// set
2583 				if (arg1 != NO_CHANGE)
2584 				{
2585 					if (arg4&1) side->SetTextureXScale(side_t::top, arg1);
2586 					if (arg4&2) side->SetTextureXScale(side_t::mid, arg1);
2587 					if (arg4&4) side->SetTextureXScale(side_t::bottom, arg1);
2588 				}
2589 				if (arg2 != NO_CHANGE)
2590 				{
2591 					if (arg4&1) side->SetTextureYScale(side_t::top, arg2);
2592 					if (arg4&2) side->SetTextureYScale(side_t::mid, arg2);
2593 					if (arg4&4) side->SetTextureYScale(side_t::bottom, arg2);
2594 				}
2595 			}
2596 			else
2597 			{
2598 				// add
2599 				if (arg1 != NO_CHANGE)
2600 				{
2601 					if (arg4&1) side->MultiplyTextureXScale(side_t::top, arg1);
2602 					if (arg4&2) side->MultiplyTextureXScale(side_t::mid, arg1);
2603 					if (arg4&4) side->MultiplyTextureXScale(side_t::bottom, arg1);
2604 				}
2605 				if (arg2 != NO_CHANGE)
2606 				{
2607 					if (arg4&1) side->MultiplyTextureYScale(side_t::top, arg2);
2608 					if (arg4&2) side->MultiplyTextureYScale(side_t::mid, arg2);
2609 					if (arg4&4) side->MultiplyTextureYScale(side_t::bottom, arg2);
2610 				}
2611 			}
2612 		}
2613 	}
2614 	return true;
2615 }
2616 
FUNC(LS_Line_SetBlocking)2617 FUNC(LS_Line_SetBlocking)
2618 // Line_SetBlocking (id, setflags, clearflags)
2619 {
2620 	static const int flagtrans[] =
2621 	{
2622 		ML_BLOCKING,
2623 		ML_BLOCKMONSTERS,
2624 		ML_BLOCK_PLAYERS,
2625 		ML_BLOCK_FLOATERS,
2626 		ML_BLOCKPROJECTILE,
2627 		ML_BLOCKEVERYTHING,
2628 		ML_RAILING,
2629 		ML_BLOCKUSE,
2630 		ML_BLOCKSIGHT,
2631 		ML_BLOCKHITSCAN,
2632 		ML_SOUNDBLOCK,
2633 		-1
2634 	};
2635 
2636 	if (arg0 == 0) return false;
2637 
2638 	int setflags = 0;
2639 	int clearflags = 0;
2640 
2641 	for(int i = 0; flagtrans[i] != -1; i++, arg1 >>= 1, arg2 >>= 1)
2642 	{
2643 		if (arg1 & 1) setflags |= flagtrans[i];
2644 		if (arg2 & 1) clearflags |= flagtrans[i];
2645 	}
2646 
2647 	FLineIdIterator itr(arg0);
2648 	int line;
2649 	while ((line = itr.Next()) >= 0)
2650 	{
2651 		lines[line].flags = (lines[line].flags & ~clearflags) | setflags;
2652 	}
2653 	return true;
2654 }
2655 
2656 
2657 
FUNC(LS_ChangeCamera)2658 FUNC(LS_ChangeCamera)
2659 // ChangeCamera (tid, who, revert?)
2660 {
2661 	AActor *camera;
2662 	if (arg0 != 0)
2663 	{
2664 		FActorIterator iterator (arg0);
2665 		camera = iterator.Next ();
2666 	}
2667 	else
2668 	{
2669 		camera = NULL;
2670 	}
2671 
2672 	if (!it || !it->player || arg1)
2673 	{
2674 		int i;
2675 
2676 		for (i = 0; i < MAXPLAYERS; i++)
2677 		{
2678 			if (!playeringame[i])
2679 				continue;
2680 
2681 			AActor *oldcamera = players[i].camera;
2682 			if (camera)
2683 			{
2684 				players[i].camera = camera;
2685 				if (arg2)
2686 					players[i].cheats |= CF_REVERTPLEASE;
2687 			}
2688 			else
2689 			{
2690 				players[i].camera = players[i].mo;
2691 				players[i].cheats &= ~CF_REVERTPLEASE;
2692 			}
2693 			if (oldcamera != players[i].camera)
2694 			{
2695 				R_ClearPastViewer (players[i].camera);
2696 			}
2697 		}
2698 	}
2699 	else
2700 	{
2701 		AActor *oldcamera = it->player->camera;
2702 		if (camera)
2703 		{
2704 			it->player->camera = camera;
2705 			if (arg2)
2706 				it->player->cheats |= CF_REVERTPLEASE;
2707 		}
2708 		else
2709 		{
2710 			it->player->camera = it;
2711 			it->player->cheats &= ~CF_REVERTPLEASE;
2712 		}
2713 		if (oldcamera != it->player->camera)
2714 		{
2715 			R_ClearPastViewer (it->player->camera);
2716 		}
2717 	}
2718 
2719 	return true;
2720 }
2721 
2722 enum
2723 {
2724 	PROP_FROZEN,
2725 	PROP_NOTARGET,
2726 	PROP_INSTANTWEAPONSWITCH,
2727 	PROP_FLY,
2728 	PROP_TOTALLYFROZEN,
2729 
2730 	PROP_INVULNERABILITY,
2731 	PROP_STRENGTH,
2732 	PROP_INVISIBILITY,
2733 	PROP_RADIATIONSUIT,
2734 	PROP_ALLMAP,
2735 	PROP_INFRARED,
2736 	PROP_WEAPONLEVEL2,
2737 	PROP_FLIGHT,
2738 	PROP_UNUSED1,
2739 	PROP_UNUSED2,
2740 	PROP_SPEED,
2741 	PROP_BUDDHA,
2742 };
2743 
FUNC(LS_SetPlayerProperty)2744 FUNC(LS_SetPlayerProperty)
2745 // SetPlayerProperty (who, value, which)
2746 // who == 0: set activator's property
2747 // who == 1: set every player's property
2748 {
2749 	int mask = 0;
2750 
2751 	if ((!it || !it->player) && !arg0)
2752 		return false;
2753 
2754 	// Add or remove a power
2755 	if (arg2 >= PROP_INVULNERABILITY && arg2 <= PROP_SPEED)
2756 	{
2757 		static const PClass *powers[11] =
2758 		{
2759 			RUNTIME_CLASS(APowerInvulnerable),
2760 			RUNTIME_CLASS(APowerStrength),
2761 			RUNTIME_CLASS(APowerInvisibility),
2762 			RUNTIME_CLASS(APowerIronFeet),
2763 			NULL, // MapRevealer
2764 			RUNTIME_CLASS(APowerLightAmp),
2765 			RUNTIME_CLASS(APowerWeaponLevel2),
2766 			RUNTIME_CLASS(APowerFlight),
2767 			NULL,
2768 			NULL,
2769 			RUNTIME_CLASS(APowerSpeed)
2770 		};
2771 		int power = arg2 - PROP_INVULNERABILITY;
2772 
2773 		if (power > 4 && powers[power] == NULL)
2774 		{
2775 			return false;
2776 		}
2777 
2778 		if (arg0 == 0)
2779 		{
2780 			if (arg1)
2781 			{ // Give power to activator
2782 				if (power != 4)
2783 				{
2784 					APowerup *item = static_cast<APowerup*>(it->GiveInventoryType (powers[power]));
2785 					if (item != NULL && power == 0 && arg1 == 1)
2786 					{
2787 						item->BlendColor = MakeSpecialColormap(INVERSECOLORMAP);
2788 					}
2789 				}
2790 				else if (it->player - players == consoleplayer)
2791 				{
2792 					level.flags2 |= LEVEL2_ALLMAP;
2793 				}
2794 			}
2795 			else
2796 			{ // Take power from activator
2797 				if (power != 4)
2798 				{
2799 					AInventory *item = it->FindInventory (powers[power], true);
2800 					if (item != NULL)
2801 					{
2802 						item->Destroy ();
2803 					}
2804 				}
2805 				else if (it->player - players == consoleplayer)
2806 				{
2807 					level.flags2 &= ~LEVEL2_ALLMAP;
2808 				}
2809 			}
2810 		}
2811 		else
2812 		{
2813 			int i;
2814 
2815 			for (i = 0; i < MAXPLAYERS; i++)
2816 			{
2817 				if (!playeringame[i] || players[i].mo == NULL)
2818 					continue;
2819 
2820 				if (arg1)
2821 				{ // Give power
2822 					if (power != 4)
2823 					{
2824 						APowerup *item = static_cast<APowerup*>(players[i].mo->GiveInventoryType (powers[power]));
2825 						if (item != NULL && power == 0 && arg1 == 1)
2826 						{
2827 							item->BlendColor = MakeSpecialColormap(INVERSECOLORMAP);
2828 						}
2829 					}
2830 					else if (i == consoleplayer)
2831 					{
2832 						level.flags2 |= LEVEL2_ALLMAP;
2833 					}
2834 				}
2835 				else
2836 				{ // Take power
2837 					if (power != 4)
2838 					{
2839 						AInventory *item = players[i].mo->FindInventory (powers[power]);
2840 						if (item != NULL)
2841 						{
2842 							item->Destroy ();
2843 						}
2844 					}
2845 					else if (i == consoleplayer)
2846 					{
2847 						level.flags2 &= ~LEVEL2_ALLMAP;
2848 					}
2849 				}
2850 			}
2851 		}
2852 		return true;
2853 	}
2854 
2855 	// Set or clear a flag
2856 	switch (arg2)
2857 	{
2858 	case PROP_BUDDHA:
2859 		mask = CF_BUDDHA;
2860 		break;
2861 	case PROP_FROZEN:
2862 		mask = CF_FROZEN;
2863 		break;
2864 	case PROP_NOTARGET:
2865 		mask = CF_NOTARGET;
2866 		break;
2867 	case PROP_INSTANTWEAPONSWITCH:
2868 		mask = CF_INSTANTWEAPSWITCH;
2869 		break;
2870 	case PROP_FLY:
2871 		//mask = CF_FLY;
2872 		break;
2873 	case PROP_TOTALLYFROZEN:
2874 		mask = CF_TOTALLYFROZEN;
2875 		break;
2876 	}
2877 
2878 	if (arg0 == 0)
2879 	{
2880 		if (arg1)
2881 		{
2882 			it->player->cheats |= mask;
2883 			if (arg2 == PROP_FLY)
2884 			{
2885 				it->flags7 |= MF7_FLYCHEAT;
2886 				it->flags2 |= MF2_FLY;
2887 				it->flags |= MF_NOGRAVITY;
2888 			}
2889 		}
2890 		else
2891 		{
2892 			it->player->cheats &= ~mask;
2893 			if (arg2 == PROP_FLY)
2894 			{
2895 				it->flags7 &= ~MF7_FLYCHEAT;
2896 				it->flags2 &= ~MF2_FLY;
2897 				it->flags &= ~MF_NOGRAVITY;
2898 			}
2899 		}
2900 	}
2901 	else
2902 	{
2903 		int i;
2904 
2905 		if ((ib_compatflags & BCOMPATF_LINKFROZENPROPS) && (mask & (CF_FROZEN | CF_TOTALLYFROZEN)))
2906 		{ // Clearing one of these properties clears both of them (if the compat flag is set.)
2907 			mask = CF_FROZEN | CF_TOTALLYFROZEN;
2908 		}
2909 
2910 		for (i = 0; i < MAXPLAYERS; i++)
2911 		{
2912 			if (!playeringame[i])
2913 				continue;
2914 
2915 			if (arg1)
2916 			{
2917 				players[i].cheats |= mask;
2918 				if (arg2 == PROP_FLY)
2919 				{
2920 					players[i].mo->flags2 |= MF2_FLY;
2921 					players[i].mo->flags |= MF_NOGRAVITY;
2922 				}
2923 			}
2924 			else
2925 			{
2926 				players[i].cheats &= ~mask;
2927 				if (arg2 == PROP_FLY)
2928 				{
2929 					players[i].mo->flags2 &= ~MF2_FLY;
2930 					players[i].mo->flags &= ~MF_NOGRAVITY;
2931 				}
2932 			}
2933 		}
2934 	}
2935 
2936 	return !!mask;
2937 }
2938 
FUNC(LS_TranslucentLine)2939 FUNC(LS_TranslucentLine)
2940 // TranslucentLine (id, amount, type)
2941 {
2942 	FLineIdIterator itr(arg0);
2943 	int linenum;
2944 	while ((linenum = itr.Next()) >= 0)
2945 	{
2946 		lines[linenum].Alpha = Scale(clamp(arg1, 0, 255), FRACUNIT, 255);
2947 		if (arg2 == 0)
2948 		{
2949 			lines[linenum].flags &= ~ML_ADDTRANS;
2950 		}
2951 		else if (arg2 == 1)
2952 		{
2953 			lines[linenum].flags |= ML_ADDTRANS;
2954 		}
2955 		else
2956 		{
2957 			Printf ("Unknown translucency type used with TranslucentLine\n");
2958 		}
2959 	}
2960 
2961 	return true;
2962 }
2963 
FUNC(LS_Autosave)2964 FUNC(LS_Autosave)
2965 {
2966 	if (gameaction != ga_savegame)
2967 	{
2968 		level.flags2 &= ~LEVEL2_NOAUTOSAVEHINT;
2969 		Net_WriteByte (DEM_CHECKAUTOSAVE);
2970 	}
2971 	return true;
2972 }
2973 
FUNC(LS_ChangeSkill)2974 FUNC(LS_ChangeSkill)
2975 {
2976 	if ((unsigned)arg0 >= AllSkills.Size())
2977 	{
2978 		NextSkill = -1;
2979 	}
2980 	else
2981 	{
2982 		NextSkill = arg0;
2983 	}
2984 	return true;
2985 }
2986 
FUNC(LS_NoiseAlert)2987 FUNC(LS_NoiseAlert)
2988 // NoiseAlert (TID of target, TID of emitter)
2989 {
2990 	AActor *target, *emitter;
2991 
2992 	if (arg0 == 0)
2993 	{
2994 		target = it;
2995 	}
2996 	else
2997 	{
2998 		FActorIterator iter (arg0);
2999 		target = iter.Next();
3000 	}
3001 
3002 	if (arg1 == 0)
3003 	{
3004 		emitter = it;
3005 	}
3006 	else if (arg1 == arg0)
3007 	{
3008 		emitter = target;
3009 	}
3010 	else
3011 	{
3012 		FActorIterator iter (arg1);
3013 		emitter = iter.Next();
3014 	}
3015 
3016 	P_NoiseAlert (target, emitter);
3017 	return true;
3018 }
3019 
FUNC(LS_SendToCommunicator)3020 FUNC(LS_SendToCommunicator)
3021 // SendToCommunicator (voc #, front-only, identify, nolog)
3022 {
3023 	// This obviously isn't going to work for co-op.
3024 	if (arg1 && backSide)
3025 		return false;
3026 
3027 	if (it != NULL && it->player != NULL && it->FindInventory(NAME_Communicator))
3028 	{
3029 		char name[32];
3030 		mysnprintf (name, countof(name), "svox/voc%d", arg0);
3031 
3032 		if (!arg3)
3033 		{
3034 			it->player->SetLogNumber (arg0);
3035 		}
3036 
3037 		if (it->CheckLocalView (consoleplayer))
3038 		{
3039 			S_StopSound (CHAN_VOICE);
3040 			S_Sound (CHAN_VOICE, name, 1, ATTN_NORM);
3041 
3042 			// Get the message from the LANGUAGE lump.
3043 			FString msg;
3044 			msg.Format("TXT_COMM%d", arg2);
3045 			const char *str = GStrings[msg];
3046 			if (str != NULL)
3047 			{
3048 				Printf (PRINT_CHAT, "%s\n", str);
3049 			}
3050 		}
3051 		return true;
3052 	}
3053 	return false;
3054 }
3055 
FUNC(LS_ForceField)3056 FUNC(LS_ForceField)
3057 // ForceField ()
3058 {
3059 	if (it != NULL)
3060 	{
3061 		P_DamageMobj (it, NULL, NULL, 16, NAME_None);
3062 		P_ThrustMobj (it, it->angle + ANGLE_180, 0x7D000);
3063 	}
3064 	return true;
3065 }
3066 
FUNC(LS_ClearForceField)3067 FUNC(LS_ClearForceField)
3068 // ClearForceField (tag)
3069 {
3070 	bool rtn = false;
3071 
3072 	FSectorTagIterator itr(arg0);
3073 	int secnum;
3074 	while ((secnum = itr.Next()) >= 0)
3075 	{
3076 		sector_t *sec = &sectors[secnum];
3077 		rtn = true;
3078 
3079 		for (int i = 0; i < sec->linecount; ++i)
3080 		{
3081 			line_t *line = sec->lines[i];
3082 			if (line->backsector != NULL && line->special == ForceField)
3083 			{
3084 				line->flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING);
3085 				line->special = 0;
3086 				line->sidedef[0]->SetTexture(side_t::mid, FNullTextureID());
3087 				line->sidedef[1]->SetTexture(side_t::mid, FNullTextureID());
3088 			}
3089 		}
3090 	}
3091 	return rtn;
3092 }
3093 
FUNC(LS_GlassBreak)3094 FUNC(LS_GlassBreak)
3095 // GlassBreak (bNoJunk)
3096 {
3097 	bool switched;
3098 	bool quest1, quest2;
3099 
3100 	ln->flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING);
3101 	switched = P_ChangeSwitchTexture (ln->sidedef[0], false, 0, &quest1);
3102 	ln->special = 0;
3103 	if (ln->sidedef[1] != NULL)
3104 	{
3105 		switched |= P_ChangeSwitchTexture (ln->sidedef[1], false, 0, &quest2);
3106 	}
3107 	else
3108 	{
3109 		quest2 = quest1;
3110 	}
3111 	if (switched)
3112 	{
3113 		if (!arg0)
3114 		{ // Break some glass
3115 			fixed_t x, y;
3116 			AActor *glass;
3117 			angle_t an;
3118 			int speed;
3119 
3120 			x = ln->v1->x + ln->dx/2;
3121 			y = ln->v1->y + ln->dy/2;
3122 			x += (ln->frontsector->soundorg[0] - x) / 5;
3123 			y += (ln->frontsector->soundorg[1] - y) / 5;
3124 
3125 			for (int i = 0; i < 7; ++i)
3126 			{
3127 				glass = Spawn("GlassJunk", x, y, ONFLOORZ, ALLOW_REPLACE);
3128 
3129 				glass->AddZ(24 * FRACUNIT);
3130 				glass->SetState (glass->SpawnState + (pr_glass() % glass->health));
3131 				an = pr_glass() << (32-8);
3132 				glass->angle = an;
3133 				an >>= ANGLETOFINESHIFT;
3134 				speed = pr_glass() & 3;
3135 				glass->velx = finecosine[an] * speed;
3136 				glass->vely = finesine[an] * speed;
3137 				glass->velz = (pr_glass() & 7) << FRACBITS;
3138 				// [RH] Let the shards stick around longer than they did in Strife.
3139 				glass->tics += pr_glass();
3140 			}
3141 		}
3142 		if (quest1 || quest2)
3143 		{ // Up stats and signal this mission is complete
3144 			if (it == NULL)
3145 			{
3146 				for (int i = 0; i < MAXPLAYERS; ++i)
3147 				{
3148 					if (playeringame[i])
3149 					{
3150 						it = players[i].mo;
3151 						break;
3152 					}
3153 				}
3154 			}
3155 			if (it != NULL)
3156 			{
3157 				it->GiveInventoryType (QuestItemClasses[28]);
3158 				it->GiveInventoryType (RUNTIME_CLASS(AUpgradeAccuracy));
3159 				it->GiveInventoryType (RUNTIME_CLASS(AUpgradeStamina));
3160 			}
3161 		}
3162 	}
3163 	// We already changed the switch texture, so don't make the main code switch it back.
3164 	return false;
3165 }
3166 
FUNC(LS_StartConversation)3167 FUNC(LS_StartConversation)
3168 // StartConversation (tid, facetalker)
3169 {
3170 	FActorIterator iterator (arg0);
3171 
3172 	AActor *target = iterator.Next();
3173 
3174 	// Nothing to talk to
3175 	if (target == NULL)
3176 	{
3177 		return false;
3178 	}
3179 
3180 	// Only living players are allowed to start conversations
3181 	if (it == NULL || it->player == NULL || it->player->mo != it || it->health<=0)
3182 	{
3183 		return false;
3184 	}
3185 
3186 	// Dead things can't talk.
3187 	if (target->health <= 0)
3188 	{
3189 		return false;
3190 	}
3191 	// Fighting things don't talk either.
3192 	if (target->flags4 & MF4_INCOMBAT)
3193 	{
3194 		return false;
3195 	}
3196 	if (target->Conversation != NULL)
3197 	{
3198 		// Give the NPC a chance to play a brief animation
3199 		target->ConversationAnimation (0);
3200 		P_StartConversation (target, it, !!arg1, true);
3201 		return true;
3202 	}
3203 	return false;
3204 }
3205 
FUNC(LS_Thing_SetConversation)3206 FUNC(LS_Thing_SetConversation)
3207 // Thing_SetConversation (tid, dlg_id)
3208 {
3209 	int dlg_index = -1;
3210 	FStrifeDialogueNode *node = NULL;
3211 
3212 	if (arg1 != 0)
3213 	{
3214 		dlg_index = GetConversation(arg1);
3215 		if (dlg_index == -1) return false;
3216 		node = StrifeDialogues[dlg_index];
3217 	}
3218 
3219 	if (arg0 != 0)
3220 	{
3221 		FActorIterator iterator (arg0);
3222 		while ((it = iterator.Next()) != NULL)
3223 		{
3224 			it->ConversationRoot = dlg_index;
3225 			it->Conversation = node;
3226 		}
3227 	}
3228 	else if (it)
3229 	{
3230 		it->ConversationRoot = dlg_index;
3231 		it->Conversation = node;
3232 	}
3233 	return true;
3234 }
3235 
3236 
3237 lnSpecFunc LineSpecials[256] =
3238 {
3239 	/*   0 */ LS_NOP,
3240 	/*   1 */ LS_NOP,		// Polyobj_StartLine,
3241 	/*   2 */ LS_Polyobj_RotateLeft,
3242 	/*   3 */ LS_Polyobj_RotateRight,
3243 	/*   4 */ LS_Polyobj_Move,
3244 	/*   5 */ LS_NOP,		// Polyobj_ExplicitLine
3245 	/*   6 */ LS_Polyobj_MoveTimes8,
3246 	/*   7 */ LS_Polyobj_DoorSwing,
3247 	/*   8 */ LS_Polyobj_DoorSlide,
3248 	/*   9 */ LS_NOP,		// Line_Horizon
3249 	/*  10 */ LS_Door_Close,
3250 	/*  11 */ LS_Door_Open,
3251 	/*  12 */ LS_Door_Raise,
3252 	/*  13 */ LS_Door_LockedRaise,
3253 	/*  14 */ LS_Door_Animated,
3254 	/*  15 */ LS_Autosave,
3255 	/*  16 */ LS_NOP,		// Transfer_WallLight
3256 	/*  17 */ LS_Thing_Raise,
3257 	/*  18 */ LS_StartConversation,
3258 	/*  19 */ LS_Thing_Stop,
3259 	/*  20 */ LS_Floor_LowerByValue,
3260 	/*  21 */ LS_Floor_LowerToLowest,
3261 	/*  22 */ LS_Floor_LowerToNearest,
3262 	/*  23 */ LS_Floor_RaiseByValue,
3263 	/*  24 */ LS_Floor_RaiseToHighest,
3264 	/*  25 */ LS_Floor_RaiseToNearest,
3265 	/*  26 */ LS_Stairs_BuildDown,
3266 	/*  27 */ LS_Stairs_BuildUp,
3267 	/*  28 */ LS_Floor_RaiseAndCrush,
3268 	/*  29 */ LS_Pillar_Build,
3269 	/*  30 */ LS_Pillar_Open,
3270 	/*  31 */ LS_Stairs_BuildDownSync,
3271 	/*  32 */ LS_Stairs_BuildUpSync,
3272 	/*  33 */ LS_ForceField,
3273 	/*  34 */ LS_ClearForceField,
3274 	/*  35 */ LS_Floor_RaiseByValueTimes8,
3275 	/*  36 */ LS_Floor_LowerByValueTimes8,
3276 	/*  37 */ LS_Floor_MoveToValue,
3277 	/*  38 */ LS_Ceiling_Waggle,
3278 	/*  39 */ LS_Teleport_ZombieChanger,
3279 	/*  40 */ LS_Ceiling_LowerByValue,
3280 	/*  41 */ LS_Ceiling_RaiseByValue,
3281 	/*  42 */ LS_Ceiling_CrushAndRaise,
3282 	/*  43 */ LS_Ceiling_LowerAndCrush,
3283 	/*  44 */ LS_Ceiling_CrushStop,
3284 	/*  45 */ LS_Ceiling_CrushRaiseAndStay,
3285 	/*  46 */ LS_Floor_CrushStop,
3286 	/*  47 */ LS_Ceiling_MoveToValue,
3287 	/*  48 */ LS_NOP,		// Sector_Attach3dMidtex
3288 	/*  49 */ LS_GlassBreak,
3289 	/*  50 */ LS_NOP,		// ExtraFloor_LightOnly
3290 	/*  51 */ LS_Sector_SetLink,
3291 	/*  52 */ LS_Scroll_Wall,
3292 	/*  53 */ LS_Line_SetTextureOffset,
3293 	/*  54 */ LS_Sector_ChangeFlags,
3294 	/*  55 */ LS_Line_SetBlocking,
3295 	/*  56 */ LS_Line_SetTextureScale,
3296 	/*  57 */ LS_NOP,		// Sector_SetPortal
3297 	/*  58 */ LS_NOP,		// Sector_CopyScroller
3298 	/*  59 */ LS_Polyobj_OR_MoveToSpot,
3299 	/*  60 */ LS_Plat_PerpetualRaise,
3300 	/*  61 */ LS_Plat_Stop,
3301 	/*  62 */ LS_Plat_DownWaitUpStay,
3302 	/*  63 */ LS_Plat_DownByValue,
3303 	/*  64 */ LS_Plat_UpWaitDownStay,
3304 	/*  65 */ LS_Plat_UpByValue,
3305 	/*  66 */ LS_Floor_LowerInstant,
3306 	/*  67 */ LS_Floor_RaiseInstant,
3307 	/*  68 */ LS_Floor_MoveToValueTimes8,
3308 	/*  69 */ LS_Ceiling_MoveToValueTimes8,
3309 	/*  70 */ LS_Teleport,
3310 	/*  71 */ LS_Teleport_NoFog,
3311 	/*  72 */ LS_ThrustThing,
3312 	/*  73 */ LS_DamageThing,
3313 	/*  74 */ LS_Teleport_NewMap,
3314 	/*  75 */ LS_Teleport_EndGame,
3315 	/*  76 */ LS_TeleportOther,
3316 	/*  77 */ LS_TeleportGroup,
3317 	/*  78 */ LS_TeleportInSector,
3318 	/*  79 */ LS_Thing_SetConversation,
3319 	/*  80 */ LS_ACS_Execute,
3320 	/*  81 */ LS_ACS_Suspend,
3321 	/*  82 */ LS_ACS_Terminate,
3322 	/*  83 */ LS_ACS_LockedExecute,
3323 	/*  84 */ LS_ACS_ExecuteWithResult,
3324 	/*  85 */ LS_ACS_LockedExecuteDoor,
3325 	/*  86 */ LS_Polyobj_MoveToSpot,
3326 	/*  87 */ LS_Polyobj_Stop,
3327 	/*  88 */ LS_Polyobj_MoveTo,
3328 	/*  89 */ LS_Polyobj_OR_MoveTo,
3329 	/*  90 */ LS_Polyobj_OR_RotateLeft,
3330 	/*  91 */ LS_Polyobj_OR_RotateRight,
3331 	/*  92 */ LS_Polyobj_OR_Move,
3332 	/*  93 */ LS_Polyobj_OR_MoveTimes8,
3333 	/*  94 */ LS_Pillar_BuildAndCrush,
3334 	/*  95 */ LS_FloorAndCeiling_LowerByValue,
3335 	/*  96 */ LS_FloorAndCeiling_RaiseByValue,
3336 	/*  97 */ LS_Ceiling_LowerAndCrushDist,
3337 	/*  98 */ LS_Sector_SetTranslucent,
3338 	/*  99 */ LS_Floor_RaiseAndCrushDoom,
3339 	/* 100 */ LS_NOP,		// Scroll_Texture_Left
3340 	/* 101 */ LS_NOP,		// Scroll_Texture_Right
3341 	/* 102 */ LS_NOP,		// Scroll_Texture_Up
3342 	/* 103 */ LS_NOP,		// Scroll_Texture_Down
3343 	/* 104 */ LS_Ceiling_CrushAndRaiseSilentDist,
3344 	/* 105 */ LS_NOP,
3345 	/* 106 */ LS_NOP,
3346 	/* 107 */ LS_NOP,
3347 	/* 108 */ LS_NOP,
3348 	/* 109 */ LS_Light_ForceLightning,
3349 	/* 110 */ LS_Light_RaiseByValue,
3350 	/* 111 */ LS_Light_LowerByValue,
3351 	/* 112 */ LS_Light_ChangeToValue,
3352 	/* 113 */ LS_Light_Fade,
3353 	/* 114 */ LS_Light_Glow,
3354 	/* 115 */ LS_Light_Flicker,
3355 	/* 116 */ LS_Light_Strobe,
3356 	/* 117 */ LS_Light_Stop,
3357 	/* 118 */ LS_NOP,		// Plane_Copy
3358 	/* 119 */ LS_Thing_Damage,
3359 	/* 120 */ LS_Radius_Quake,
3360 	/* 121 */ LS_NOP,		// Line_SetIdentification
3361 	/* 122 */ LS_NOP,
3362 	/* 123 */ LS_NOP,
3363 	/* 124 */ LS_NOP,
3364 	/* 125 */ LS_Thing_Move,
3365 	/* 126 */ LS_NOP,
3366 	/* 127 */ LS_Thing_SetSpecial,
3367 	/* 128 */ LS_ThrustThingZ,
3368 	/* 129 */ LS_UsePuzzleItem,
3369 	/* 130 */ LS_Thing_Activate,
3370 	/* 131 */ LS_Thing_Deactivate,
3371 	/* 132 */ LS_Thing_Remove,
3372 	/* 133 */ LS_Thing_Destroy,
3373 	/* 134 */ LS_Thing_Projectile,
3374 	/* 135 */ LS_Thing_Spawn,
3375 	/* 136 */ LS_Thing_ProjectileGravity,
3376 	/* 137 */ LS_Thing_SpawnNoFog,
3377 	/* 138 */ LS_Floor_Waggle,
3378 	/* 139 */ LS_Thing_SpawnFacing,
3379 	/* 140 */ LS_Sector_ChangeSound,
3380 	/* 141 */ LS_NOP,
3381 	/* 142 */ LS_NOP,
3382 	/* 143 */ LS_NOP,
3383 	/* 144 */ LS_NOP,
3384 	/* 145 */ LS_NOP,		// 145 Player_SetTeam
3385 	/* 146 */ LS_NOP,
3386 	/* 147 */ LS_NOP,
3387 	/* 148 */ LS_NOP,
3388 	/* 149 */ LS_NOP,
3389 	/* 150 */ LS_NOP,
3390 	/* 151 */ LS_NOP,
3391 	/* 152 */ LS_NOP,		// 152 Team_Score
3392 	/* 153 */ LS_NOP,		// 153 Team_GivePoints
3393 	/* 154 */ LS_Teleport_NoStop,
3394 	/* 155 */ LS_NOP,
3395 	/* 156 */ LS_NOP,
3396 	/* 157 */ LS_NOP,		// SetGlobalFogParameter // in GZDoom
3397 	/* 158 */ LS_NOP,		// FS_Execute
3398 	/* 159 */ LS_NOP,		// Sector_SetPlaneReflection in GZDoom
3399 	/* 160 */ LS_NOP,		// Sector_Set3DFloor
3400 	/* 161 */ LS_NOP,		// Sector_SetContents
3401 	/* 162 */ LS_NOP,		// Reserved Doom64 branch
3402 	/* 163 */ LS_NOP,		// Reserved Doom64 branch
3403 	/* 164 */ LS_NOP,		// Reserved Doom64 branch
3404 	/* 165 */ LS_NOP,		// Reserved Doom64 branch
3405 	/* 166 */ LS_NOP,		// Reserved Doom64 branch
3406 	/* 167 */ LS_NOP,		// Reserved Doom64 branch
3407 	/* 168 */ LS_Ceiling_CrushAndRaiseDist,
3408 	/* 169 */ LS_Generic_Crusher2,
3409 	/* 170 */ LS_Sector_SetCeilingScale2,
3410 	/* 171 */ LS_Sector_SetFloorScale2,
3411 	/* 172 */ LS_Plat_UpNearestWaitDownStay,
3412 	/* 173 */ LS_NoiseAlert,
3413 	/* 174 */ LS_SendToCommunicator,
3414 	/* 175 */ LS_Thing_ProjectileIntercept,
3415 	/* 176 */ LS_Thing_ChangeTID,
3416 	/* 177 */ LS_Thing_Hate,
3417 	/* 178 */ LS_Thing_ProjectileAimed,
3418 	/* 179 */ LS_ChangeSkill,
3419 	/* 180 */ LS_Thing_SetTranslation,
3420 	/* 181 */ LS_NOP,		// Plane_Align
3421 	/* 182 */ LS_NOP,		// Line_Mirror
3422 	/* 183 */ LS_Line_AlignCeiling,
3423 	/* 184 */ LS_Line_AlignFloor,
3424 	/* 185 */ LS_Sector_SetRotation,
3425 	/* 186 */ LS_Sector_SetCeilingPanning,
3426 	/* 187 */ LS_Sector_SetFloorPanning,
3427 	/* 188 */ LS_Sector_SetCeilingScale,
3428 	/* 189 */ LS_Sector_SetFloorScale,
3429 	/* 190 */ LS_NOP,		// Static_Init
3430 	/* 191 */ LS_SetPlayerProperty,
3431 	/* 192 */ LS_Ceiling_LowerToHighestFloor,
3432 	/* 193 */ LS_Ceiling_LowerInstant,
3433 	/* 194 */ LS_Ceiling_RaiseInstant,
3434 	/* 195 */ LS_Ceiling_CrushRaiseAndStayA,
3435 	/* 196 */ LS_Ceiling_CrushAndRaiseA,
3436 	/* 197 */ LS_Ceiling_CrushAndRaiseSilentA,
3437 	/* 198 */ LS_Ceiling_RaiseByValueTimes8,
3438 	/* 199 */ LS_Ceiling_LowerByValueTimes8,
3439 	/* 200 */ LS_Generic_Floor,
3440 	/* 201 */ LS_Generic_Ceiling,
3441 	/* 202 */ LS_Generic_Door,
3442 	/* 203 */ LS_Generic_Lift,
3443 	/* 204 */ LS_Generic_Stairs,
3444 	/* 205 */ LS_Generic_Crusher,
3445 	/* 206 */ LS_Plat_DownWaitUpStayLip,
3446 	/* 207 */ LS_Plat_PerpetualRaiseLip,
3447 	/* 208 */ LS_TranslucentLine,
3448 	/* 209 */ LS_NOP,		// Transfer_Heights
3449 	/* 210 */ LS_NOP,		// Transfer_FloorLight
3450 	/* 211 */ LS_NOP,		// Transfer_CeilingLight
3451 	/* 212 */ LS_Sector_SetColor,
3452 	/* 213 */ LS_Sector_SetFade,
3453 	/* 214 */ LS_Sector_SetDamage,
3454 	/* 215 */ LS_Teleport_Line,
3455 	/* 216 */ LS_Sector_SetGravity,
3456 	/* 217 */ LS_Stairs_BuildUpDoom,
3457 	/* 218 */ LS_Sector_SetWind,
3458 	/* 219 */ LS_Sector_SetFriction,
3459 	/* 220 */ LS_Sector_SetCurrent,
3460 	/* 221 */ LS_Scroll_Texture_Both,
3461 	/* 222 */ LS_NOP,		// Scroll_Texture_Model
3462 	/* 223 */ LS_Scroll_Floor,
3463 	/* 224 */ LS_Scroll_Ceiling,
3464 	/* 225 */ LS_NOP,		// Scroll_Texture_Offsets
3465 	/* 226 */ LS_ACS_ExecuteAlways,
3466 	/* 227 */ LS_PointPush_SetForce,
3467 	/* 228 */ LS_Plat_RaiseAndStayTx0,
3468 	/* 229 */ LS_Thing_SetGoal,
3469 	/* 230 */ LS_Plat_UpByValueStayTx,
3470 	/* 231 */ LS_Plat_ToggleCeiling,
3471 	/* 232 */ LS_Light_StrobeDoom,
3472 	/* 233 */ LS_Light_MinNeighbor,
3473 	/* 234 */ LS_Light_MaxNeighbor,
3474 	/* 235 */ LS_Floor_TransferTrigger,
3475 	/* 236 */ LS_Floor_TransferNumeric,
3476 	/* 237 */ LS_ChangeCamera,
3477 	/* 238 */ LS_Floor_RaiseToLowestCeiling,
3478 	/* 239 */ LS_Floor_RaiseByValueTxTy,
3479 	/* 240 */ LS_Floor_RaiseByTexture,
3480 	/* 241 */ LS_Floor_LowerToLowestTxTy,
3481 	/* 242 */ LS_Floor_LowerToHighest,
3482 	/* 243 */ LS_Exit_Normal,
3483 	/* 244 */ LS_Exit_Secret,
3484 	/* 245 */ LS_Elevator_RaiseToNearest,
3485 	/* 246 */ LS_Elevator_MoveToFloor,
3486 	/* 247 */ LS_Elevator_LowerToNearest,
3487 	/* 248 */ LS_HealThing,
3488 	/* 249 */ LS_Door_CloseWaitOpen,
3489 	/* 250 */ LS_Floor_Donut,
3490 	/* 251 */ LS_FloorAndCeiling_LowerRaise,
3491 	/* 252 */ LS_Ceiling_RaiseToNearest,
3492 	/* 253 */ LS_Ceiling_LowerToLowest,
3493 	/* 254 */ LS_Ceiling_LowerToFloor,
3494 	/* 255 */ LS_Ceiling_CrushRaiseAndStaySilA
3495 };
3496 
3497 #define DEFINE_SPECIAL(name, num, min, max, mmax) {#name, num, min, max, mmax},
3498 static FLineSpecial LineSpecialNames[] = {
3499 #include "actionspecials.h"
3500 };
3501 const FLineSpecial *LineSpecialsInfo[256];
3502 
lscmp(const void * a,const void * b)3503 static int STACK_ARGS lscmp (const void * a, const void * b)
3504 {
3505 	return stricmp( ((FLineSpecial*)a)->name, ((FLineSpecial*)b)->name);
3506 }
3507 
3508 static struct InitLineSpecials
3509 {
InitLineSpecialsInitLineSpecials3510 	InitLineSpecials()
3511 	{
3512 		qsort(LineSpecialNames, countof(LineSpecialNames), sizeof(FLineSpecial), lscmp);
3513 		for (size_t i = 0; i < countof(LineSpecialNames); ++i)
3514 		{
3515 			assert(LineSpecialsInfo[LineSpecialNames[i].number] == NULL);
3516 			LineSpecialsInfo[LineSpecialNames[i].number] = &LineSpecialNames[i];
3517 		}
3518 	}
3519 } DoInit;
3520 
3521 //==========================================================================
3522 //
3523 // P_FindLineSpecial
3524 //
3525 // Finds a line special and also returns the min and max argument count.
3526 //
3527 //==========================================================================
3528 
P_FindLineSpecial(const char * string,int * min_args,int * max_args)3529 int P_FindLineSpecial (const char *string, int *min_args, int *max_args)
3530 {
3531 	int min = 0, max = countof(LineSpecialNames) - 1;
3532 
3533 	while (min <= max)
3534 	{
3535 		int mid = (min + max) / 2;
3536 		int lexval = stricmp (string, LineSpecialNames[mid].name);
3537 		if (lexval == 0)
3538 		{
3539 			if (min_args != NULL) *min_args = LineSpecialNames[mid].min_args;
3540 			if (max_args != NULL) *max_args = LineSpecialNames[mid].max_args;
3541 			return LineSpecialNames[mid].number;
3542 		}
3543 		else if (lexval > 0)
3544 		{
3545 			min = mid + 1;
3546 		}
3547 		else
3548 		{
3549 			max = mid - 1;
3550 		}
3551 	}
3552 	return 0;
3553 }
3554 
3555 
3556 //==========================================================================
3557 //
3558 // P_ExecuteSpecial
3559 //
3560 //==========================================================================
3561 
P_ExecuteSpecial(int num,struct line_t * line,class AActor * activator,bool backSide,int arg1,int arg2,int arg3,int arg4,int arg5)3562 int P_ExecuteSpecial(int			num,
3563 					 struct line_t	*line,
3564 					 class AActor	*activator,
3565 					 bool			backSide,
3566 					 int			arg1,
3567 					 int			arg2,
3568 					 int			arg3,
3569 					 int			arg4,
3570 					 int			arg5)
3571 {
3572 	if (num >= 0 && num <= 255)
3573 	{
3574 		return LineSpecials[num](line, activator, backSide, arg1, arg2, arg3, arg4, arg5);
3575 	}
3576 	return 0;
3577 }
3578