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(§ors[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 = §ors[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