/ source / blood / src / actor.cpp
actor.cpp
   1  //-------------------------------------------------------------------------
   2  /*
   3  Copyright (C) 2010-2019 EDuke32 developers and contributors
   4  Copyright (C) 2019 Nuke.YKT
   5  
   6  This file is part of NBlood.
   7  
   8  NBlood is free software; you can redistribute it and/or
   9  modify it under the terms of the GNU General Public License version 2
  10  as published by the Free Software Foundation.
  11  
  12  This program is distributed in the hope that it will be useful,
  13  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15  
  16  See the GNU General Public License for more details.
  17  
  18  You should have received a copy of the GNU General Public License
  19  along with this program; if not, write to the Free Software
  20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  21  */
  22  //-------------------------------------------------------------------------
  23  #include <iostream>
  24  
  25  #include "build.h"
  26  #include "pragmas.h"
  27  #include "mmulti.h"
  28  #include "common.h"
  29  #include "common_game.h"
  30  
  31  #include "actor.h"
  32  #include "ai.h"
  33  #include "aibat.h"
  34  #include "aibeast.h"
  35  #include "aiboneel.h"
  36  #include "aiburn.h"
  37  #include "aicaleb.h"
  38  #include "aicerber.h"
  39  #include "aicult.h"
  40  #include "aigarg.h"
  41  #include "aighost.h"
  42  #include "aigilbst.h"
  43  #include "aihand.h"
  44  #include "aihound.h"
  45  #include "aiinnoc.h"
  46  #include "aipod.h"
  47  #include "airat.h"
  48  #include "aispid.h"
  49  #include "aitchern.h"
  50  #include "aizomba.h"
  51  #include "aizombf.h"
  52  #include "blood.h"
  53  #include "callback.h"
  54  #include "config.h"
  55  #include "db.h"
  56  #include "endgame.h"
  57  #include "eventq.h"
  58  #include "fx.h"
  59  #include "gameutil.h"
  60  #include "gib.h"
  61  #include "globals.h"
  62  #include "levels.h"
  63  #include "loadsave.h"
  64  #include "player.h"
  65  #include "seq.h"
  66  #include "sfx.h"
  67  #include "sound.h"
  68  #include "tile.h"
  69  #include "trig.h"
  70  #include "triggers.h"
  71  #include "view.h"
  72  #include "warp.h"
  73  #include "weapon.h"
  74  #ifdef NOONE_EXTENSIONS
  75  #include "aicdud.h"
  76  #include "nnexts.h"
  77  #endif
  78  
  79  VECTORDATA gVectorData[] = {
  80      
  81      // Tine
  82      {
  83          kDamageBullet,
  84          17,
  85          174762,
  86          1152,
  87          10240,
  88          0,
  89          1,
  90          20480,
  91          FX_NONE, FX_NONE, FX_NONE, -1,
  92          FX_43, FX_5, FX_NONE, 500,
  93          FX_NONE, FX_5, FX_NONE, 501,
  94          FX_43, FX_6, FX_NONE, 502,
  95          FX_43, FX_0, FX_NONE, 503,
  96          FX_NONE, FX_4, FX_NONE, -1,
  97          FX_NONE, FX_6, FX_7, 502,
  98          FX_43, FX_6, FX_7, 502,
  99          FX_NONE, FX_NONE, FX_NONE, 503,
 100          FX_43, FX_NONE, FX_NONE, -1,
 101          FX_NONE, FX_6, FX_NONE, 503,
 102          FX_NONE, FX_6, FX_NONE, -1,
 103          FX_NONE, FX_6, FX_NONE, 502,
 104          FX_NONE, FX_NONE, FX_NONE, -1,
 105          FX_NONE, FX_5, FX_NONE, -1,
 106      },
 107     
 108      // Shell
 109      {
 110          kDamageBullet,
 111          4,
 112          65536,
 113          0,
 114          8192,
 115          0,
 116          1,
 117          12288,
 118          FX_NONE, FX_NONE, FX_NONE, -1,
 119          FX_43, FX_5, FX_NONE, -1,
 120          FX_NONE, FX_5, FX_NONE, 501,
 121          FX_43, FX_6, FX_NONE, -1,
 122          FX_43, FX_0, FX_NONE, -1,
 123          FX_NONE, FX_4, FX_NONE, -1,
 124          FX_NONE, FX_6, FX_NONE, -1,
 125          FX_43, FX_6, FX_NONE, -1,
 126          FX_NONE, FX_NONE, FX_NONE, -1,
 127          FX_43, FX_NONE, FX_NONE, -1,
 128          FX_NONE, FX_6, FX_NONE, -1,
 129          FX_NONE, FX_6, FX_NONE, -1,
 130          FX_NONE, FX_6, FX_NONE, -1,
 131          FX_NONE, FX_NONE, FX_NONE, -1,
 132          FX_NONE, FX_5, FX_NONE, -1,
 133      },
 134      
 135      // Bullet
 136      {
 137          kDamageBullet,
 138          7,
 139          21845,
 140          0,
 141          32768,
 142          0,
 143          1,
 144          12288,
 145          FX_NONE, FX_NONE, FX_NONE, -1,
 146          FX_43, FX_5, FX_7, 510,
 147          FX_NONE, FX_5, FX_7, 511,
 148          FX_43, FX_6, FX_NONE, 512,
 149          FX_43, FX_0, FX_NONE, 513,
 150          FX_NONE, FX_4, FX_NONE, -1,
 151          FX_NONE, FX_6, FX_7, 512,
 152          FX_43, FX_6, FX_7, 512,
 153          FX_NONE, FX_NONE, FX_NONE, 513,
 154          FX_43, FX_NONE, FX_NONE, 513,
 155          FX_NONE, FX_6, FX_NONE, 513,
 156          FX_NONE, FX_6, FX_NONE, 513,
 157          FX_NONE, FX_6, FX_NONE, 513,
 158          FX_NONE, FX_NONE, FX_NONE, -1,
 159          FX_NONE, FX_5, FX_NONE, -1,
 160  
 161      },
 162      
 163      // Tommy AP
 164      {
 165          kDamageBullet,
 166          20,
 167          65536,
 168          0,
 169          16384,
 170          0,
 171          1,
 172          20480,
 173          FX_NONE, FX_NONE, FX_NONE, -1,
 174          FX_43, FX_5, FX_7, 510,
 175          FX_NONE, FX_5, FX_7, 511,
 176          FX_43, FX_6, FX_NONE, 512,
 177          FX_43, FX_0, FX_NONE, 513,
 178          FX_NONE, FX_4, FX_NONE, -1,
 179          FX_NONE, FX_6, FX_7, 512,
 180          FX_43, FX_6, FX_7, 512,
 181          FX_NONE, FX_NONE, FX_NONE, 513,
 182          FX_43, FX_NONE, FX_NONE, 513,
 183          FX_NONE, FX_6, FX_NONE, 513,
 184          FX_NONE, FX_6, FX_NONE, 513,
 185          FX_NONE, FX_6, FX_NONE, 513,
 186          FX_NONE, FX_NONE, FX_NONE, -1,
 187          FX_NONE, FX_5, FX_NONE, -1,
 188      },
 189      
 190      // Shell AP
 191      {
 192          kDamageBullet,
 193          6,
 194          87381,
 195          0,
 196          12288,
 197          0,
 198          1,
 199          6144,
 200          FX_NONE, FX_NONE, FX_NONE, -1,
 201          FX_43, FX_5, FX_NONE, -1,
 202          FX_NONE, FX_5, FX_NONE, 501,
 203          FX_43, FX_6, FX_NONE, -1,
 204          FX_43, FX_0, FX_NONE, -1,
 205          FX_NONE, FX_4, FX_NONE, -1,
 206          FX_NONE, FX_6, FX_NONE, -1,
 207          FX_43, FX_6, FX_NONE, -1,
 208          FX_NONE, FX_NONE, FX_NONE, -1,
 209          FX_43, FX_NONE, FX_NONE, -1,
 210          FX_NONE, FX_6, FX_NONE, -1,
 211          FX_NONE, FX_6, FX_NONE, -1,
 212          FX_NONE, FX_6, FX_NONE, -1,
 213          FX_NONE, FX_NONE, FX_NONE, -1,
 214          FX_NONE, FX_5, FX_NONE, -1,
 215      },
 216      
 217      // Tommy regular
 218      {
 219          kDamageBullet,
 220          12,
 221          65536,
 222          0,
 223          16384,
 224          0,
 225          1,
 226          12288,
 227          FX_NONE, FX_NONE, FX_NONE, -1,
 228          FX_43, FX_5, FX_7, 510,
 229          FX_NONE, FX_5, FX_7, 511,
 230          FX_43, FX_6, FX_NONE, 512,
 231          FX_43, FX_0, FX_NONE, 513,
 232          FX_NONE, FX_4, FX_NONE, -1,
 233          FX_NONE, FX_6, FX_7, 512,
 234          FX_43, FX_6, FX_7, 512,
 235          FX_NONE, FX_NONE, FX_NONE, 513,
 236          FX_43, FX_NONE, FX_NONE, 513,
 237          FX_NONE, FX_6, FX_NONE, 513,
 238          FX_NONE, FX_6, FX_NONE, 513,
 239          FX_NONE, FX_6, FX_NONE, 513,
 240          FX_NONE, FX_NONE, FX_NONE, -1,
 241          FX_NONE, FX_5, FX_NONE, -1,
 242      },
 243      
 244      // Bat bite
 245      {
 246          kDamageBullet,
 247          4,
 248          0,
 249          921,
 250          0,
 251          0,
 252          1,
 253          4096,
 254          FX_NONE, FX_NONE, FX_NONE, -1,
 255          FX_NONE, FX_5, FX_NONE, -1,
 256          FX_NONE, FX_5, FX_NONE, -1,
 257          FX_NONE, FX_6, FX_NONE, 502,
 258          FX_NONE, FX_0, FX_NONE, 503,
 259          FX_NONE, FX_4, FX_NONE, -1,
 260          FX_NONE, FX_6, FX_NONE, 502,
 261          FX_NONE, FX_6, FX_NONE, 502,
 262          FX_NONE, FX_NONE, FX_NONE, -1,
 263          FX_NONE, FX_NONE, FX_NONE, -1,
 264          FX_NONE, FX_NONE, FX_NONE, -1,
 265          FX_NONE, FX_NONE, FX_NONE, -1,
 266          FX_NONE, FX_NONE, FX_NONE, 502,
 267          FX_NONE, FX_NONE, FX_NONE, -1,
 268          FX_NONE, FX_NONE, FX_NONE, -1,
 269      },
 270      
 271      // Eel bite
 272      {
 273          kDamageBullet,
 274          12,
 275          0,
 276          1177,
 277          0,
 278          0,
 279          0,
 280          0,
 281          FX_NONE, FX_NONE, FX_NONE, -1,
 282          FX_NONE, FX_5, FX_NONE, 500,
 283          FX_NONE, FX_5, FX_NONE, 501,
 284          FX_NONE, FX_6, FX_NONE, 502,
 285          FX_NONE, FX_0, FX_NONE, 503,
 286          FX_NONE, FX_4, FX_NONE, -1,
 287          FX_NONE, FX_6, FX_NONE, 502,
 288          FX_NONE, FX_6, FX_NONE, 502,
 289          FX_NONE, FX_NONE, FX_NONE, -1,
 290          FX_NONE, FX_NONE, FX_NONE, -1,
 291          FX_NONE, FX_NONE, FX_NONE, -1,
 292          FX_NONE, FX_NONE, FX_NONE, -1,
 293          FX_NONE, FX_NONE, FX_NONE, 502,
 294          FX_NONE, FX_NONE, FX_NONE, -1,
 295          FX_NONE, FX_NONE, FX_NONE, -1,
 296      },
 297      
 298      // Gill bite
 299      {
 300          kDamageBullet,
 301          9,
 302          0,
 303          1177,
 304          0,
 305          0,
 306          0,
 307          0,
 308          FX_NONE, FX_NONE, FX_NONE, -1,
 309          FX_NONE, FX_5, FX_NONE, 500,
 310          FX_NONE, FX_5, FX_NONE, 501,
 311          FX_NONE, FX_6, FX_NONE, 502,
 312          FX_NONE, FX_0, FX_NONE, 503,
 313          FX_NONE, FX_4, FX_NONE, -1,
 314          FX_NONE, FX_6, FX_NONE, 502,
 315          FX_NONE, FX_6, FX_NONE, 502,
 316          FX_NONE, FX_NONE, FX_NONE, -1,
 317          FX_NONE, FX_NONE, FX_NONE, -1,
 318          FX_NONE, FX_NONE, FX_NONE, -1,
 319          FX_NONE, FX_NONE, FX_NONE, -1,
 320          FX_NONE, FX_NONE, FX_NONE, 502,
 321          FX_NONE, FX_NONE, FX_NONE, -1,
 322          FX_NONE, FX_NONE, FX_NONE, -1,
 323      },
 324      
 325      // Beast slash
 326      {
 327          kDamageExplode,
 328          50,
 329          43690,
 330          1024,
 331          8192,
 332          0,
 333          4,
 334          32768,
 335          FX_NONE, FX_NONE, FX_NONE, -1,
 336          FX_NONE, FX_5, FX_NONE, 500,
 337          FX_NONE, FX_5, FX_NONE, 501,
 338          FX_NONE, FX_6, FX_NONE, 502,
 339          FX_NONE, FX_0, FX_NONE, 503,
 340          FX_NONE, FX_4, FX_NONE, -1,
 341          FX_NONE, FX_6, FX_NONE, 502,
 342          FX_NONE, FX_6, FX_NONE, 502,
 343          FX_NONE, FX_NONE, FX_NONE, -1,
 344          FX_NONE, FX_NONE, FX_NONE, -1,
 345          FX_NONE, FX_NONE, FX_NONE, -1,
 346          FX_NONE, FX_NONE, FX_NONE, -1,
 347          FX_NONE, FX_NONE, FX_NONE, 502,
 348          FX_NONE, FX_NONE, FX_NONE, -1,
 349          FX_NONE, FX_NONE, FX_NONE, -1,
 350      },
 351      
 352      // Axe
 353      {
 354          kDamageBullet,
 355          18,
 356          436906,
 357          1024,
 358          16384,
 359          0,
 360          2,
 361          20480,
 362          FX_NONE, FX_NONE, FX_NONE, -1,
 363          FX_NONE, FX_5, FX_NONE, 500,
 364          FX_NONE, FX_5, FX_NONE, 501,
 365          FX_NONE, FX_6, FX_NONE, 502,
 366          FX_NONE, FX_0, FX_NONE, 503,
 367          FX_NONE, FX_4, FX_NONE, -1,
 368          FX_NONE, FX_6, FX_NONE, 502,
 369          FX_NONE, FX_6, FX_NONE, 502,
 370          FX_NONE, FX_NONE, FX_NONE, -1,
 371          FX_NONE, FX_NONE, FX_NONE, -1,
 372          FX_NONE, FX_6, FX_NONE, -1,
 373          FX_NONE, FX_6, FX_NONE, -1,
 374          FX_NONE, FX_6, FX_NONE, 502,
 375          FX_NONE, FX_NONE, FX_NONE, -1,
 376          FX_NONE, FX_5, FX_NONE, -1,
 377      },
 378      
 379      // Cleaver
 380      {
 381          kDamageBullet,
 382          9,
 383          218453,
 384          1024,
 385          0,
 386          0,
 387          1,
 388          24576,
 389          FX_NONE, FX_NONE, FX_NONE, -1,
 390          FX_NONE, FX_5, FX_NONE, 500,
 391          FX_NONE, FX_5, FX_NONE, 501,
 392          FX_NONE, FX_6, FX_NONE, 502,
 393          FX_NONE, FX_0, FX_NONE, 503,
 394          FX_NONE, FX_4, FX_NONE, -1,
 395          FX_NONE, FX_6, FX_NONE, 502,
 396          FX_NONE, FX_6, FX_NONE, 502,
 397          FX_NONE, FX_NONE, FX_NONE, -1,
 398          FX_NONE, FX_NONE, FX_NONE, -1,
 399          FX_NONE, FX_6, FX_NONE, -1,
 400          FX_NONE, FX_6, FX_NONE, -1,
 401          FX_NONE, FX_6, FX_NONE, 502,
 402          FX_NONE, FX_NONE, FX_NONE, -1,
 403          FX_NONE, FX_5, FX_NONE, -1,
 404      },
 405      
 406      // Phantasm slash
 407      {
 408          kDamageBullet,
 409          20,
 410          436906,
 411          1024,
 412          16384,
 413          0,
 414          3,
 415          24576,
 416          FX_NONE, FX_NONE, FX_NONE, -1,
 417          FX_NONE, FX_5, FX_NONE, 500,
 418          FX_NONE, FX_5, FX_NONE, 501,
 419          FX_NONE, FX_6, FX_NONE, 502,
 420          FX_NONE, FX_0, FX_NONE, 503,
 421          FX_NONE, FX_4, FX_NONE, -1,
 422          FX_NONE, FX_6, FX_NONE, 502,
 423          FX_NONE, FX_6, FX_NONE, 502,
 424          FX_NONE, FX_NONE, FX_NONE, -1,
 425          FX_NONE, FX_NONE, FX_NONE, -1,
 426          FX_NONE, FX_6, FX_NONE, -1,
 427          FX_NONE, FX_6, FX_NONE, -1,
 428          FX_NONE, FX_6, FX_NONE, 502,
 429          FX_NONE, FX_NONE, FX_NONE, -1,
 430          FX_NONE, FX_5, FX_NONE, -1,
 431      },
 432  
 433      // Gargoyle Slash
 434      {
 435          kDamageBullet,
 436          16,
 437          218453,
 438          1024,
 439          8192,
 440          0,
 441          4,
 442          20480,
 443          FX_NONE, FX_NONE, FX_NONE, -1,
 444          FX_NONE, FX_5, FX_NONE, 500,
 445          FX_NONE, FX_5, FX_NONE, 501,
 446          FX_NONE, FX_6, FX_NONE, 502,
 447          FX_NONE, FX_0, FX_NONE, 503,
 448          FX_NONE, FX_4, FX_NONE, -1,
 449          FX_NONE, FX_6, FX_NONE, 502,
 450          FX_NONE, FX_6, FX_NONE, 502,
 451          FX_NONE, FX_NONE, FX_NONE, -1,
 452          FX_NONE, FX_NONE, FX_NONE, -1,
 453          FX_NONE, FX_6, FX_NONE, -1,
 454          FX_NONE, FX_6, FX_NONE, -1,
 455          FX_NONE, FX_6, FX_NONE, 502,
 456          FX_NONE, FX_NONE, FX_NONE, -1,
 457          FX_NONE, FX_5, FX_NONE, -1,
 458      },
 459  
 460      // Cerberus bite
 461      {
 462          kDamageBullet,
 463          19,
 464          218453,
 465          614,
 466          8192,
 467          0,
 468          2,
 469          24576,
 470          FX_NONE, FX_NONE, FX_NONE, -1,
 471          FX_NONE, FX_5, FX_NONE, 500,
 472          FX_NONE, FX_5, FX_NONE, 501,
 473          FX_NONE, FX_6, FX_NONE, 502,
 474          FX_NONE, FX_0, FX_NONE, 503,
 475          FX_NONE, FX_4, FX_NONE, -1,
 476          FX_NONE, FX_6, FX_NONE, 502,
 477          FX_NONE, FX_6, FX_NONE, 502,
 478          FX_NONE, FX_NONE, FX_NONE, -1,
 479          FX_NONE, FX_NONE, FX_NONE, -1,
 480          FX_NONE, FX_NONE, FX_NONE, -1,
 481          FX_NONE, FX_NONE, FX_NONE, -1,
 482          FX_NONE, FX_NONE, FX_NONE, 502,
 483          FX_NONE, FX_NONE, FX_NONE, -1,
 484          FX_NONE, FX_NONE, FX_NONE, -1,
 485      },
 486  
 487      // Hound bite
 488      {
 489          kDamageBullet,
 490          10,
 491          218453,
 492          614,
 493          8192,
 494          0,
 495          2,
 496          24576,
 497          FX_NONE, FX_NONE, FX_NONE, -1,
 498          FX_NONE, FX_5, FX_NONE, 500,
 499          FX_NONE, FX_5, FX_NONE, 501,
 500          FX_NONE, FX_6, FX_NONE, 502,
 501          FX_NONE, FX_0, FX_NONE, 503,
 502          FX_NONE, FX_4, FX_NONE, -1,
 503          FX_NONE, FX_6, FX_NONE, 502,
 504          FX_NONE, FX_6, FX_NONE, 502,
 505          FX_NONE, FX_NONE, FX_NONE, -1,
 506          FX_NONE, FX_NONE, FX_NONE, -1,
 507          FX_NONE, FX_NONE, FX_NONE, -1,
 508          FX_NONE, FX_NONE, FX_NONE, -1,
 509          FX_NONE, FX_NONE, FX_NONE, 502,
 510          FX_NONE, FX_NONE, FX_NONE, -1,
 511          FX_NONE, FX_NONE, FX_NONE, -1,
 512      },
 513  
 514      // Rat bite
 515      {
 516          kDamageBullet,
 517          4,
 518          0,
 519          921,
 520          0,
 521          0,
 522          1,
 523          24576,
 524          FX_NONE, FX_NONE, FX_NONE, -1,
 525          FX_NONE, FX_5, FX_NONE, -1,
 526          FX_NONE, FX_5, FX_NONE, -1,
 527          FX_NONE, FX_6, FX_NONE, 502,
 528          FX_NONE, FX_0, FX_NONE, 503,
 529          FX_NONE, FX_4, FX_NONE, -1,
 530          FX_NONE, FX_6, FX_NONE, 502,
 531          FX_NONE, FX_6, FX_NONE, 502,
 532          FX_NONE, FX_NONE, FX_NONE, -1,
 533          FX_NONE, FX_NONE, FX_NONE, -1,
 534          FX_NONE, FX_NONE, FX_NONE, -1,
 535          FX_NONE, FX_NONE, FX_NONE, -1,
 536          FX_NONE, FX_NONE, FX_NONE, 502,
 537          FX_NONE, FX_NONE, FX_NONE, -1,
 538          FX_NONE, FX_NONE, FX_NONE, -1,
 539      },
 540  
 541      // Spider bite
 542      {
 543          kDamageBullet,
 544          8,
 545          0,
 546          614,
 547          0,
 548          0,
 549          1,
 550          24576,
 551          FX_NONE, FX_NONE, FX_NONE, -1,
 552          FX_NONE, FX_5, FX_NONE, 500,
 553          FX_NONE, FX_5, FX_NONE, 501,
 554          FX_NONE, FX_6, FX_NONE, 502,
 555          FX_NONE, FX_0, FX_NONE, 503,
 556          FX_NONE, FX_4, FX_NONE, -1,
 557          FX_NONE, FX_6, FX_NONE, 502,
 558          FX_NONE, FX_6, FX_NONE, 502,
 559          FX_NONE, FX_NONE, FX_NONE, -1,
 560          FX_NONE, FX_NONE, FX_NONE, -1,
 561          FX_NONE, FX_NONE, FX_NONE, -1,
 562          FX_NONE, FX_NONE, FX_NONE, -1,
 563          FX_NONE, FX_NONE, FX_NONE, 502,
 564          FX_NONE, FX_NONE, FX_NONE, -1,
 565          FX_NONE, FX_NONE, FX_NONE, -1,
 566      },
 567  
 568      // Unk
 569      {
 570          kDamageBullet,
 571          9,
 572          0,
 573          512,
 574          0,
 575          0,
 576          0,
 577          0,
 578          FX_NONE, FX_NONE, FX_NONE, -1,
 579          FX_NONE, FX_5, FX_NONE, 500,
 580          FX_NONE, FX_5, FX_NONE, 501,
 581          FX_NONE, FX_6, FX_NONE, 502,
 582          FX_NONE, FX_0, FX_NONE, 503,
 583          FX_NONE, FX_4, FX_NONE, -1,
 584          FX_NONE, FX_6, FX_NONE, 502,
 585          FX_NONE, FX_6, FX_NONE, 502,
 586          FX_NONE, FX_NONE, FX_NONE, -1,
 587          FX_NONE, FX_NONE, FX_NONE, -1,
 588          FX_NONE, FX_NONE, FX_NONE, -1,
 589          FX_NONE, FX_NONE, FX_NONE, -1,
 590          FX_NONE, FX_NONE, FX_NONE, 502,
 591          FX_NONE, FX_NONE, FX_NONE, -1,
 592          FX_NONE, FX_NONE, FX_NONE, -1,
 593      },
 594      
 595      {
 596          (DAMAGE_TYPE)-1,
 597          0,
 598          0,
 599          2560,
 600          0,
 601          0,
 602          0,
 603          0,
 604          FX_NONE, FX_NONE, FX_NONE, -1,
 605          FX_NONE, FX_34, FX_35, -1,
 606          FX_NONE, FX_34, FX_35, -1,
 607          FX_NONE, FX_34, FX_35, -1,
 608          FX_NONE, FX_34, FX_35, -1,
 609          FX_NONE, FX_NONE, FX_NONE, -1,
 610          FX_NONE, FX_34, FX_35, -1,
 611          FX_NONE, FX_34, FX_35, -1,
 612          FX_NONE, FX_34, FX_35, -1,
 613          FX_NONE, FX_34, FX_35, -1,
 614          FX_NONE, FX_NONE, FX_NONE, -1,
 615          FX_NONE, FX_NONE, FX_NONE, -1,
 616          FX_NONE, FX_NONE, FX_NONE, -1,
 617          FX_NONE, FX_NONE, FX_NONE, -1,
 618          FX_NONE, FX_NONE, FX_NONE, -1,
 619      },
 620  
 621      // Tchernobog burn vector
 622      {
 623          kDamageBurn,
 624          2,
 625          0,
 626          0,
 627          0,
 628          15,
 629          0,
 630          0,
 631          FX_NONE, FX_NONE, FX_NONE, -1,
 632          FX_NONE, FX_NONE, FX_NONE, -1,
 633          FX_NONE, FX_NONE, FX_NONE, -1,
 634          FX_NONE, FX_NONE, FX_NONE, -1,
 635          FX_NONE, FX_NONE, FX_NONE, -1,
 636          FX_NONE, FX_NONE, FX_NONE, -1,
 637          FX_NONE, FX_NONE, FX_NONE, -1,
 638          FX_NONE, FX_NONE, FX_NONE, -1,
 639          FX_NONE, FX_NONE, FX_NONE, -1,
 640          FX_NONE, FX_NONE, FX_NONE, -1,
 641          FX_NONE, FX_NONE, FX_NONE, -1,
 642          FX_NONE, FX_NONE, FX_NONE, -1,
 643          FX_NONE, FX_NONE, FX_NONE, -1,
 644          FX_NONE, FX_NONE, FX_NONE, -1,
 645          FX_NONE, FX_NONE, FX_NONE, -1,
 646      },
 647  
 648      // Vodoo 1.0 vector
 649      {
 650          kDamageSpirit,
 651          25,
 652          0,
 653          0,
 654          0,
 655          0,
 656          0,
 657          0,
 658          FX_NONE, FX_NONE, FX_NONE, -1,
 659          FX_NONE, FX_NONE, FX_NONE, -1,
 660          FX_NONE, FX_NONE, FX_NONE, -1,
 661          FX_NONE, FX_NONE, FX_NONE, -1,
 662          FX_NONE, FX_NONE, FX_NONE, -1,
 663          FX_NONE, FX_NONE, FX_NONE, -1,
 664          FX_NONE, FX_NONE, FX_NONE, -1,
 665          FX_NONE, FX_NONE, FX_NONE, -1,
 666          FX_NONE, FX_NONE, FX_NONE, -1,
 667          FX_NONE, FX_NONE, FX_NONE, -1,
 668          FX_NONE, FX_NONE, FX_NONE, -1,
 669          FX_NONE, FX_NONE, FX_NONE, -1,
 670          FX_NONE, FX_NONE, FX_NONE, -1,
 671          FX_NONE, FX_NONE, FX_NONE, -1,
 672          FX_NONE, FX_NONE, FX_NONE, -1,
 673      },
 674  
 675      // 22 kVectorGenDudePunch
 676      {
 677      kDamageFall, 
 678          37, 
 679          874762, 
 680          620, 
 681          0, 
 682          0, 
 683          0, 
 684          0,
 685          FX_NONE, FX_NONE, FX_NONE, -1,
 686          FX_NONE, FX_NONE, FX_NONE, 357,
 687          FX_NONE, FX_NONE, FX_NONE, 357,
 688          FX_NONE, FX_NONE, FX_NONE, 357,
 689          FX_NONE, FX_NONE, FX_NONE, 357,
 690          FX_NONE, FX_NONE, FX_NONE, 357,
 691          FX_NONE, FX_NONE, FX_NONE, 357,
 692          FX_NONE, FX_NONE, FX_NONE, 357,
 693          FX_NONE, FX_NONE, FX_NONE, 357,
 694          FX_NONE, FX_NONE, FX_NONE, 357,
 695          FX_NONE, FX_NONE, FX_NONE, 357,
 696          FX_NONE, FX_NONE, FX_NONE, 357,
 697          FX_NONE, FX_NONE, FX_NONE, 357,
 698          FX_NONE, FX_NONE, FX_NONE, 357,
 699          FX_NONE, FX_NONE, FX_NONE, 357,
 700      },
 701  };
 702  
 703  ITEMDATA gItemData[] = {
 704      {
 705          0,
 706          2552,
 707          (char)-8,
 708          0,
 709          32,
 710          32,
 711          -1,
 712      },
 713      {
 714          0,
 715          2553,
 716          (char)-8,
 717          0,
 718          32,
 719          32,
 720          -1,
 721      },
 722      {
 723          0,
 724          2554,
 725          (char)-8,
 726          0,
 727          32,
 728          32,
 729          -1,
 730      },
 731      {
 732          0,
 733          2555,
 734          (char)-8,
 735          0,
 736          32,
 737          32,
 738          -1,
 739      },
 740      {
 741          0,
 742          2556,
 743          (char)-8,
 744          0,
 745          32,
 746          32,
 747          -1,
 748      },
 749      {
 750          0,
 751          2557,
 752          (char)-8,
 753          0,
 754          32,
 755          32,
 756          -1,
 757      },
 758      {
 759          0,
 760          2558,
 761          (char)-8,
 762          0,
 763          32,
 764          32,
 765          -1,
 766      },
 767      {
 768          0,
 769          519,
 770          (char)-8,
 771          0,
 772          48,
 773          48,
 774          0,
 775      },
 776      {
 777          0,
 778          822,
 779          (char)-8,
 780          0,
 781          40,
 782          40,
 783          -1,
 784      },
 785      {
 786          0,
 787          2169,
 788          (char)-8,
 789          0,
 790          40,
 791          40,
 792          -1,
 793      },
 794      {
 795          0,
 796          2433,
 797          (char)-8,
 798          0,
 799          40,
 800          40,
 801          -1,
 802      },
 803      {
 804          0,
 805          517,
 806          (char)-8,
 807          0,
 808          40,
 809          40,
 810          -1,
 811      },
 812      {
 813          0,
 814          783,
 815          (char)-8,
 816          0,
 817          40,
 818          40,
 819          -1,
 820      },
 821      {
 822          0,
 823          896,
 824          (char)-8,
 825          0,
 826          40,
 827          40,
 828          -1,
 829      },
 830      {
 831          0,
 832          825,
 833          (char)-8,
 834          0,
 835          40,
 836          40,
 837          -1,
 838      },
 839      {
 840          0,
 841          827,
 842          (char)-8,
 843          0,
 844          40,
 845          40,
 846          4,
 847      },
 848      {
 849          0,
 850          828,
 851          (char)-8,
 852          0,
 853          40,
 854          40,
 855          -1,
 856      },
 857      {
 858          0,
 859          829,
 860          (char)-8,
 861          0,
 862          40,
 863          40,
 864          -1,
 865      },
 866      {
 867          0,
 868          830,
 869          (char)-8,
 870          0,
 871          80,
 872          64,
 873          1,
 874      },
 875      {
 876          0,
 877          831,
 878          (char)-8,
 879          0,
 880          40,
 881          40,
 882          -1,
 883      },
 884      {
 885          0,
 886          863,
 887          (char)-8,
 888          0,
 889          40,
 890          40,
 891          -1,
 892      },
 893      {
 894          0,
 895          760,
 896          (char)-8,
 897          0,
 898          40,
 899          40,
 900          2,
 901      },
 902      {
 903          0,
 904          836,
 905          (char)-8,
 906          0,
 907          40,
 908          40,
 909          -1,
 910      },
 911      {
 912          0,
 913          851,
 914          (char)-8,
 915          0,
 916          40,
 917          40,
 918          -1,
 919      },
 920      {
 921          0,
 922          2428,
 923          (char)-8,
 924          0,
 925          40,
 926          40,
 927          -1,
 928      },
 929      {
 930          0,
 931          839,
 932          (char)-8,
 933          0,
 934          40,
 935          40,
 936          3,
 937      },
 938      {
 939          0,
 940          768,
 941          (char)-8,
 942          0,
 943          64,
 944          64,
 945          -1,
 946      },
 947      {
 948          0,
 949          840,
 950          (char)-8,
 951          0,
 952          48,
 953          48,
 954          -1,
 955      },
 956      {
 957          0,
 958          841,
 959          (char)-8,
 960          0,
 961          48,
 962          48,
 963          -1,
 964      },
 965      {
 966          0,
 967          842,
 968          (char)-8,
 969          0,
 970          48,
 971          48,
 972          -1,
 973      },
 974      {
 975          0,
 976          843,
 977          (char)-8,
 978          0,
 979          48,
 980          48,
 981          -1,
 982      },
 983      {
 984          0,
 985          683,
 986          (char)-8,
 987          0,
 988          40,
 989          40,
 990          -1,
 991      },
 992      {
 993          0,
 994          521,
 995          (char)-8,
 996          0,
 997          40,
 998          40,
 999          -1,
1000      },
1001      {
1002          0,
1003          604,
1004          (char)-8,
1005          0,
1006          40,
1007          40,
1008          -1,
1009      },
1010      {
1011          0,
1012          520,
1013          (char)-8,
1014          0,
1015          40,
1016          40,
1017          -1,
1018      },
1019      {
1020          0,
1021          803,
1022          (char)-8,
1023          0,
1024          40,
1025          40,
1026          -1,
1027      },
1028      {
1029          0,
1030          518,
1031          (char)-8,
1032          0,
1033          40,
1034          40,
1035          -1,
1036      },
1037      {
1038          0,
1039          522,
1040          (char)-8,
1041          0,
1042          40,
1043          40,
1044          -1,
1045      },
1046      {
1047          0,
1048          523,
1049          (char)-8,
1050          0,
1051          40,
1052          40,
1053          -1,
1054      },
1055      {
1056          0,
1057          837,
1058          (char)-8,
1059          0,
1060          80,
1061          64,
1062          -1,
1063      },
1064      {
1065          0,
1066          2628,
1067          (char)-8,
1068          0,
1069          64,
1070          64,
1071          -1,
1072      },
1073      {
1074          0,
1075          2586,
1076          (char)-8,
1077          0,
1078          64,
1079          64,
1080          -1,
1081      },
1082      {
1083          0,
1084          2578,
1085          (char)-8,
1086          0,
1087          64,
1088          64,
1089          -1,
1090      },
1091      {
1092          0,
1093          2602,
1094          (char)-8,
1095          0,
1096          64,
1097          64,
1098          -1,
1099      },
1100      {
1101          0,
1102          2594,
1103          (char)-8,
1104          0,
1105          64,
1106          64,
1107          -1,
1108      },
1109      {
1110          0,
1111          753,
1112          (char)-8,
1113          0,
1114          64,
1115          64,
1116          -1,
1117      },
1118      {
1119          0,
1120          753,
1121          (char)-8,
1122          7,
1123          64,
1124          64,
1125          -1,
1126      },
1127      {
1128          0,
1129          3558,
1130          (char)-128,
1131          0,
1132          64,
1133          64,
1134          -1,
1135      },
1136      {
1137          0,
1138          3558,
1139          (char)-128,
1140          7,
1141          64,
1142          64,
1143          -1,
1144      }
1145  };
1146  
1147  AMMOITEMDATA gAmmoItemData[] = {
1148      {
1149          0,
1150          618,
1151          (char)-8,
1152          0,
1153          40,
1154          40,
1155          480,
1156          6,
1157          7
1158      },
1159      {
1160          0,
1161          589,
1162          (char)-8,
1163          0,
1164          48,
1165          48,
1166          1,
1167          5,
1168          6
1169      },
1170      {
1171          0,
1172          589,
1173          (char)-8,
1174          0,
1175          48,
1176          48,
1177          1,
1178          5,
1179          6
1180      },
1181      {
1182          0,
1183          809,
1184          (char)-8,
1185          0,
1186          48,
1187          48,
1188          5,
1189          5,
1190          6
1191      },
1192      {
1193          0,
1194          811,
1195          (char)-8,
1196          0,
1197          48,
1198          48,
1199          1,
1200          10,
1201          11
1202      },
1203      {
1204          0,
1205          810,
1206          (char)-8,
1207          0,
1208          48,
1209          48,
1210          1,
1211          11,
1212          12
1213      },
1214      {
1215          0,
1216          820,
1217          (char)-8,
1218          0,
1219          24,
1220          24,
1221          10,
1222          8,
1223          0
1224      },
1225      {
1226          0,
1227          619,
1228          (char)-8,
1229          0,
1230          48,
1231          48,
1232          4,
1233          2,
1234          0
1235      },
1236      {
1237          0,
1238          812,
1239          (char)-8,
1240          0,
1241          48,
1242          48,
1243          15,
1244          2,
1245          0
1246      },
1247      {
1248          0,
1249          813,
1250          (char)-8,
1251          0,
1252          48,
1253          48,
1254          15,
1255          3,
1256          0
1257      },
1258      {
1259          0,
1260          525,
1261          (char)-8,
1262          0,
1263          48,
1264          48,
1265          100,
1266          9,
1267          10
1268      },
1269      {
1270          0,
1271          814,
1272          (char)-8,
1273          0,
1274          48,
1275          48,
1276          15,
1277          255,
1278          0
1279      },
1280      {
1281          0,
1282          817,
1283          (char)-8,
1284          0,
1285          48,
1286          48,
1287          100,
1288          3,
1289          0
1290      },
1291      {
1292          0,
1293          548,
1294          (char)-8,
1295          0,
1296          24,
1297          24,
1298          32,
1299          7,
1300          0
1301      },
1302      {
1303          0,
1304          0,
1305          (char)-8,
1306          0,
1307          48,
1308          48,
1309          6,
1310          255,
1311          0
1312      },
1313      {
1314          0,
1315          0,
1316          (char)-8,
1317          0,
1318          48,
1319          48,
1320          6,
1321          255,
1322          0
1323      },
1324      {
1325          0,
1326          816,
1327          (char)-8,
1328          0,
1329          48,
1330          48,
1331          8,
1332          1,
1333          0
1334      },
1335      {
1336          0,
1337          818,
1338          (char)-8,
1339          0,
1340          48,
1341          48,
1342          8,
1343          255,
1344          0
1345      },
1346      {
1347          0,
1348          819,
1349          (char)-8,
1350          0,
1351          48,
1352          48,
1353          8,
1354          255,
1355          0
1356      },
1357      {
1358          0,
1359          801,
1360          (char)-8,
1361          0,
1362          48,
1363          48,
1364          6,
1365          4,
1366          0
1367      },
1368      {
1369          0,
1370          0,
1371          0,
1372          0,
1373          0,
1374          0,
1375          0,
1376          0,
1377          0
1378      },
1379  };
1380  
1381  WEAPONITEMDATA gWeaponItemData[] = {
1382      {
1383          0,
1384          -1,
1385          (char)0,
1386          0,
1387          0,
1388          0,
1389          0,
1390          -1,
1391          0
1392      },
1393      {
1394          0,
1395          559,
1396          (char)-8,
1397          0,
1398          48,
1399          48,
1400          3,
1401          2,
1402          8
1403      },
1404      {
1405          0,
1406          558,
1407          (char)-8,
1408          0,
1409          48,
1410          48,
1411          4,
1412          3,
1413          50
1414      },
1415      {
1416          0,
1417          524,
1418          (char)-8,
1419          0,
1420          48,
1421          48,
1422          2,
1423          1,
1424          9
1425      },
1426      {
1427          0,
1428          525,
1429          (char)-8,
1430          0,
1431          48,
1432          48,
1433          10,
1434          9,
1435          100
1436      },
1437      {
1438          0,
1439          539,
1440          (char)-8,
1441          0,
1442          48,
1443          48,
1444          8,
1445          7,
1446          64
1447      },
1448      {
1449          0,
1450          526,
1451          (char)-8,
1452          0,
1453          48,
1454          48,
1455          5,
1456          4,
1457          6
1458      },
1459      {
1460          0,
1461          -1,
1462          (char)0,
1463          0,
1464          0,
1465          0,
1466          1,
1467          -1,
1468          0
1469      },
1470      {
1471          0,
1472          618,
1473          (char)-8,
1474          0,
1475          48,
1476          48,
1477          7,
1478          6,
1479          480
1480      },
1481      {
1482          0,
1483          589,
1484          (char)-8,
1485          0,
1486          48,
1487          48,
1488          6,
1489          5,
1490          1
1491      },
1492      {
1493          0,
1494          800,
1495          (char)-8,
1496          0,
1497          48,
1498          48,
1499          9,
1500          8,
1501          35
1502      }
1503  };
1504  
1505  MissileType missileInfo[] = {
1506      // Cleaver
1507      {
1508          2138,
1509          978670,
1510          512,
1511          40,
1512          40,
1513          (char)-16,
1514          16,
1515      },
1516      // Regular flare
1517      {
1518          2424,
1519          3145728,
1520          0,
1521          32,
1522          32,
1523          (char)-128,
1524          32,
1525      },
1526      // Tesla alt
1527      {
1528          3056,
1529          2796202,
1530          0,
1531          32,
1532          32,
1533          (char)-128,
1534          32,
1535      },
1536      // Flare alt
1537      {
1538          2424,
1539          2446677,
1540          0,
1541          32,
1542          32,
1543          (char)-128,
1544          4,
1545      },
1546      // Spray flame
1547      {
1548          0,
1549          1118481,
1550          0,
1551          24,
1552          24,
1553          (char)-128,
1554          16,
1555      },
1556      // Fireball
1557      {
1558          0,
1559          1118481,
1560          0,
1561          32,
1562          32,
1563          (char)-128,
1564          32,
1565      },
1566      // Tesla regular
1567      {
1568          2130,
1569          2796202,
1570          0,
1571          32,
1572          32,
1573          (char)-128,
1574          16,
1575      },
1576      // EctoSkull
1577      {
1578          870,
1579          699050,
1580          0,
1581          32,
1582          32,
1583          (char)-24,
1584          32,
1585      },
1586      // Hellhound flame
1587      {
1588          0,
1589          1118481,
1590          0,
1591          24,
1592          24,
1593          (char)-128,
1594          16,
1595      },
1596      // Puke
1597      {
1598          0,
1599          838860,
1600          0,
1601          16,
1602          16,
1603          (char)-16,
1604          16,
1605      },
1606      // Reserved
1607      {
1608          0,
1609          838860,
1610          0,
1611          8,
1612          8,
1613          (char)0,
1614          16,
1615      },
1616      // Stone gargoyle projectile
1617      {
1618          3056,
1619          2097152,
1620          0,
1621          32,
1622          32,
1623          (char)-128,
1624          16,
1625      },
1626      // Napalm launcher
1627      {
1628          0,
1629          2446677,
1630          0,
1631          30,
1632          30,
1633          (char)-128,
1634          24,
1635      },
1636      // Cerberus fireball
1637      {
1638          0,
1639          2446677,
1640          0,
1641          30,
1642          30,
1643          (char)-128,
1644          24,
1645      },
1646      // Tchernobog fireball
1647      {
1648          0,
1649          1398101,
1650          0,
1651          24,
1652          24,
1653          (char)-128,
1654          16,
1655      },
1656      // Regular life leech
1657      {
1658          2446,
1659          2796202,
1660          0,
1661          32,
1662          32,
1663          (char)-128,
1664          16,
1665      },
1666      // Dropped life leech (enough ammo)
1667      {
1668          3056,
1669          2446677,
1670          0,
1671          16,
1672          16,
1673          (char)-128,
1674          16,
1675      },
1676      // Dropped life leech (no ammo)
1677      {
1678          3056,
1679          1747626,
1680          0,
1681          32,
1682          32,
1683          (char)-128,
1684          16,
1685      },
1686      // Shotgun shell projectile
1687      {
1688          9295, // use bullet sprite from notblood.pk3/TILES099.ART
1689          (625)<<12,
1690          0,
1691          14,
1692          14,
1693          (char)-128,
1694          8,
1695      },
1696      // Tommy gun bullet projectile
1697      {
1698          9295, // use bullet sprite from notblood.pk3/TILES099.ART
1699          (625)<<12,
1700          0,
1701          14,
1702          14,
1703          (char)-128,
1704          8,
1705      }
1706  };
1707  
1708  THINGINFO thingInfo[] = {
1709      //TNT Barrel
1710      {
1711          25,
1712          250,
1713          32,
1714          11,
1715          4096,
1716          80,
1717          384,
1718          907,
1719          (char)0,
1720          0,
1721          0,
1722          0,
1723          256, 256, 128, 64, 0, 0, 128,
1724      },
1725      
1726      // Armed Proxy Dynamite
1727      {
1728          5,
1729          5,
1730          16,
1731          3,
1732          24576,
1733          1600,
1734          256,
1735          3444,
1736          (char)-16,
1737          0,
1738          32,
1739          32,
1740          256, 256, 256, 64, 0, 0, 512,
1741      },
1742      // Armed Remote Dynamite
1743      {
1744          5,
1745          5,
1746          16,
1747          3,
1748          24576,
1749          1600,
1750          256,
1751          3457,
1752          (char)-16,
1753          0,
1754          32,
1755          32,
1756          256, 256, 256, 64, 0, 0, 512,
1757      },
1758      // Vase1
1759      {
1760          1,
1761          20,
1762          32,
1763          3,
1764          32768,
1765          80,
1766          0,
1767          739,
1768          (char)0,
1769          0,
1770          0,
1771          0,
1772          256, 0, 256, 128, 0, 0, 0,
1773      },
1774      // Vase2
1775      {
1776          1,
1777          150,
1778          32,
1779          3,
1780          32768,
1781          80,
1782          0,
1783          642,
1784          (char)0,
1785          0,
1786          0,
1787          0,
1788          256, 256, 256, 128, 0, 0, 0,
1789      },
1790      // Crate face
1791      {
1792          10,
1793          0,
1794          0,
1795          0,
1796          0,
1797          0,
1798          0,
1799          462,
1800          (char)0,
1801          0,
1802          0,
1803          0,
1804          0, 0, 0, 256, 0, 0, 0,
1805      },
1806      // Glass window
1807      {
1808          1,
1809          0,
1810          0,
1811          0,
1812          0,
1813          0,
1814          0,
1815          266,
1816          (char)0,
1817          0,
1818          0,
1819          0,
1820          256, 0, 256, 256, 0, 0, 0,
1821      },
1822      // Flourescent Light
1823      {
1824          1,
1825          0,
1826          0,
1827          0,
1828          0,
1829          0,
1830          0,
1831          796,
1832          (char)0,
1833          0,
1834          0,
1835          0,
1836          256, 0, 256, 256, 0, 0, 512,
1837      },
1838      // Wall Crack
1839      {
1840          50,
1841          0,
1842          0,
1843          0,
1844          0,
1845          0,
1846          0,
1847          1127,
1848          (char)0,
1849          0,
1850          0,
1851          0,
1852          0, 0, 0, 256, 0, 0, 0,
1853      },
1854      // Wood Beam
1855      {
1856          8,
1857          0,
1858          0,
1859          0,
1860          0,
1861          0,
1862          0,
1863          1142,
1864          (char)0,
1865          0,
1866          0,
1867          0,
1868          256, 0, 256, 128, 0, 0, 0,
1869      },
1870      // Spider's Web
1871      {
1872          4,
1873          0,
1874          0,
1875          0,
1876          0,
1877          0,
1878          0,
1879          1069,
1880          (char)0,
1881          0,
1882          0,
1883          0,
1884          256, 256, 64, 256, 0, 0, 128,
1885      },
1886      // Metal Grate
1887      {
1888          40,
1889          0,
1890          0,
1891          0,
1892          0,
1893          0,
1894          0,
1895          483,
1896          (char)0,
1897          0,
1898          0,
1899          0,
1900          64, 0, 128, 256, 0, 0, 0,
1901      },
1902      // Flammable Tree
1903      {
1904          1,
1905          0,
1906          0,
1907          0,
1908          0,
1909          0,
1910          0,
1911          -1,
1912          (char)0,
1913          0,
1914          0,
1915          0,
1916          0, 256, 0, 256, 0, 0, 128,
1917      },
1918      // MachineGun Trap
1919      {
1920          1000,
1921          0,
1922          0,
1923          8,
1924          0,
1925          0,
1926          0,
1927          -1,
1928          (char)0,
1929          0,
1930          0,
1931          0,
1932          0, 0, 128, 256, 0, 0, 512,
1933      },
1934      // Falling Rock
1935      {
1936          0,
1937          15,
1938          8,
1939          3,
1940          32768,
1941          0,
1942          0,
1943          -1,
1944          (char)0,
1945          0,
1946          0,
1947          0,
1948          0, 0, 0, 0, 0, 0, 0,
1949      },
1950      // Kickable Pail
1951      {
1952          0,
1953          8,
1954          48,
1955          3,
1956          49152,
1957          0,
1958          0,
1959          -1,
1960          (char)0,
1961          0,
1962          0,
1963          0,
1964          0, 0, 0, 0, 0, 0, 0,
1965      },
1966      // Gib Object
1967      {
1968          10,
1969          2,
1970          0,
1971          0,
1972          32768,
1973          0,
1974          0,
1975          -1,
1976          (char)0,
1977          0,
1978          0,
1979          0,
1980          256, 0, 256, 256, 0, 0, 128,
1981      },
1982      // Explode Object
1983      {
1984          20,
1985          2,
1986          0,
1987          0,
1988          32768,
1989          0,
1990          0,
1991          -1,
1992          (char)0,
1993          0,
1994          0,
1995          0,
1996          0, 0, 0, 256, 0, 0, 128,
1997      },
1998      // Armed stick Of TNT
1999      {
2000          5,
2001          14,
2002          16,
2003          3,
2004          24576,
2005          1600,
2006          256,
2007          3422,
2008          (char)-32,
2009          0,
2010          32,
2011          32,
2012          64, 256, 128, 64, 0, 0, 256,
2013      },
2014      // Armed bundle Of TNT
2015      {
2016          5,
2017          14,
2018          16,
2019          3,
2020          24576,
2021          1600,
2022          256,
2023          3433,
2024          (char)-32,
2025          0,
2026          32,
2027          32,
2028          64, 256, 128, 64, 0, 0, 256,
2029      },
2030      // Armed aerosol
2031      {
2032          5,
2033          14,
2034          16,
2035          3,
2036          32768,
2037          1600,
2038          256,
2039          3467,
2040          (char)-128,
2041          0,
2042          32,
2043          32,
2044          64, 256, 128, 64, 0, 0, 256,
2045      },
2046      // Bone (Flesh Garg.)
2047      {
2048          5,
2049          6,
2050          16,
2051          3,
2052          32768,
2053          1600,
2054          256,
2055          1462,
2056          (char)0,
2057          0,
2058          32,
2059          32,
2060          0, 0, 0, 0, 0, 0, 0,
2061      },
2062      // Some alpha stuff
2063      {
2064          8,
2065          3,
2066          16,
2067          11,
2068          32768,
2069          1600,
2070          256,
2071          -1,
2072          (char)0,
2073          0,
2074          0,
2075          0,
2076          256, 0, 256, 256, 0, 0, 0,
2077      },
2078      // WaterDrip 
2079      {
2080          0,
2081          1,
2082          1,
2083          2,
2084          0,
2085          0,
2086          0,
2087          1147,
2088          (char)0,
2089          10,
2090          0,
2091          0,
2092          0, 0, 0, 0, 0, 0, 0,
2093      },
2094      // BloodDrip 
2095      {
2096          0,
2097          1,
2098          1,
2099          2,
2100          0,
2101          0,
2102          0,
2103          1160,
2104          (char)0,
2105          2,
2106          0,
2107          0,
2108          0, 0, 0, 0, 0, 0, 0,
2109      },
2110      // Blood chucks1 
2111      {
2112          15,
2113          4,
2114          4,
2115          3,
2116          24576,
2117          0,
2118          257,
2119          -1,
2120          (char)0,
2121          0,
2122          0,
2123          0,
2124          128, 64, 256, 256, 0, 0, 256,
2125      },
2126      // Blood chucks2
2127      {
2128          30,
2129          30,
2130          8,
2131          3,
2132          8192,
2133          0,
2134          257,
2135          -1,
2136          (char)0,
2137          0,
2138          0,
2139          0,
2140          128, 64, 256, 256, 0, 0, 64,
2141      },
2142      // Axe Zombie Head 
2143      {
2144          60,
2145          5,
2146          32,
2147          3,
2148          40960,
2149          1280,
2150          257,
2151          3405,
2152          (char)0,
2153          0,
2154          40,
2155          40,
2156          128, 64, 256, 256, 0, 0, 64,
2157      },
2158      // Napalm's Alt Fire explosion
2159      {
2160          80,
2161          30,
2162          32,
2163          3,
2164          57344,
2165          1600,
2166          256,
2167          3281,
2168          (char)-128,
2169          0,
2170          32,
2171          32,
2172          0, 0, 0, 0, 0, 0, 0,
2173      },
2174      // Fire Pod Explosion
2175      {
2176          80,
2177          30,
2178          32,
2179          3,
2180          57344,
2181          1600,
2182          256,
2183          2020,
2184          (char)-128,
2185          0,
2186          32,
2187          32,
2188          256, 0, 256, 256, 0, 0, 0,
2189      },
2190      // Green Pod Explosion
2191      {
2192          80,
2193          30,
2194          32,
2195          3,
2196          57344,
2197          1600,
2198          256,
2199          1860,
2200          (char)-128,
2201          0,
2202          32,
2203          32,
2204          256, 0, 256, 256, 0, 0, 0,
2205      },
2206      // Life Leech
2207      {
2208          150,
2209          30,
2210          48,
2211          3,
2212          32768,
2213          1600,
2214          257,
2215          800,
2216          (char)-128,
2217          0,
2218          48,
2219          48,
2220          64, 64, 112, 64, 0, 96, 96,
2221      },
2222      // Voodoo Head
2223      {
2224          1,
2225          30,
2226          48,
2227          3,
2228          32768,
2229          1600,
2230          0,
2231          2443,
2232          (char)-128,
2233          0,
2234          16,
2235          16,
2236          0, 0, 0, 0, 0, 0, 0,
2237      },
2238      // 433 - kModernThingTNTProx
2239      {
2240          5,
2241          5,
2242          16,
2243          3,
2244          24576,
2245          1600,
2246          256,
2247          3444,
2248          (char)-16,
2249          7,
2250          32,
2251          32,
2252          256, 256, 256, 64, 0, 0, 512,
2253      },
2254      // 434 - kModernThingThrowableRock
2255      {
2256          5,
2257          6,
2258          16,
2259          3,
2260          32768,
2261          1600,
2262          256,
2263          1462,
2264          (char)0,
2265          0,
2266          32,
2267          32,
2268          0, 0, 0, 0, 0, 0, 0,
2269      },
2270      // 435 - kModernThingEnemyLifeLeech
2271      {
2272          150,
2273          30,
2274          48,
2275          3,
2276          32768,
2277          1600,
2278          257,
2279          800,
2280          (char)-128,
2281          0,
2282          44,
2283          44,
2284          0, 1024, 512, 1024, 0, 64, 512,
2285      },
2286  };
2287  
2288  EXPLOSION explodeInfo[] = {
2289      {
2290          40,
2291          10,
2292          10,
2293          75,
2294          450,
2295          0,
2296          60,
2297          80,
2298          40
2299      },
2300      {
2301          80,
2302          20,
2303          10,
2304          150,
2305          900,
2306          0,
2307          60,
2308          160,
2309          60
2310      },
2311      {
2312          120,
2313          40,
2314          15,
2315          225,
2316          1350,
2317          0,
2318          60,
2319          240,
2320          80
2321      },
2322      {
2323          80,
2324          5,
2325          10,
2326          120,
2327          20,
2328          10,
2329          60,
2330          0,
2331          40
2332      },
2333      {
2334          120,
2335          10,
2336          10,
2337          180,
2338          40,
2339          10,
2340          60,
2341          0,
2342          80
2343      },
2344      {
2345          160,
2346          15,
2347          10,
2348          240,
2349          60,
2350          10,
2351          60,
2352          0,
2353          120
2354      },
2355      {
2356          40,
2357          20,
2358          10,
2359          120,
2360          0,
2361          10,
2362          30,
2363          60,
2364          40
2365      },
2366      {
2367          80,
2368          20,
2369          10,
2370          150,
2371          800,
2372          5,
2373          60,
2374          160,
2375          60
2376      },
2377  };
2378  
2379  int gDudeDrag = 0x2a00;
2380  
2381  short gAffectedSectors[kMaxSectors];
2382  short gAffectedXWalls[kMaxXWalls];
2383  short gPlayerGibThingComments[] = {
2384      734, 735, 736, 737, 738, 739, 740, 741, 3038, 3049
2385  };
2386  
2387  void FireballSeqCallback(int, int);
2388  void sub_38938(int, int);
2389  void NapalmSeqCallback(int, int);
2390  void sub_3888C(int, int);
2391  void TreeToGibCallback(int, int);
2392  void DudeToGibCallback1(int, int);
2393  void DudeToGibCallback2(int, int);
2394  
2395  int nFireballClient = seqRegisterClient(FireballSeqCallback);
2396  int dword_2192D8 = seqRegisterClient(sub_38938); // fireball smoke
2397  int nNapalmClient = seqRegisterClient(NapalmSeqCallback);
2398  int dword_2192E0 = seqRegisterClient(sub_3888C); // flame lick
2399  int nTreeToGibClient = seqRegisterClient(TreeToGibCallback);
2400  int nDudeToGibClient1 = seqRegisterClient(DudeToGibCallback1);
2401  int nDudeToGibClient2 = seqRegisterClient(DudeToGibCallback2);
2402  
2403  int gPostCount = 0;
2404  
2405  struct POSTPONE {
2406      short nSprite;
2407      short nStatus;
2408  };
2409  
2410  POSTPONE gPost[kMaxSprites];
2411  
2412  bool IsUnderwaterSector(int nSector)
2413  {
2414      int nXSector = sector[nSector].extra;
2415      if (nXSector > 0 && xsector[nXSector].Underwater)
2416          return 1;
2417      return 0;
2418  }
2419  
2420  int actSpriteOwnerToSpriteId(spritetype *pSprite)
2421  {
2422      dassert(pSprite != NULL);
2423      if (pSprite->owner == -1)
2424          return -1;
2425      int nSprite = pSprite->owner & (kMaxSprites-1);
2426      if (pSprite->owner & kMaxSprites)
2427          nSprite = gPlayer[nSprite].pSprite->index;
2428      return nSprite;
2429  }
2430  
2431  void actPropagateSpriteOwner(spritetype *pTarget, spritetype *pSource)
2432  {
2433      dassert(pTarget != NULL && pSource != NULL);
2434      if (IsPlayerSprite(pSource))
2435          pTarget->owner = (pSource->type - kDudePlayer1) | kMaxSprites;
2436      else
2437          pTarget->owner = pSource->index;
2438  }
2439  
2440  int actSpriteIdToOwnerId(int nSprite)
2441  {
2442      if (nSprite == -1)
2443          return -1;
2444      dassert(nSprite >= 0 && nSprite < kMaxSprites);
2445      spritetype *pSprite = &sprite[nSprite];
2446      if (IsPlayerSprite(pSprite))
2447          nSprite = (pSprite->type - kDudePlayer1) | kMaxSprites;
2448      return nSprite;
2449  }
2450  
2451  int actOwnerIdToSpriteId(int nSprite)
2452  {
2453      if (nSprite == -1)
2454          return -1;
2455      if (nSprite & kMaxSprites)
2456          nSprite = gPlayer[nSprite&(kMaxSprites-1)].pSprite->index;
2457      return nSprite;
2458  }
2459  
2460  bool actSpriteOwnerIsPlayer(spritetype *pSprite)
2461  {
2462      dassert(pSprite != NULL);
2463      if (pSprite->owner < 0)
2464          return false;
2465      return (pSprite->owner & kMaxSprites);
2466  }
2467  
2468  bool actSpriteOwnerIsDude(spritetype *pSprite)
2469  {
2470      dassert(pSprite != NULL);
2471      const int nOwner = pSprite->owner;
2472      if (nOwner < 0)
2473          return false;
2474      if (nOwner & kMaxSprites)
2475          return true;
2476      return (sprite[nOwner].type >= kDudeBase) && (sprite[nOwner].type < kDudeMax);
2477  }
2478  
2479  bool actTypeInSector(int nSector, int nType)
2480  {
2481      for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritestat[nSprite])
2482      {
2483          if (sprite[nSprite].index == nType)
2484              return 1;
2485      }
2486      return 0;
2487  }
2488  
2489  void actAllocateSpares(void)
2490  {
2491  }
2492  
2493  int DudeDifficulty[5] = {
2494      512, 384, 256, 208, 160
2495  };
2496  
2497  void actInit(bool bSaveLoad) {
2498      if (!bSaveLoad) { // reset postpone count on new level
2499          gPostCount = 0;
2500      }
2501  
2502      #ifdef NOONE_EXTENSIONS
2503      if (!gModernMap) {
2504          LOG_F(INFO, "> This map *does not* provide modern features.");
2505          nnExtResetGlobals();
2506      } else {
2507          LOG_F(INFO, "> This map provides modern features.");
2508          nnExtInitModernStuff(bSaveLoad);
2509      }
2510  
2511      if (!gUserItemsInitialized)
2512      {
2513          userItemsInit(1);
2514          gUserItemsInitialized = 1;
2515      }
2516  
2517      #endif
2518      
2519      for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
2520          switch (sprite[nSprite].type) {
2521              case kItemWeaponVoodooDoll:
2522                  sprite[nSprite].type = kItemAmmoVoodooDoll;
2523                  break;
2524          }
2525      }
2526  
2527      for (int nSprite = headspritestat[kStatTraps]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
2528          spritetype *pSprite = &sprite[nSprite];
2529          switch (pSprite->type) {
2530              case kTrapExploder:
2531                  pSprite->cstat &= ~1; pSprite->cstat |= CSTAT_SPRITE_INVISIBLE;
2532                  if (pSprite->extra <= 0 || pSprite->extra >= kMaxXSprites) continue;
2533                  xsprite[pSprite->extra].waitTime = ClipLow(xsprite[pSprite->extra].waitTime, 1);
2534                  xsprite[pSprite->extra].state = 0;
2535                  break;
2536          }
2537      }
2538  
2539      for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
2540          if (sprite[nSprite].extra <= 0 || sprite[nSprite].extra >= kMaxXSprites) continue;
2541          spritetype* pSprite = &sprite[nSprite]; XSPRITE *pXSprite = &xsprite[pSprite->extra];
2542          
2543          int nType = pSprite->type - kThingBase;
2544          pXSprite->health = thingInfo[nType].startHealth << 4;
2545          #ifdef NOONE_EXTENSIONS
2546              // allow level designer to set custom clipdist.
2547              // this is especially useful for various Gib and Explode objects which have clipdist 1 for some reason predefined,
2548              // but what if it have voxel model...?
2549              if (!gModernMap)
2550          #endif
2551              pSprite->clipdist = thingInfo[nType].clipdist;
2552          
2553          pSprite->flags = thingInfo[nType].flags;
2554          if (pSprite->flags & kPhysGravity) pSprite->flags |= kPhysFalling;
2555          xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0;
2556          
2557          switch (pSprite->type) {
2558              case kThingArmedProxBomb:
2559              case kTrapMachinegun:
2560              #ifdef NOONE_EXTENSIONS
2561              case kModernThingTNTProx:
2562              #endif
2563                  pXSprite->state = 0;
2564                  break;
2565              case kThingBloodChunks: {
2566                  SEQINST *pInst = GetInstance(3, pSprite->extra);
2567                  if (pInst && pInst->isPlaying) {
2568                      DICTNODE *hSeq = gSysRes.Lookup(pInst->nSeq, "SEQ");
2569                      if (!hSeq) break;
2570                      seqSpawn(pInst->nSeq, 3, pSprite->extra);
2571                  }
2572                  break;
2573              }
2574              default:
2575                  pXSprite->state = 1;
2576                  break;
2577          }
2578      }
2579      
2580      const char bHalfMotherSpiderHp = gGameOptions.uSpriteBannedFlags&BANNED_MSPIDERS_HP && !(!Bstrcmp(BloodIniFile, "BLOOD.INI") && (gGameOptions.nEpisode == 1 && gGameOptions.nLevel == 7)); // do not lower HP for E2M8
2581      const char bMaxTchernobogHp = gGameOptions.uSpriteBannedFlags&BANNED_TCHERNOBOG_HP; // max tchernobog HP
2582      dudeInfo[kDudeSpiderMother-kDudeBase].startHealth = bHalfMotherSpiderHp ? 50 : 100;
2583      dudeInfo[kDudeTchernobog-kDudeBase].startHealth = bMaxTchernobogHp ? 255 : 32;
2584      if (gGameOptions.nMonsterSettings == 0)
2585      {
2586          gKillMgr.SetCount(0);
2587          int nSprite = headspritestat[kStatDude];
2588          while (nSprite >= 0)
2589          {
2590              spritetype *pSprite = &sprite[headspritestat[kStatDude]];
2591              if (IsPlayerSprite(pSprite)) { // do not delete player sprites (causes game to crash on load save)
2592                  nSprite = nextspritestat[nSprite];
2593                  continue;
2594              }
2595              if (pSprite->extra > 0 && pSprite->extra < kMaxXSprites && xsprite[pSprite->extra].key > 0) // Drop Key
2596                  actDropObject(pSprite, kItemKeyBase + (xsprite[pSprite->extra].key - 1));
2597              DeleteSprite(nSprite);
2598              nSprite = headspritestat[kStatDude]; // start all over again until only player sprites are left
2599          }
2600      }
2601      else
2602      {
2603          gKillMgr.CountTotalKills();
2604  
2605          for (int i = 0; i < kDudeMax - kDudeBase; i++)
2606              for (int j = 0; j < kDamageMax; j++)
2607                  dudeInfo[i].curDamage[j] = mulscale8(DudeDifficulty[gGameOptions.nEnemyHealth], dudeInfo[i].startDamage[j]);
2608  
2609          for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
2610          {
2611              if (xspriRangeIsFine(sprite[nSprite].extra))
2612              {
2613                  spritetype* pSprite = &sprite[nSprite]; XSPRITE* pXSprite = &xsprite[pSprite->extra];
2614                  int nType = pSprite->type - kDudeBase; int seqStartId = dudeInfo[nType].seqStartID;
2615                  
2616                  if (!IsPlayerSprite(pSprite))
2617                  {
2618                      xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0;
2619  
2620                      #ifdef NOONE_EXTENSIONS
2621                      if (!gModernMap && !bSaveLoad) pXSprite->health = dudeInfo[nType].startHealth << 4;
2622                      else if (!bSaveLoad) // add a way to set custom hp for every enemy
2623                          pXSprite->health = nnExtDudeStartHealth(pSprite, pXSprite->sysData2);
2624                      #else
2625                      if (!bSaveLoad) pXSprite->health = dudeInfo[nType].startHealth << 4;
2626                      #endif
2627  
2628                      switch (pSprite->type)
2629                      {
2630                          #ifdef NOONE_EXTENSIONS
2631                          case kDudeModernCustom:
2632                          case kDudePodMother:
2633                              if (gModernMap)
2634                              {
2635                                  pXSprite->sysData1 = pXSprite->data3; // move sndStartId to sysData1, because data3 used by the game
2636                                  pXSprite->data3 = 0;
2637                                  
2638                                  // no seq, custom flags, clipdist and cstat
2639                                  continue;
2640                              }
2641                              
2642                              fallthrough__;
2643                          #endif
2644                          default:
2645                              pSprite->clipdist = dudeInfo[nType].clipdist;
2646                              pSprite->cstat |= 4096 + CSTAT_SPRITE_BLOCK_HITSCAN + CSTAT_SPRITE_BLOCK;
2647                              break;
2648                      }
2649                  }
2650  
2651                  if (gSysRes.Lookup(seqStartId, "SEQ"))
2652                      seqSpawn(seqStartId, 3, pSprite->extra);
2653              }
2654          }
2655  
2656          #ifdef NOONE_EXTENSIONS
2657          userItemsInitSprites();
2658          #endif
2659  
2660          aiInit();
2661      }
2662  }
2663  
2664  void ConcussSprite(int a1, spritetype *pSprite, int x, int y, int z, int a6)
2665  {
2666      dassert(pSprite != NULL);
2667      int dx = pSprite->x-x;
2668      int dy = pSprite->y-y;
2669      int dz = (pSprite->z-z)>>4;
2670      int dist2 = 0x40000+dx*dx+dy*dy+dz*dz;
2671      dassert(dist2 > 0);
2672      if (gNukeMode && !VanillaMode()) // nuke cheat
2673          a6 *= 4;
2674      a6 = scale(0x40000, a6, dist2);
2675  
2676      if (pSprite->flags & kPhysMove) {
2677          int mass = 0;
2678          if (IsDudeSprite(pSprite)) {
2679  
2680              #ifdef NOONE_EXTENSIONS
2681              mass = (IsCustomDude(pSprite)) ? cdudeGet(pSprite)->mass : getDudeInfo(pSprite->type)->mass;
2682              #else
2683              mass = getDudeInfo(pSprite->type)->mass;
2684              #endif
2685  
2686          } else if (pSprite->type >= kThingBase && pSprite->type < kThingMax) {
2687              mass = thingInfo[pSprite->type - kThingBase].mass;
2688          } else {
2689              consoleSysMsg("Unexpected type in ConcussSprite(): Sprite: %d  Type: %d  Stat: %d", (int)pSprite->index, (int)pSprite->type, (int)pSprite->statnum);
2690              return;
2691          }
2692  
2693          int size = (tilesiz[pSprite->picnum].x*pSprite->xrepeat*tilesiz[pSprite->picnum].y*pSprite->yrepeat)>>1;
2694          dassert(mass > 0);
2695  
2696          int t = scale(a6, size, mass);
2697          dx = mulscale16(t, dx);
2698          dy = mulscale16(t, dy);
2699          dz = mulscale16(t, dz);
2700          int nSprite = pSprite->index;
2701          dassert(nSprite >= 0 && nSprite < kMaxSprites);
2702          xvel[nSprite] += dx;
2703          yvel[nSprite] += dy;
2704          zvel[nSprite] += dz;
2705      }
2706  
2707      actDamageSprite(a1, pSprite, kDamageExplode, a6);
2708  }
2709  
2710  int actWallBounceVector(int *x, int *y, int nWall, int a4)
2711  {
2712      int wx, wy;
2713      GetWallNormal(nWall, &wx, &wy);
2714      int t = dmulscale16(*x, wx, *y, wy);
2715      int t2 = mulscale16r(t, a4+0x10000);
2716      *x -= mulscale16(wx, t2);
2717      *y -= mulscale16(wy, t2);
2718      return mulscale16r(t, 0x10000-a4);
2719  }
2720  
2721  int actFloorBounceVector(int *x, int *y, int *z, int nSector, int a5)
2722  {
2723      int t = 0x10000-a5;
2724      if (sector[nSector].floorheinum == 0)
2725      {
2726          int t2 = mulscale16(*z, t);
2727          *z = -(*z-t2);
2728          return t2;
2729      }
2730      walltype *pWall = &wall[sector[nSector].wallptr];
2731      walltype *pWall2 = &wall[pWall->point2];
2732      int angle = getangle(pWall2->x-pWall->x, pWall2->y-pWall->y)+512;
2733      int t2 = sector[nSector].floorheinum<<4;
2734      int t3 = approxDist(-0x10000, t2);
2735      int t4 = divscale16(-0x10000, t3);
2736      int t5 = divscale16(t2, t3);
2737      int t6 = mulscale30(t5, Cos(angle));
2738      int t7 = mulscale30(t5, Sin(angle));
2739      int t8 = tmulscale16(*x, t6, *y, t7, *z, t4);
2740      int t9 = mulscale16(t8, 0x10000+a5);
2741      *x -= mulscale16(t6, t9);
2742      *y -= mulscale16(t7, t9);
2743      *z -= mulscale16(t4, t9);
2744      return mulscale16r(t8, t);
2745  }
2746  
2747  void actRadiusDamage(int nSprite, int x, int y, int z, int nSector, int nDist, int baseDmg, int distDmg, DAMAGE_TYPE dmgType, int flags, int burn)
2748  {
2749      char sectmap[bitmap_size(kMaxSectors)];
2750      int nOwner = actSpriteIdToOwnerId(nSprite);
2751      gAffectedSectors[0] = 0;
2752      gAffectedXWalls[0] = 0;
2753      const bool bAccurateCheck = (!VanillaMode() && spriRangeIsFine(nOwner) && IsDudeSprite(&sprite[nOwner])); // use new sector checking logic
2754      GetClosestSpriteSectors(nSector, x, y, nDist, gAffectedSectors, sectmap, gAffectedXWalls, bAccurateCheck);
2755      nDist <<= 4;
2756      if (flags & 2)
2757      {
2758          for (int i = headspritestat[kStatDude]; i >= 0; i = nextspritestat[i])
2759          {
2760              if (i != nSprite || (flags & 1))
2761              {
2762                  spritetype *pSprite2 = &sprite[i];
2763                  if (pSprite2->extra > 0 && pSprite2->extra < kMaxXSprites)
2764                  {
2765  
2766                      if (pSprite2->flags & 0x20)
2767                          continue;
2768                      if (!TestBitString(sectmap, pSprite2->sectnum))
2769                          continue;
2770                      if (!CheckProximity(pSprite2, x, y, z, nSector, nDist))
2771                          continue;
2772                      int dx = klabs(x-pSprite2->x);
2773                      int dy = klabs(y-pSprite2->y);
2774                      int dz = klabs(z-pSprite2->z)>>4;
2775                      int dist = ksqrt(dx*dx+dy*dy+dz*dz);
2776                      if (dist > nDist)
2777                          continue;
2778                      int vcx;
2779                      if (dist != 0)
2780                          vcx = baseDmg+((nDist-dist)*distDmg)/nDist;
2781                      else
2782                          vcx = baseDmg+distDmg;
2783                      actDamageSprite(nSprite, pSprite2, dmgType, vcx<<4);
2784                      if (burn)
2785                          actBurnSprite(nOwner, &xsprite[pSprite2->extra], burn);
2786                  }
2787              }
2788          }
2789      }
2790      if (flags & 4)
2791      {
2792          for (int i = headspritestat[kStatThing]; i >= 0; i = nextspritestat[i])
2793          {
2794              spritetype *pSprite2 = &sprite[i];
2795  
2796              if (pSprite2->flags&0x20)
2797                  continue;
2798              if (!TestBitString(sectmap, pSprite2->sectnum))
2799                  continue;
2800              if (!CheckProximity(pSprite2, x, y, z, nSector, nDist))
2801                  continue;
2802              XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
2803              if (pXSprite2->locked)
2804                  continue;
2805              int dx = klabs(x-pSprite2->x);
2806              int dy = klabs(y-pSprite2->y);
2807              int dist = ksqrt(dx*dx+dy*dy);
2808              if (dist > nDist)
2809                  continue;
2810              int vcx;
2811              if (dist != 0)
2812                  vcx = baseDmg+((nDist-dist)*distDmg)/nDist;
2813              else
2814                  vcx = baseDmg+distDmg;
2815              actDamageSprite(nSprite, pSprite2, dmgType, vcx<<4);
2816              if (burn)
2817                  actBurnSprite(nOwner, pXSprite2, burn);
2818          }
2819      }
2820  }
2821  
2822  void actNapalmMove(spritetype *pSprite, XSPRITE *pXSprite)
2823  {
2824      int nSprite = actOwnerIdToSpriteId(pSprite->owner);
2825      actPostSprite(pSprite->index, kStatDecoration);
2826      seqSpawn(9, 3, pSprite->extra);
2827      if (Chance(0x8000))
2828          pSprite->cstat |= 4;
2829  
2830      sfxPlay3DSound(pSprite, 303, 24+(pSprite->flags&3), 1);
2831      actRadiusDamage(nSprite, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 128, 0, 60, kDamageExplode, 15, 120);
2832      if (pXSprite->data4 > 1)
2833      {
2834          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
2835          int v14[2];
2836          v14[0] = pXSprite->data4>>1;
2837          v14[1] = pXSprite->data4-v14[0];
2838          int v4 = pSprite->ang;
2839          xvel[pSprite->index] = 0;
2840          yvel[pSprite->index] = 0;
2841          zvel[pSprite->index] = 0;
2842          for (int i = 0; i < 2; i++)
2843          {
2844              int t1 = Random(0x33333)+0x33333;
2845              int t2 = Random2(0x71);
2846              pSprite->ang = (t2+v4+2048)&2047;
2847              spritetype *pSprite2 = actFireThing(pSprite, 0, 0, -0x93d0, kThingNapalmBall, t1);
2848              XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
2849              pSprite2->owner = pSprite->owner;
2850              seqSpawn(61, 3, pSprite2->extra, nNapalmClient);
2851              pXSprite2->data4 = v14[i];
2852          }
2853      }
2854  }
2855  
2856  spritetype *actSpawnFloor(spritetype *pSprite)
2857  {
2858      short nSector = pSprite->sectnum;
2859      int x = pSprite->x;
2860      int y = pSprite->y;
2861      updatesector(x, y, &nSector);
2862      int zFloor = getflorzofslope(nSector, x, y);
2863      spritetype *pSprite2 = actSpawnSprite(nSector, x, y, zFloor, 3, 0);
2864      if (pSprite2)
2865          pSprite2->cstat &= ~257;
2866      return pSprite2;
2867  }
2868  
2869  spritetype *actDropAmmo(spritetype *pSprite, int nType)
2870  {
2871      if (dbIsBannedSpriteType(nType))
2872          return NULL;
2873      spritetype *pSprite2 = NULL;
2874      if (pSprite && pSprite->statnum < kMaxStatus && nType >= kItemAmmoBase && nType < kItemAmmoMax)
2875      {
2876          pSprite2 = actSpawnFloor(pSprite);
2877          AMMOITEMDATA *pAmmo = &gAmmoItemData[nType - kItemAmmoBase];
2878          pSprite2->type = nType;
2879          pSprite2->picnum = pAmmo->picnum;
2880          pSprite2->shade = pAmmo->shade;
2881          pSprite2->xrepeat = pAmmo->xrepeat;
2882          pSprite2->yrepeat = pAmmo->yrepeat;
2883      }
2884      return pSprite2;
2885  }
2886  
2887  spritetype *actDropWeapon(spritetype *pSprite, int nType)
2888  {
2889      if (dbIsBannedSpriteType(nType))
2890          return NULL;
2891      spritetype *pSprite2 = NULL;
2892      if (pSprite && pSprite->statnum < kMaxStatus && nType >= kItemWeaponBase && nType < kItemWeaponMax)
2893      {
2894          pSprite2 = actSpawnFloor(pSprite);
2895          WEAPONITEMDATA *pWeapon = &gWeaponItemData[nType - kItemWeaponBase];
2896          pSprite2->type = nType;
2897          pSprite2->picnum = pWeapon->picnum;
2898          pSprite2->shade = pWeapon->shade;
2899          pSprite2->xrepeat = pWeapon->xrepeat;
2900          pSprite2->yrepeat = pWeapon->yrepeat;
2901      }
2902      return pSprite2;
2903  }
2904  
2905  spritetype *actDropItem(spritetype *pSprite, int nType)
2906  {
2907      if (dbIsBannedSpriteType(nType))
2908          return NULL;
2909      spritetype *pSprite2 = NULL;
2910      if (pSprite && pSprite->statnum < kMaxStatus && nType >= kItemBase && nType < kItemMax)
2911      {
2912          pSprite2 = actSpawnFloor(pSprite);
2913          ITEMDATA *pItem = &gItemData[nType - kItemBase];
2914          pSprite2->type = nType;
2915          pSprite2->picnum = pItem->picnum;
2916          pSprite2->shade = pItem->shade;
2917          pSprite2->xrepeat = pItem->xrepeat;
2918          pSprite2->yrepeat = pItem->yrepeat;
2919      }
2920      return pSprite2;
2921  }
2922  
2923  spritetype *actDropKey(spritetype *pSprite, int nType)
2924  {
2925      spritetype *pSprite2 = NULL;
2926      if (pSprite && pSprite->statnum < kMaxStatus && nType >= kItemKeyBase && nType < kItemKeyMax)
2927      {
2928          pSprite2 = actDropItem(pSprite, nType);
2929          if (pSprite2 && gGameOptions.nGameType == kGameTypeCoop)
2930          {
2931              if (pSprite2->extra == -1)
2932                  dbInsertXSprite(pSprite2->index);
2933              xsprite[pSprite2->extra].respawn = 3;
2934              gSpriteHit[pSprite2->extra].florhit = 0;
2935              gSpriteHit[pSprite2->extra].ceilhit = 0;
2936          }
2937      }
2938      return pSprite2;
2939  }
2940  
2941  spritetype *actDropFlag(spritetype *pSprite, int nType)
2942  {
2943      spritetype *pSprite2 = NULL;
2944      if (pSprite && pSprite->statnum < kMaxStatus && (nType == kItemFlagA || nType == kItemFlagB))
2945      {
2946          pSprite2 = actDropItem(pSprite, nType);
2947          if (pSprite2 && gGameOptions.nGameType == kGameTypeTeams)
2948          {
2949              evPost(pSprite2->index, 3, 1800, kCallbackReturnFlag);
2950          }
2951      }
2952      return pSprite2;
2953  }
2954  
2955  spritetype *actDropObject(spritetype *pSprite, int nType) {
2956      spritetype *pSprite2 = NULL;
2957      
2958  #ifdef NOONE_EXTENSIONS
2959      if (IsUserItem(nType))
2960      {
2961          pSprite2 = userItemDrop(pSprite, nType);
2962      }
2963      else
2964  #endif
2965      if (nType >= kItemKeyBase && nType < kItemKeyMax) pSprite2 = actDropKey(pSprite, nType);
2966      else if (nType == kItemFlagA || nType == kItemFlagB) pSprite2 = actDropFlag(pSprite, nType);
2967      else if (nType >= kItemBase && nType < kItemMax) pSprite2 = actDropItem(pSprite, nType);
2968      else if (nType >= kItemAmmoBase && nType < kItemAmmoMax) pSprite2 = actDropAmmo(pSprite, nType);
2969      else if (nType >= kItemWeaponBase && nType < kItemWeaponMax) pSprite2 = actDropWeapon(pSprite, nType);
2970      
2971      if (pSprite2) {
2972          int top, bottom;
2973          GetSpriteExtents(pSprite2, &top, &bottom);
2974          if (bottom >= pSprite2->z)
2975              pSprite2->z -= bottom - pSprite2->z;
2976      }
2977  
2978      return pSprite2;
2979  }
2980  
2981  bool actHealDude(XSPRITE *pXDude, int a2, int a3)
2982  {
2983      dassert(pXDude != NULL);
2984      a2 <<= 4;
2985      a3 <<= 4;
2986      if (pXDude->health < a3)
2987      {
2988          spritetype *pSprite = &sprite[pXDude->reference];
2989          if (IsPlayerSprite(pSprite))
2990              sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 780, pSprite->sectnum);
2991          pXDude->health = ClipHigh(pXDude->health+a2, a3);
2992          return 1;
2993      }
2994      return 0;
2995  }
2996  
2997  void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, int damage)
2998  {
2999      spritetype *pKillerSprite = &sprite[nKillerSprite];
3000      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
3001      int nType = pSprite->type-kDudeBase;
3002      int nXSprite = pSprite->extra;
3003      dassert(nXSprite > 0);
3004      XSPRITE *pXSprite = &xsprite[pSprite->extra];
3005      
3006      switch (pSprite->type) {
3007      #ifdef NOONE_EXTENSIONS
3008      case kDudeModernCustom:
3009          cdudeGet(pSprite)->Kill(nKillerSprite, damageType, damage);
3010              return;
3011      #endif
3012      case kDudeCerberusTwoHead: // Cerberus
3013          seqSpawn(dudeInfo[nType].seqStartID+1, 3, nXSprite, -1);
3014          return;
3015      case kDudeCultistTommy:
3016      case kDudeCultistShotgun:
3017      case kDudeCultistTesla:
3018      case kDudeCultistTNT:
3019          if (damageType == kDamageBurn && pXSprite->medium == kMediumNormal)
3020          {
3021              pSprite->type = kDudeBurningCultist;
3022              aiNewState(pSprite, pXSprite, &cultistBurnGoto);
3023              actHealDude(pXSprite, dudeInfo[40].startHealth, dudeInfo[40].startHealth);
3024              return;
3025          }
3026          // no break
3027          fallthrough__;
3028      case kDudeBeast:
3029          if (damageType == kDamageBurn && pXSprite->medium == kMediumNormal)
3030          {
3031              pSprite->type = kDudeBurningBeast;
3032              aiNewState(pSprite, pXSprite, &beastBurnGoto);
3033              actHealDude(pXSprite, dudeInfo[53].startHealth, dudeInfo[53].startHealth);
3034              return;
3035          }
3036          // no break
3037          fallthrough__;
3038      case kDudeInnocent:
3039          if (damageType == kDamageBurn && pXSprite->medium == kMediumNormal)
3040          {
3041              pSprite->type = kDudeBurningInnocent;
3042              aiNewState(pSprite, pXSprite, &innocentBurnGoto);
3043              actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
3044              return;
3045          }
3046          break;
3047      case kDudeTinyCaleb:
3048          if (!EnemiesNBlood() || VanillaMode())
3049              break;
3050          if (damageType == kDamageBurn && pXSprite->medium == kMediumNormal)
3051          {
3052              pSprite->type = kDudeBurningTinyCaleb;
3053              aiNewState(pSprite, pXSprite, &tinycalebBurnGoto);
3054              actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
3055              return;
3056          }
3057          break;
3058      }
3059      for (int p = connecthead; p >= 0; p = connectpoint2[p])
3060      {
3061          if (gPlayer[p].fraggerId == pSprite->index && gPlayer[p].deathTime > 0)
3062              gPlayer[p].fraggerId = -1;
3063      }
3064      if (pSprite->type != kDudeCultistBeast)
3065          trTriggerSprite(pSprite->index, pXSprite, kCmdOff, nKillerSprite);
3066  
3067      pSprite->flags |= 7;
3068      if (VanillaMode()) {
3069          if (IsPlayerSprite(pKillerSprite)) {
3070              PLAYER *pPlayer = &gPlayer[pKillerSprite->type - kDudePlayer1];
3071              if (gGameOptions.nGameType == kGameTypeCoop)
3072                  pPlayer->fragCount++;
3073          }
3074      } else if (gGameOptions.nGameType == kGameTypeCoop && IsPlayerSprite(pKillerSprite) && pSprite->statnum == kStatDude) {
3075              switch (pSprite->type) {
3076                  case kDudeBat:
3077                  case kDudeRat:
3078                  case kDudeInnocent:
3079                  case kDudeBurningInnocent:
3080                      break;
3081                  default:
3082                      PLAYER* pKillerPlayer = &gPlayer[pKillerSprite->type - kDudePlayer1];
3083                      pKillerPlayer->fragCount++;
3084                      break;
3085              }
3086      }
3087      if (gSoundDingKill && gMe && (pKillerSprite == gMe->pSprite) && (pSprite->statnum == kStatDude)) // if we killed an AI enemy
3088      {
3089          static int nLastDinged = 0;
3090          if (klabs(nLastDinged - gLevelTime) > (kTicsPerSec>>2)) // only allow one ding every 1/4 second
3091              sndStartSample("NOTKILL", gSoundDingKillVol, -1, gSoundDingKillPitch);
3092          nLastDinged = gLevelTime;
3093      }
3094  
3095      if (pXSprite->key > 0)
3096          actDropObject(pSprite, kItemKeyBase + pXSprite->key - 1);
3097      
3098      if (pXSprite->dropMsg > 0)
3099          actDropObject(pSprite, pXSprite->dropMsg);
3100      
3101      switch (pSprite->type) {
3102          case kDudeCultistTommy: {
3103              int nRand = Random(100);
3104              if (nRand < 10) actDropObject(pSprite, kItemWeaponTommygun);
3105              else if (nRand < 50) actDropObject(pSprite, kItemAmmoTommygunFew);
3106          }
3107          break;
3108          case kDudeCultistShotgun: {
3109              int nRand = Random(100);
3110              if (nRand <= 10) actDropObject(pSprite, kItemWeaponSawedoff);
3111              else if (nRand <= 50) actDropObject(pSprite, kItemAmmoSawedoffFew);
3112          }
3113          break;
3114      }
3115  
3116      int nSeq;
3117      switch (damageType)
3118      {
3119      case kDamageExplode:
3120          nSeq = 2;
3121          switch (pSprite->type) {
3122              case kDudeCultistTommy:
3123              case kDudeCultistShotgun:
3124              case kDudeCultistTommyProne:
3125              case kDudeBurningInnocent:
3126              case kDudeBurningCultist:
3127              case kDudeInnocent:
3128              case kDudeCultistShotgunProne:
3129              case kDudeCultistTesla:
3130              case kDudeCultistTNT:
3131              case kDudeCultistBeast:
3132              case kDudeTinyCaleb:
3133              case kDudeBurningTinyCaleb:
3134                  sfxPlay3DSound(pSprite, 717,-1,0);
3135                  break;
3136          }
3137          break;
3138      case kDamageBurn:
3139          nSeq = 3;
3140          sfxPlay3DSound(pSprite, 351, -1, 0);
3141          break;
3142      case kDamageSpirit:
3143          switch (pSprite->type) {
3144              case kDudeZombieAxeNormal:
3145              case kDudeZombieAxeBuried:
3146                  nSeq = 14;
3147                  break;
3148              case kDudeZombieButcher:
3149                  nSeq = 11;
3150                  break;
3151              default:
3152                  nSeq = 1;
3153                  break;
3154          }
3155          break;
3156      case kDamageFall:
3157          switch (pSprite->type)
3158          {
3159          case kDudeCultistTommy:
3160          case kDudeCultistShotgun:
3161              nSeq = 1;
3162              break;
3163          default:
3164              nSeq = 1;
3165              break;
3166          }
3167          break;
3168      default:
3169          nSeq = 1;
3170          break;
3171      }
3172  
3173      if (!gSysRes.Lookup(getDudeInfo(nType+kDudeBase)->seqStartID + nSeq, "SEQ"))
3174      {
3175          seqKill(3, nXSprite);
3176          gKillMgr.AddKill(pSprite);
3177          actPostSprite(pSprite->index, kStatFree);
3178          return;
3179      }
3180  
3181      switch (pSprite->type) {
3182      case kDudeZombieAxeNormal:
3183          sfxPlay3DSound(pSprite, 1107+Random(2), -1, 0);
3184          if (nSeq == 2) {
3185              
3186              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1);
3187              int top, bottom;
3188              GetSpriteExtents(pSprite, &top, &bottom);
3189              CGibPosition gibPos(pSprite->x, pSprite->y, top);
3190              CGibVelocity gibVel(xvel[pSprite->index]>>1, yvel[pSprite->index]>>1, -0xccccc);
3191              GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel);
3192          
3193          } else if (nSeq == 1 && Chance(0x4000)) {
3194              
3195              seqSpawn(dudeInfo[nType].seqStartID+7, 3, nXSprite, nDudeToGibClient1);
3196              evPost(pSprite->index, 3, 0, kCallbackFXZombieSpurt);
3197              sfxPlay3DSound(pSprite, 362, -1, 0);
3198              pXSprite->data1 = 35;
3199              pXSprite->data2 = 5;
3200              int top, bottom;
3201              GetSpriteExtents(pSprite, &top, &bottom);
3202              CGibPosition gibPos(pSprite->x, pSprite->y, top);
3203              CGibVelocity gibVel(xvel[pSprite->index] >> 1, yvel[pSprite->index] >> 1, -0x111111);
3204              GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel);
3205  
3206          } else if (nSeq == 14)
3207              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3208          else if (nSeq == 3)
3209              seqSpawn(dudeInfo[nType].seqStartID+13, 3, nXSprite, nDudeToGibClient2);
3210          else
3211              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1);
3212          break;
3213      case kDudeCultistTommy:
3214      case kDudeCultistShotgun:
3215      case kDudeCultistTesla:
3216      case kDudeCultistTNT:
3217          if (EnemiesNBlood() && !VanillaMode()) // trigger cultist death scream sfx over cultist alerts sfx channel
3218              sfxPlay3DSound(pSprite, 1018+Random(2), kMaxSprites+pSprite->index, 1|8); // cultist alert sfx use kMaxSprites+sprite index as channel
3219          else
3220              sfxPlay3DSound(pSprite, 1018+Random(2), -1, 0);
3221          if (nSeq == 3)
3222              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient2);
3223          else
3224              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1);
3225          break;
3226      case kDudeBurningCultist:
3227          if (Chance(0x4000) && nSeq == 3)
3228              sfxPlay3DSound(pSprite, 718, -1, 0);
3229          else
3230              sfxPlay3DSound(pSprite, 1018+Random(2), -1, 0);
3231          damageType = kDamageExplode;
3232          if (Chance(0x8000))
3233          {
3234              for (int i = 0; i < 3; i++)
3235                  GibSprite(pSprite, GIBTYPE_7, NULL, NULL);
3236              seqSpawn(dudeInfo[nType].seqStartID+16-Random(1), 3, nXSprite, nDudeToGibClient1);
3237          }
3238          else
3239              seqSpawn(dudeInfo[nType].seqStartID+15, 3, nXSprite, nDudeToGibClient2);
3240          break;
3241      case kDudeBurningZombieAxe:
3242          if (Chance(0x8000) && nSeq == 3)
3243              sfxPlay3DSound(pSprite, 1109, -1, 0);
3244          else
3245              sfxPlay3DSound(pSprite, 1107+Random(2), -1, 0);
3246          damageType = kDamageExplode;
3247          if (Chance(0x8000))
3248          {
3249              seqSpawn(dudeInfo[nType].seqStartID+13, 3, nXSprite, nDudeToGibClient1);
3250              int top, bottom;
3251              GetSpriteExtents(pSprite, &top, &bottom);
3252              CGibPosition gibPos(pSprite->x, pSprite->y, top);
3253              CGibVelocity gibVel(xvel[pSprite->index]>>1, yvel[pSprite->index]>>1, -0xccccc);
3254              GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel);
3255          }
3256          else
3257              seqSpawn(dudeInfo[nType].seqStartID+13, 3, nXSprite, nDudeToGibClient2);
3258          break;
3259      case kDudeBurningZombieButcher:
3260          if (Chance(0x4000) && nSeq == 3)
3261              sfxPlay3DSound(pSprite, 1206, -1, 0);
3262          else
3263              sfxPlay3DSound(pSprite, 1204+Random(2), -1, 0);
3264          seqSpawn(dudeInfo[4].seqStartID+10, 3, nXSprite, -1);
3265          break;
3266      case kDudeBurningInnocent:
3267          damageType = kDamageExplode;
3268          seqSpawn(dudeInfo[nType].seqStartID+7, 3, nXSprite, nDudeToGibClient1);
3269          break;
3270      case kDudeZombieButcher:
3271          if (nSeq == 14) {
3272              sfxPlay3DSound(pSprite, 1206, -1, 0);
3273              seqSpawn(dudeInfo[nType].seqStartID+11, 3, nXSprite, -1);
3274              break;
3275          }
3276          sfxPlay3DSound(pSprite, 1204+Random(2), -1, 0);
3277          if (nSeq == 3)
3278              seqSpawn(dudeInfo[nType].seqStartID+10, 3, nXSprite, -1);
3279          else
3280              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3281          break;
3282      case kDudeGargoyleFlesh:
3283          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1405, -1, 0);
3284          else sfxPlay3DSound(pSprite, 1403+Random(2), -1, 0);
3285          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3286          break;
3287      case kDudeGargoyleStone:
3288          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1455, -1, 0);
3289          else sfxPlay3DSound(pSprite, 1453+Random(2), -1, 0);
3290          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3291          break;
3292      case kDudePhantasm:
3293          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1605, -1, 0);
3294          else sfxPlay3DSound(pSprite, 1603+Random(2), -1, 0);
3295          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3296          break;
3297      case kDudeHellHound:
3298          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1305, -1, 0);
3299          else sfxPlay3DSound(pSprite, 1303+Random(2), -1, 0);
3300          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3301          break;
3302      case kDudeHand:
3303          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1905, -1, 0);
3304          else sfxPlay3DSound(pSprite, 1903+Random(2), -1, 0);
3305          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3306          break;
3307      case kDudeSpiderBrown:
3308          if (pSprite->owner != -1) {
3309              spritetype *pOwner = &sprite[actSpriteOwnerToSpriteId(pSprite)];
3310              gDudeExtra[pOwner->extra].stats.birthCounter--;
3311          }
3312          
3313          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1805, -1, 0);
3314          else sfxPlay3DSound(pSprite, 1803+Random(2), -1, 0);
3315          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3316          break;
3317      case kDudeSpiderRed:
3318          if (pSprite->owner != -1) {
3319              spritetype *pOwner = &sprite[actSpriteOwnerToSpriteId(pSprite)];
3320              gDudeExtra[pOwner->extra].stats.birthCounter--;
3321          }
3322          
3323          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1805, -1, 0);
3324          else sfxPlay3DSound(pSprite, 1803+Random(2), -1, 0);
3325          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3326          break;
3327      case kDudeSpiderBlack:
3328          if (pSprite->owner != -1) {
3329              spritetype *pOwner = &sprite[actSpriteOwnerToSpriteId(pSprite)];
3330              gDudeExtra[pOwner->extra].stats.birthCounter--;
3331          }
3332          
3333          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1805, -1, 0);
3334          else sfxPlay3DSound(pSprite, 1803+Random(2), -1, 0);
3335          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3336          break;
3337      case kDudeSpiderMother:
3338          sfxPlay3DSound(pSprite, 1850, -1, 0);
3339          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3340          break;
3341      case kDudeGillBeast:
3342          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1705, -1, 0);
3343          else sfxPlay3DSound(pSprite, 1703+Random(2), -1, 0);
3344          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3345          break;
3346      case kDudeBoneEel:
3347          if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1505, -1, 0);
3348          else sfxPlay3DSound(pSprite, 1503+Random(2), -1, 0);
3349          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3350          break;
3351      case kDudeBat:
3352          if (Chance(0x4000) && nSeq == 3)
3353              sfxPlay3DSound(pSprite, 2005, -1, 0);
3354          else
3355              sfxPlay3DSound(pSprite, 2003+Random(2), -1, 0);
3356          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3357          break;
3358      case kDudeRat:
3359          if (Chance(0x4000) && nSeq == 3)
3360              sfxPlay3DSound(pSprite, 2105, -1, 0);
3361          else
3362              sfxPlay3DSound(pSprite, 2103+Random(2), -1, 0);
3363          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3364          break;
3365      case kDudePodGreen:
3366      case kDudeTentacleGreen:
3367      case kDudePodFire:
3368      case kDudeTentacleFire:
3369          if ((pSprite->cstat & CSTAT_SPRITE_YFLIP)) pSprite->cstat &= ~CSTAT_SPRITE_YFLIP;
3370          switch (pSprite->type) {
3371              case kDudePodGreen:
3372                  if (Chance(0x4000) && nSeq == 3)
3373                      sfxPlay3DSound(pSprite, 2205, -1, 0);
3374                  else
3375                      sfxPlay3DSound(pSprite, 2203 + Random(2), -1, 0);
3376                  seqSpawn(dudeInfo[nType].seqStartID + nSeq, 3, nXSprite, -1);
3377                  break;
3378              case kDudeTentacleGreen:
3379                  if (damage == 5)
3380                      sfxPlay3DSound(pSprite, 2471, -1, 0);
3381                  else
3382                      sfxPlay3DSound(pSprite, 2472, -1, 0);
3383                  seqSpawn(dudeInfo[nType].seqStartID + nSeq, 3, nXSprite, -1);
3384                  break;
3385              case kDudePodFire:
3386                  if (damage == 5)
3387                      sfxPlay3DSound(pSprite, 2451, -1, 0);
3388                  else
3389                      sfxPlay3DSound(pSprite, 2452, -1, 0);
3390                  seqSpawn(dudeInfo[nType].seqStartID + nSeq, 3, nXSprite, -1);
3391                  break;
3392              case kDudeTentacleFire:
3393                  sfxPlay3DSound(pSprite, 2501, -1, 0);
3394                  seqSpawn(dudeInfo[nType].seqStartID + nSeq, 3, nXSprite, -1);
3395                  break;
3396          }
3397          break;
3398      case kDudePodMother:
3399          if (Chance(0x4000) && nSeq == 3)
3400              sfxPlay3DSound(pSprite, 2205, -1, 0);
3401          else
3402              sfxPlay3DSound(pSprite, 2203+Random(2), -1, 0);
3403          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3404          break;
3405      case kDudeTentacleMother:
3406          if (Chance(0x4000) && nSeq == 3)
3407              sfxPlay3DSound(pSprite, 2205, -1, 0);
3408          else
3409              sfxPlay3DSound(pSprite, 2203+Random(2), -1, 0);
3410          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3411          break;
3412      case kDudeCerberusTwoHead:
3413          if (Chance(0x4000) && nSeq == 3)
3414              sfxPlay3DSound(pSprite, 2305, -1, 0);
3415          else
3416              sfxPlay3DSound(pSprite, 2305+Random(2), -1, 0);
3417          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3418          break;
3419      case kDudeCerberusOneHead:
3420          if (Chance(0x4000) && nSeq == 3)
3421              sfxPlay3DSound(pSprite, 2305, -1, 0);
3422          else
3423              sfxPlay3DSound(pSprite, 2305+Random(2), -1, 0);
3424          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3425          break;
3426      case kDudeTchernobog:
3427          sfxPlay3DSound(pSprite, 2380, -1, 0);
3428          seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1);
3429          break;
3430      case kDudeBurningTinyCaleb:
3431          damageType = kDamageExplode;
3432          seqSpawn(dudeInfo[nType].seqStartID+11, 3, nXSprite, nDudeToGibClient1);
3433          break;
3434      case kDudeBeast:
3435          sfxPlay3DSound(pSprite, 9000+Random(2), -1, 0);
3436          if (nSeq == 3)
3437              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient2);
3438          else
3439              seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1);
3440          break;
3441      case kDudeBurningBeast:
3442          damageType = kDamageExplode;
3443          seqSpawn(dudeInfo[nType].seqStartID+12, 3, nXSprite, nDudeToGibClient1);
3444          break;
3445      default:
3446          seqSpawn(getDudeInfo(nType+kDudeBase)->seqStartID+nSeq, 3, nXSprite, -1);
3447          break;
3448      }
3449      
3450      if (damageType == kDamageExplode)
3451      {
3452          DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
3453          for (int i = 0; i < 3; i++)
3454              if (pDudeInfo->nGibType[i] > -1)
3455                  GibSprite(pSprite, (GIBTYPE)pDudeInfo->nGibType[i], NULL, NULL);
3456          for (int i = 0; i < 4; i++)
3457              fxSpawnBlood(pSprite, damage);
3458          if ((gGameOptions.nGoreBehavior > 1) && !VanillaMode()) // for default, spawn more blood on explosion
3459          {
3460              switch (pSprite->type)
3461              {
3462              case kDudePhantasm:
3463              case kDudeHand:
3464              case kDudeSpiderBrown:
3465              case kDudeSpiderRed:
3466              case kDudeBoneEel:
3467              case kDudeBat:
3468              case kDudeRat:
3469              case kDudeTentacleGreen:
3470              case kDudeTentacleFire:
3471              case kDudeBurningTinyCaleb:
3472                  break;
3473              default:
3474                  for (int i = 0; i < 16; i++)
3475                      fxSpawnBlood(pSprite, damage);
3476                  for (int i = 0; i < 2; i++)
3477                      GibSprite(pSprite, GIBTYPE_7, NULL, NULL);
3478                  break;
3479              }
3480          }
3481      }
3482      gKillMgr.AddKill(pSprite);
3483      actCheckRespawn(pSprite);
3484      pSprite->type = kThingBloodChunks;
3485      actPostSprite(pSprite->index, kStatThing);
3486  }
3487  
3488  int actDamageSprite(int nSource, spritetype *pSprite, DAMAGE_TYPE damageType, int damage) {
3489      dassert(nSource < kMaxSprites);
3490  
3491      if (pSprite->flags&32 || pSprite->extra <= 0 || pSprite->extra >= kMaxXSprites || xsprite[pSprite->extra].reference != pSprite->index) 
3492          return 0;
3493      
3494      XSPRITE *pXSprite = &xsprite[pSprite->extra];
3495      if ((pXSprite->health == 0 && pSprite->statnum != kStatDude) || pXSprite->locked)
3496          return 0;
3497  
3498      if (nSource == -1) 
3499          nSource = pSprite->index;
3500      
3501      PLAYER *pSourcePlayer = NULL;
3502      if (IsPlayerSprite(&sprite[nSource])) pSourcePlayer = &gPlayer[sprite[nSource].type - kDudePlayer1];
3503      if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pSourcePlayer, pSprite)) return 0;
3504      if (!VanillaMode())
3505      {
3506          if ((gGameOptions.nRandomizerCheat == 12) && IsPlayerSprite(pSprite) && !actSpriteOwnerIsPlayer(pSprite) && (damageType != kDamageExplode)) // "WEED420!" random seed cheat (cultists only but they're green and make you dizzy on damage)
3507          {
3508              const int type = sprite[nSource].type;
3509              const char bWeedType = (type == kDudeCultistTommy) || (type == kDudeCultistShotgun) || (type == kDudeCultistTommyProne) || (type == kDudeCultistShotgunProne) || (type == kDudeCultistTesla) || (type == kDudeCultistTNT);
3510              if (bWeedType)
3511                  gPlayer[pSprite->type - kDudePlayer1].pwUpTime[kPwUpDeliriumShroom] = gPowerUpInfo[kPwUpDeliriumShroom].bonusTime >> 1;
3512          }
3513          if (gGameOptions.bQuadDamagePowerup)
3514          {
3515              if (IsPlayerSprite(&sprite[nSource]) && (&sprite[nSource] != pSprite) && powerupCheck(pSourcePlayer, kPwUpTwoGuns)) // if quad damage is active
3516                  damage *= 2;
3517          }
3518      }
3519      
3520      switch (pSprite->statnum) {
3521          case kStatDude: {
3522              if (!IsDudeSprite(pSprite)) {
3523                  consoleSysMsg("Bad Dude Failed: initial=%d type=%d %s\n", (int)pSprite->inittype, (int)pSprite->type, (int)(pSprite->flags & 16) ? "RESPAWN" : "NORMAL");
3524                  return damage >> 4;
3525                  //ThrowError("Bad Dude Failed: initial=%d type=%d %s\n", (int)pSprite->inittype, (int)pSprite->type, (int)(pSprite->flags & 16) ? "RESPAWN" : "NORMAL");
3526              }
3527  
3528              int nDamageFactor;
3529              #ifdef NOONE_EXTENSIONS
3530              if (pSprite->type == kDudeModernCustom)
3531                      nDamageFactor = cdudeGet(pSprite)->GetDamage(nSource, damageType);
3532                  else
3533                      nDamageFactor = getDudeInfo(pSprite->type)->curDamage[damageType];
3534              #else
3535                  nDamageFactor = getDudeInfo(pSprite->type)->curDamage[damageType];
3536              #endif
3537  
3538              const int kDamageBeforeScaling = damage;
3539              const char bWasAlive = (pXSprite->health > 0);
3540  
3541              if (!nDamageFactor) return 0;
3542              else if (nDamageFactor != 256)
3543                  damage = mulscale8(damage, nDamageFactor);
3544  
3545              if (!IsPlayerSprite(pSprite)) {
3546              
3547                  if (pXSprite->health <= 0) return 0;
3548                  damage = aiDamageSprite(pSprite, pXSprite, nSource, damageType, damage);
3549                  if (pXSprite->health <= 0)
3550                      actKillDude(nSource, pSprite, ((damageType == kDamageExplode && damage < 160) ? kDamageFall : damageType), damage);
3551          
3552              } else {
3553              
3554                  PLAYER *pPlayer = &gPlayer[pSprite->type - kDudePlayer1];
3555                  if (pXSprite->health > 0 || playerSeqPlaying(pPlayer, 16))
3556                      damage = playerDamageSprite(nSource, pPlayer, damageType, damage);
3557  
3558              }
3559  
3560              if (gSoundDing && (pSourcePlayer == gMe) && gMe && (pSprite != gMe->pSprite) && (damage > 0) && bWasAlive) {
3561                  for (int i = 0; i < 4; i++) {
3562                      DMGFEEDBACK *pSoundDmgSprite = &gSoundDingSprite[i];
3563                      if (pSoundDmgSprite->nSprite == -1) {
3564                          pSoundDmgSprite->nSprite = pSprite->index;
3565                          pSoundDmgSprite->nDamage = kDamageBeforeScaling;
3566                          break;
3567                      }
3568                      if (pSoundDmgSprite->nSprite == pSprite->index) {
3569                          pSoundDmgSprite->nDamage += kDamageBeforeScaling;
3570                          break;
3571                      }
3572                  }
3573              }
3574          }
3575          break;
3576      case kStatThing:
3577          dassert(pSprite->type >= kThingBase && pSprite->type < kThingMax);
3578          int nType = pSprite->type - kThingBase; int nDamageFactor = thingInfo[nType].dmgControl[damageType];
3579          
3580          if (!nDamageFactor) return 0;
3581          else if (nDamageFactor != 256)
3582              damage = mulscale8(damage, nDamageFactor);
3583  
3584          pXSprite->health = ClipLow(pXSprite->health - damage, 0);
3585          if (pXSprite->health <= 0) {
3586              switch (pSprite->type) {
3587                  case kThingDroppedLifeLeech:
3588                  #ifdef NOONE_EXTENSIONS
3589                  case kModernThingEnemyLifeLeech:
3590                  #endif
3591                      GibSprite(pSprite, GIBTYPE_14, NULL, NULL);
3592                      pXSprite->data1 = pXSprite->data2 =  pXSprite->data3 = pXSprite->DudeLockout = 0;
3593                      pXSprite->stateTimer =  pXSprite->data4 = pXSprite->isTriggered = 0;
3594  
3595                      #ifdef NOONE_EXTENSIONS
3596                      if (spriRangeIsFine(pSprite->owner))
3597                      {
3598                          spritetype* pOwner = &sprite[pSprite->owner];
3599                          if (IsCustomDude(pOwner))
3600                              cdudeGet(pOwner)->LeechKill(false);
3601                      }
3602                      #endif
3603                      break;
3604  
3605                  default:
3606                      if (!(pSprite->flags & kHitagRespawn))
3607                          actPropagateSpriteOwner(pSprite, &sprite[nSource]);
3608                      break;
3609              }
3610  
3611              trTriggerSprite(pSprite->index, pXSprite, kCmdOff, nSource);
3612              
3613              switch (pSprite->type) {
3614                  case kThingObjectGib:
3615                  case kThingObjectExplode:
3616                  case kThingBloodBits:
3617                  case kThingBloodChunks:
3618                  case kThingZombieHead:
3619                      if (damageType == 3 && pSourcePlayer && gFrameClock > pSourcePlayer->laughCount && Chance(0x4000)) {
3620                          const int nSound = gPlayerGibThingComments[Random(10)];
3621                          const bool playerAlive = VanillaMode() || (pSourcePlayer->pSprite && pSourcePlayer->pXSprite && (pSourcePlayer->pXSprite->health > 0)); // only trigger gib talk if player is alive
3622                          if (playerAlive && (gCalebTalk <= 1))
3623                              sfxPlay3DSound(pSourcePlayer->pSprite, nSound, 0, 2);
3624                          pSourcePlayer->laughCount = (int)gFrameClock+3600;
3625                      }
3626                      break;
3627                  case kTrapMachinegun:
3628                      seqSpawn(28, 3, pSprite->extra, -1);
3629                      break;
3630                  case kThingFluorescent:
3631                      seqSpawn(12, 3, pSprite->extra, -1);
3632                      GibSprite(pSprite, GIBTYPE_6, NULL, NULL);
3633                      break;
3634                  case kThingSpiderWeb:
3635                      seqSpawn(15, 3, pSprite->extra, -1);
3636                      break;
3637                  case kThingMetalGrate:
3638                      seqSpawn(21, 3, pSprite->extra, -1);
3639                      GibSprite(pSprite, GIBTYPE_4, NULL, NULL);
3640                      break;
3641                  case kThingFlammableTree:
3642                      switch (pXSprite->data1) {
3643                          case -1:
3644                              GibSprite(pSprite, GIBTYPE_14, NULL, NULL);
3645                              sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 312, pSprite->sectnum);
3646                              actPostSprite(pSprite->index, kStatFree);
3647                              break;
3648                          case 0:
3649                              seqSpawn(25, 3, pSprite->extra, nTreeToGibClient);
3650                              sfxPlay3DSound(pSprite, 351, -1, 0);
3651                              break;
3652                          case 1:
3653                              seqSpawn(26, 3, pSprite->extra, nTreeToGibClient);
3654                              sfxPlay3DSound(pSprite, 351, -1, 0);
3655                              break;
3656                      }
3657                      break;
3658              }
3659          }
3660          break;
3661      }
3662      
3663      return damage >> 4;
3664  }
3665  
3666  void actHitcodeToData(int a1, HITINFO *pHitInfo, int *a3, spritetype **a4, XSPRITE **a5, int *a6, walltype **a7, XWALL **a8, int *a9, sectortype **a10, XSECTOR **a11)
3667  {
3668      dassert(pHitInfo != NULL);
3669      int nSprite = -1;
3670      spritetype *pSprite = NULL;
3671      XSPRITE *pXSprite = NULL;
3672      int nWall = -1;
3673      walltype *pWall = NULL;
3674      XWALL *pXWall = NULL;
3675      int nSector = -1;
3676      sectortype *pSector = NULL;
3677      XSECTOR *pXSector = NULL;
3678      switch (a1)
3679      {
3680      case 3:
3681      case 5:
3682          nSprite = pHitInfo->hitsprite;
3683          dassert(nSprite >= 0 && nSprite < kMaxSprites);
3684          pSprite = &sprite[nSprite];
3685          if (pSprite->extra > 0)
3686              pXSprite = &xsprite[pSprite->extra];
3687          break;
3688      case 0:
3689      case 4:
3690          nWall = pHitInfo->hitwall;
3691          dassert(nWall >= 0 && nWall < kMaxWalls);
3692          pWall = &wall[nWall];
3693          if (pWall->extra > 0)
3694              pXWall = &xwall[pWall->extra];
3695          break;
3696      case 1:
3697      case 2:
3698      case 6:
3699          nSector = pHitInfo->hitsect;
3700          dassert(nSector >= 0 && nSector < kMaxSectors);
3701          pSector = &sector[nSector];
3702          if (pSector->extra > 0)
3703              pXSector = &xsector[pSector->extra];
3704          break;
3705      }
3706      if (a3)
3707          *a3 = nSprite;
3708      if (a4)
3709          *a4 = pSprite;
3710      if (a5)
3711          *a5 = pXSprite;
3712      if (a6)
3713          *a6 = nWall;
3714      if (a7)
3715          *a7 = pWall;
3716      if (a8)
3717          *a8 = pXWall;
3718      if (a9)
3719          *a9 = nSector;
3720      if (a10)
3721          *a10 = pSector;
3722      if (a11)
3723          *a11 = pXSector;
3724  }
3725  
3726  void actImpactMissile(spritetype *pMissile, int hitCode)
3727  {
3728      int nXMissile = pMissile->extra;
3729      dassert(nXMissile > 0 && nXMissile < kMaxXSprites);
3730      XSPRITE *pXMissile = &xsprite[pMissile->extra];
3731      
3732      int nSpriteHit = -1; int nWallHit = -1; int nSectorHit = -1;
3733      spritetype *pSpriteHit = NULL; XSPRITE *pXSpriteHit = NULL;
3734      walltype *pWallHit = NULL; XWALL *pXWallHit = NULL;
3735      sectortype *pSectorHit = NULL; XSECTOR *pXSectorHit = NULL;
3736  
3737      actHitcodeToData(hitCode, &gHitInfo, &nSpriteHit, &pSpriteHit, &pXSpriteHit, &nWallHit, &pWallHit, &pXWallHit, &nSectorHit, &pSectorHit, &pXSectorHit);
3738      THINGINFO *pThingInfo = NULL; DUDEINFO *pDudeInfo = NULL;
3739  
3740      if (hitCode == 3 && pSpriteHit) {
3741          switch (pSpriteHit->statnum) {
3742              case kStatThing:
3743                  pThingInfo = &thingInfo[pSpriteHit->type - kThingBase];
3744                  break;
3745              case kStatDude:
3746                  pDudeInfo = getDudeInfo(pSpriteHit->type);
3747                  break;
3748          }
3749      }
3750      switch (pMissile->type) {
3751          case kMissileLifeLeechRegular:
3752              if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) {
3753                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
3754                  DAMAGE_TYPE rand1 = (DAMAGE_TYPE)Random(7);
3755                  int rand2 = (7 + Random(7)) << 4;
3756                  int nDamage = actDamageSprite(nOwner, pSpriteHit, rand1, rand2);
3757                  if ((pThingInfo && pThingInfo->dmgControl[kDamageBurn] != 0) || (pDudeInfo && pDudeInfo->curDamage[kDamageBurn] != 0))
3758                      actBurnSprite(pMissile->owner, pXSpriteHit, 360);
3759  
3760                  // by NoOne: make Life Leech heal user, just like it was in 1.0x versions
3761                  if ((WeaponsV10x() || WeaponsNotBlood()) && !VanillaMode() && pDudeInfo != NULL) {
3762                      spritetype* pSource = &sprite[nOwner];
3763                      XSPRITE* pXSource = (pSource->extra >= 0) ? &xsprite[pSource->extra] : NULL;
3764  
3765                      if (IsDudeSprite(pSource) && pXSource != NULL && pXSource->health != 0)
3766  
3767                          actHealDude(pXSource, nDamage >> 2, getDudeInfo(pSource->type)->startHealth);
3768                  }
3769              }
3770          
3771              if (pMissile->extra > 0) {
3772                  actPostSprite(pMissile->index, kStatDecoration);
3773                  if (pMissile->ang == 1024) sfxPlay3DSound(pMissile, 307, -1, 0);
3774                  pMissile->type = kSpriteDecoration;
3775                  seqSpawn(9, 3, pMissile->extra, -1);
3776              } else {
3777                  actPostSprite(pMissile->index, kStatFree);
3778              }
3779  
3780              break;
3781          case kMissileTeslaAlt:
3782              teslaHit(pMissile, hitCode);
3783              switch (hitCode) {
3784                  case 0:
3785                  case 4:
3786                      if (pWallHit) {
3787                          spritetype* pFX = gFX.fxSpawn(FX_52, pMissile->sectnum, pMissile->x, pMissile->y, pMissile->z);
3788                          if (pFX) pFX->ang = (GetWallAngle(nWallHit) + 512) & 2047;
3789                      }
3790                      break;
3791              }
3792              GibSprite(pMissile, GIBTYPE_24, NULL, NULL);
3793              actPostSprite(pMissile->index, kStatFree);
3794              break;
3795          case kMissilePukeGreen:
3796              seqKill(3, nXMissile);
3797              if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo))
3798              {
3799                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
3800                  int nDamage = (15+Random(7))<<4;
3801                  actDamageSprite(nOwner, pSpriteHit, kDamageBullet, nDamage);
3802              }
3803              actPostSprite(pMissile->index, kStatFree);
3804              break;
3805          case kMissileArcGargoyle:
3806              sfxKill3DSound(pMissile, -1, -1);
3807              sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 306, pMissile->sectnum);
3808              GibSprite(pMissile, GIBTYPE_6, NULL, NULL);
3809              if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo))
3810              {
3811                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
3812                  int nDamage = (25+Random(20))<<4;
3813                  actDamageSprite(nOwner, pSpriteHit, kDamageSpirit, nDamage);
3814              }
3815              actPostSprite(pMissile->index, kStatFree);
3816              break;
3817          case kMissileLifeLeechAltNormal:
3818          case kMissileLifeLeechAltSmall:
3819              sfxKill3DSound(pMissile, -1, -1);
3820              sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 306, pMissile->sectnum);
3821              if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) {
3822                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
3823                  int nDmgMul = (pMissile->type == kMissileLifeLeechAltSmall) ? 6 : 3;
3824                  int nDamage = (nDmgMul+Random(nDmgMul))<<4;
3825                  if (WeaponsNotBlood() && !VanillaMode()) // increase the damage for lifeleech
3826                  {
3827                      if (!IsPlayerSprite(&sprite[pSpriteHit->index])) // target is not a player, do extra damage
3828                          nDamage *= 3;
3829                  }
3830                  actDamageSprite(nOwner, pSpriteHit, kDamageSpirit, nDamage);
3831              }
3832              actPostSprite(pMissile->index, kStatFree);
3833              break;
3834          case kMissileFireball:
3835          case kMissileFireballNapalm:
3836              if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo))
3837              {
3838                  if (pThingInfo && pSpriteHit->type == kThingTNTBarrel && pXSpriteHit->burnTime == 0)
3839                      evPost(nSpriteHit, 3, 0, kCallbackFXFlameLick);
3840                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
3841                  int nDamage = (50+Random(50))<<4;
3842                  actDamageSprite(nOwner, pSpriteHit, kDamageBullet, nDamage);
3843              }
3844              actExplodeSprite(pMissile);
3845              break;
3846          case kMissileFlareAlt:
3847              sfxKill3DSound(pMissile, -1, -1);
3848              actExplodeSprite(pMissile);
3849              break;
3850          case kMissileFlareRegular:
3851              sfxKill3DSound(pMissile, -1, -1);
3852              if ((hitCode == 3 && pSpriteHit) && (pThingInfo || pDudeInfo)) {
3853                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
3854                  if ((pThingInfo && pThingInfo->dmgControl[kDamageBurn] != 0) || (pDudeInfo && pDudeInfo->curDamage[kDamageBurn] != 0)) {
3855                      if (pThingInfo && pSpriteHit->type == kThingTNTBarrel && pXSpriteHit->burnTime == 0)
3856                          evPost(nSpriteHit, 3, 0, kCallbackFXFlameLick);
3857                  
3858                      actBurnSprite(pMissile->owner, pXSpriteHit, 480);
3859                      actRadiusDamage(nOwner, pMissile->x, pMissile->y, pMissile->z, pMissile->sectnum, 16, 20, 10, kDamageBullet, 6, 480);
3860  
3861                      // by NoOne: allow additional bullet damage for Flare Gun
3862                      if (WeaponsV10x() && !VanillaMode()) {
3863                          int nDamage = (20 + Random(10)) << 4;
3864                          actDamageSprite(nOwner, pSpriteHit, kDamageBullet, nDamage);
3865                      }
3866                  } else  {
3867                      int nDamage = (20+Random(10))<<4;
3868                      actDamageSprite(nOwner, pSpriteHit, kDamageBullet, nDamage);
3869                  }
3870              
3871                  if (surfType[pSpriteHit->picnum] == kSurfFlesh) {
3872                      pMissile->picnum = 2123;
3873                      pXMissile->target = nSpriteHit;
3874                      pXMissile->targetZ = pMissile->z-pSpriteHit->z;
3875                      pXMissile->goalAng = getangle(pMissile->x-pSpriteHit->x, pMissile->y-pSpriteHit->y)-pSpriteHit->ang;
3876                      pXMissile->state = 1;
3877                      actPostSprite(pMissile->index, kStatFlare);
3878                      pMissile->cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
3879                      break;
3880                  }
3881              }
3882              GibSprite(pMissile, GIBTYPE_17, NULL, NULL);
3883              actPostSprite(pMissile->index, kStatFree);
3884              break;
3885          case kMissileFlameSpray:
3886          case kMissileFlameHound:
3887              if (hitCode == 3)
3888              {
3889                  int nObject = gHitInfo.hitsprite;
3890                  dassert(nObject >= 0 && nObject < kMaxSprites);
3891                  spritetype *pObject = &sprite[nObject];
3892                  if (pObject->extra > 0)
3893                  {
3894                      XSPRITE *pXObject = &xsprite[pObject->extra];
3895                      if ((pObject->statnum == kStatThing || pObject->statnum == kStatDude) && pXObject->burnTime == 0)
3896                          evPost(nObject, 3, 0, kCallbackFXFlameLick);
3897                      int nOwner = actSpriteOwnerToSpriteId(pMissile);
3898                      actBurnSprite(pMissile->owner, pXObject, (4+gGameOptions.nDifficulty)<<2);
3899                      actDamageSprite(nOwner, pObject, kDamageBurn, 8);
3900                  }
3901              }
3902              break;
3903          case kMissileFireballCerberus:
3904              actExplodeSprite(pMissile);
3905              if (hitCode == 3)
3906              {
3907                  int nObject = gHitInfo.hitsprite;
3908                  dassert(nObject >= 0 && nObject < kMaxSprites);
3909                  spritetype *pObject = &sprite[nObject];
3910                  if (pObject->extra > 0)
3911                  {
3912                      XSPRITE *pXObject = &xsprite[pObject->extra];
3913                      if ((pObject->statnum == kStatThing || pObject->statnum == kStatDude) && pXObject->burnTime == 0)
3914                          evPost(nObject, 3, 0, kCallbackFXFlameLick);
3915                      int nOwner = actSpriteOwnerToSpriteId(pMissile);
3916                      actBurnSprite(pMissile->owner, pXObject, (4+gGameOptions.nDifficulty)<<2);
3917                      actDamageSprite(nOwner, pObject, kDamageBurn, 8);
3918                      int nDamage = (25+Random(10))<<4;
3919                      actDamageSprite(nOwner, pObject, kDamageBullet, nDamage);
3920                  }
3921              }
3922              actExplodeSprite(pMissile);
3923              break;
3924          case kMissileFireballTchernobog:
3925              actExplodeSprite(pMissile);
3926              if (hitCode == 3)
3927              {
3928                  int nObject = gHitInfo.hitsprite;
3929                  dassert(nObject >= 0 && nObject < kMaxSprites);
3930                  spritetype *pObject = &sprite[nObject];
3931                  if (pObject->extra > 0)
3932                  {
3933                      XSPRITE *pXObject = &xsprite[pObject->extra];
3934                      if ((pObject->statnum == kStatThing || pObject->statnum == kStatDude) && pXObject->burnTime == 0)
3935                          evPost(nObject, 3, 0, kCallbackFXFlameLick);
3936                      int nOwner = actSpriteOwnerToSpriteId(pMissile);
3937                      actBurnSprite(pMissile->owner, pXObject, 32);
3938                      actDamageSprite(nOwner, pObject, kDamageSpirit, 12);
3939                      int nDamage = (25+Random(10))<<4;
3940                      actDamageSprite(nOwner, pObject, kDamageBullet, nDamage);
3941                  }
3942              }
3943              actExplodeSprite(pMissile);
3944              break;
3945          case kMissileEctoSkull:
3946              sfxKill3DSound(pMissile, -1, -1);
3947              sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 522, pMissile->sectnum);
3948              actPostSprite(pMissile->index, kStatDebris);
3949              seqSpawn(20, 3, pMissile->extra, -1);
3950              if (hitCode == 3)
3951              {
3952                  int nObject = gHitInfo.hitsprite;
3953                  dassert(nObject >= 0 && nObject < kMaxSprites);
3954                  spritetype *pObject = &sprite[nObject];
3955                  if (pObject->statnum == kStatDude)
3956                  {
3957                      int nOwner = actSpriteOwnerToSpriteId(pMissile);
3958                      int nDamage = (25+Random(10))<<4;
3959                      actDamageSprite(nOwner, pObject, kDamageSpirit, nDamage);
3960                  }
3961              }
3962              break;
3963          case kMissileButcherKnife:
3964              actPostSprite(pMissile->index, kStatDebris);
3965              pMissile->cstat &= ~16;
3966              pMissile->type = kSpriteDecoration;
3967              seqSpawn(20, 3, pMissile->extra, -1);
3968              if (hitCode == 3)
3969              {
3970                  int nObject = gHitInfo.hitsprite;
3971                  dassert(nObject >= 0 && nObject < kMaxSprites);
3972                  spritetype *pObject = &sprite[nObject];
3973                  if (pObject->statnum == kStatDude)
3974                  {
3975                      int nOwner = actSpriteOwnerToSpriteId(pMissile);
3976                      int nDamage = (10+Random(10))<<4;
3977                      actDamageSprite(nOwner, pObject, kDamageSpirit, nDamage);
3978                      spritetype *pOwner = &sprite[nOwner];
3979                      XSPRITE *pXOwner = &xsprite[pOwner->extra];
3980                      int nType = pOwner->type-kDudeBase;
3981                      if (pXOwner->health > 0)
3982                          actHealDude(pXOwner, 10, getDudeInfo(nType+kDudeBase)->startHealth);
3983                  }
3984              }
3985              break;
3986          case kMissileTeslaRegular:
3987              if ((hitCode == 3) && pSpriteHit && WeaponsNotBlood() && !VanillaMode())
3988              {
3989                  if (IsPlayerSprite(pSpriteHit)) // if a player was shot, reflect tesla projectile
3990                  {
3991                      if (powerupCheck(&gPlayer[pSpriteHit->type - kDudePlayer1], kPwUpReflectShots))
3992                      {
3993                          xvel[pMissile->index] = -xvel[pMissile->index]; // return to sender
3994                          yvel[pMissile->index] = -yvel[pMissile->index];
3995                          zvel[pMissile->index] = -zvel[pMissile->index];
3996                          pMissile->owner = pSpriteHit->index; // set projectile owner as player with reflective shot
3997                          break;
3998                      }
3999                  }
4000              }
4001              sfxKill3DSound(pMissile, -1, -1);
4002              sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 518, pMissile->sectnum);
4003              GibSprite(pMissile, (hitCode == 2) ? GIBTYPE_23 : GIBTYPE_22, NULL, NULL);
4004              evKill(pMissile->index, 3);
4005              seqKill(3, nXMissile);
4006              actPostSprite(pMissile->index, kStatFree);
4007              if (hitCode == 3)
4008              {
4009                  int nObject = gHitInfo.hitsprite;
4010                  dassert(nObject >= 0 && nObject < kMaxSprites);
4011                  spritetype *pObject = &sprite[nObject];
4012                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
4013                  int nDamage = (15+Random(10))<<4;
4014                  actDamageSprite(nOwner, pObject, kDamageTesla, nDamage);
4015              }
4016              break;
4017          default:
4018              seqKill(3, nXMissile);
4019              actPostSprite(pMissile->index, kStatFree);
4020              if (hitCode == 3)
4021              {
4022                  int nObject = gHitInfo.hitsprite;
4023                  dassert(nObject >= 0 && nObject < kMaxSprites);
4024                  spritetype *pObject = &sprite[nObject];
4025                  int nOwner = actSpriteOwnerToSpriteId(pMissile);
4026                  int nDamage = (10+Random(10))<<4;
4027                  actDamageSprite(nOwner, pObject, kDamageFall, nDamage);
4028              }
4029              break;
4030      }
4031      
4032      #ifdef NOONE_EXTENSIONS
4033      if (gModernMap && pXSpriteHit && pXSpriteHit->state != pXSpriteHit->restState && pXSpriteHit->Impact)
4034      {
4035          int nOwner = actSpriteOwnerToSpriteId(pMissile);
4036          trTriggerSprite(nSpriteHit, pXSpriteHit, kCmdSpriteImpact, (nOwner >= 0) ? nOwner : kCauserGame);
4037      }
4038      #endif
4039      pMissile->cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
4040  }
4041  
4042  void actKickObject(spritetype *pSprite1, spritetype *pSprite2)
4043  {
4044      int nSprite1 = pSprite1->index;
4045      int nSprite2 = pSprite2->index;
4046      int nSpeed = ClipLow(approxDist(xvel[nSprite1], yvel[nSprite1])*2, 0xaaaaa);
4047      xvel[nSprite2] = mulscale30(nSpeed, Cos(pSprite1->ang+Random2(85)));
4048      yvel[nSprite2] = mulscale30(nSpeed, Sin(pSprite1->ang+Random2(85)));
4049      zvel[nSprite2] = mulscale14(nSpeed, -0x2000);
4050      pSprite2->flags = 7;
4051  }
4052  
4053  void actTouchFloor(spritetype *pSprite, int nSector)
4054  {
4055      dassert(pSprite != NULL);
4056      dassert(nSector >= 0 && nSector < kMaxSectors);
4057      sectortype * pSector = &sector[nSector];
4058      XSECTOR * pXSector = NULL;
4059      if (pSector->extra > 0)
4060          pXSector = &xsector[pSector->extra];
4061  
4062      bool doDamage = (pXSector && (pSector->type == kSectorDamage || pXSector->damageType > 0));
4063      // don't allow damage for damage sectors if they are not enabled
4064      #ifdef NOONE_EXTENSIONS
4065      if (gModernMap && doDamage && pSector->type == kSectorDamage && !pXSector->state)
4066          doDamage = false;
4067      #endif
4068      
4069      if (doDamage) {
4070          DAMAGE_TYPE nDamageType;
4071  
4072          if (pSector->type == kSectorDamage)
4073              nDamageType = (DAMAGE_TYPE)ClipRange(pXSector->damageType, kDamageFall, kDamageTesla);
4074          else
4075              nDamageType = (DAMAGE_TYPE)ClipRange(pXSector->damageType - 1, kDamageFall, kDamageTesla);
4076          int nDamage;
4077          if (pXSector->data)
4078              nDamage = ClipRange(pXSector->data, 0, 1000);
4079          else
4080              nDamage = 1000;
4081          actDamageSprite(pSprite->index, pSprite, nDamageType, scale(4, nDamage, 120) << 4);
4082      }
4083      if (tileGetSurfType(nSector, 0x4000) == kSurfLava)
4084      {
4085          actDamageSprite(pSprite->index, pSprite, kDamageBurn, 16);
4086          sfxPlay3DSound(pSprite, 352, 5, 2);
4087      }
4088  }
4089  
4090  void ProcessTouchObjects(spritetype *pSprite, int nXSprite)
4091  {
4092      int nSprite = pSprite->index;
4093      XSPRITE *pXSprite = &xsprite[nXSprite];
4094      SPRITEHIT *pSpriteHit = &gSpriteHit[nXSprite];
4095      PLAYER *pPlayer = NULL;
4096      if (IsPlayerSprite(pSprite))
4097          pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
4098      int nHitSprite = pSpriteHit->ceilhit & 0x3fff;
4099      switch (pSpriteHit->ceilhit&0xc000)
4100      {
4101      case 0x8000:
4102          break;
4103      case 0xc000:
4104          if (sprite[nHitSprite].extra > 0)
4105          {
4106              spritetype *pSprite2 = &sprite[nHitSprite];
4107              XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
4108              if ((pSprite2->statnum == kStatThing || pSprite2->statnum == kStatDude) && (xvel[nSprite] != 0 || yvel[nSprite] != 0 || zvel[nSprite] != 0))
4109              {
4110                  if (pSprite2->statnum == kStatThing)
4111                  {
4112                      int nType = pSprite2->type-kThingBase;
4113                      THINGINFO *pThingInfo = &thingInfo[nType];
4114                      if (pThingInfo->flags&1)
4115  
4116                          pSprite2->flags |= 1;
4117                      if (pThingInfo->flags&2)
4118  
4119                          pSprite2->flags |= 4;
4120                      // Inlined ?
4121                      xvel[pSprite2->index] += mulscale2(4, pSprite2->x-sprite[nSprite].x);
4122                      yvel[pSprite2->index] += mulscale2(4, pSprite2->y-sprite[nSprite].y);
4123                  }
4124                  else
4125                  {
4126  
4127                      pSprite2->flags |= 5;
4128                      xvel[pSprite2->index] += mulscale2(4, pSprite2->x-sprite[nSprite].x);
4129                      yvel[pSprite2->index] += mulscale2(4, pSprite2->y-sprite[nSprite].y);
4130                      
4131                      #ifdef NOONE_EXTENSIONS
4132                      // add size shroom abilities
4133                      if ((IsPlayerSprite(pSprite) && isShrinked(pSprite)) || (IsPlayerSprite(pSprite2) && isGrown(pSprite2))) {
4134  
4135                          int mass1 = getDudeInfo(pSprite2->type)->mass;
4136                          int mass2 = (IsCustomDude(pSprite)) ? cdudeGet(pSprite)->mass : getDudeInfo(pSprite->type)->mass;
4137  
4138                          if (mass1 > mass2)
4139                          {
4140                              int dmg = abs((mass1 - mass2) * (pSprite2->clipdist - pSprite->clipdist));
4141                              if (IsDudeSprite(pSprite2)) {
4142                                  if (dmg > 0)
4143                                      actDamageSprite(pSprite2->index, pSprite, (Chance(0x2000)) ? kDamageFall : (Chance(0x4000)) ? kDamageExplode : kDamageBullet, dmg);
4144  
4145                                  if (Chance(0x0200))
4146                                      actKickObject(pSprite2, pSprite);
4147                              }
4148                          }
4149                      }
4150                      #endif
4151                      if (!IsPlayerSprite(pSprite) || gPlayer[pSprite->type - kDudePlayer1].godMode == 0) {
4152                          switch (pSprite2->type) {
4153                              case kDudeTchernobog:
4154                                  actDamageSprite(pSprite2->index, pSprite, kDamageExplode, pXSprite->health << 2);
4155                                  break;
4156                          }
4157                              
4158                      }
4159                  }
4160              }
4161              
4162              if (pSprite2->type == kTrapSawCircular) {
4163                  if (!pXSprite2->state) actDamageSprite(nSprite, pSprite, kDamageBullet, 1);
4164                  else {
4165                      pXSprite2->data1 = 1;
4166                      pXSprite2->data2 = ClipHigh(pXSprite2->data2+8, 600);
4167                      actDamageSprite(nSprite, pSprite, kDamageBullet, 16);
4168                  }
4169              }
4170  
4171          }
4172          break;
4173      }
4174      nHitSprite = pSpriteHit->hit & 0x3fff;
4175      switch (pSpriteHit->hit&0xc000)
4176      {
4177      case 0x8000:
4178          break;
4179      case 0xc000:
4180          if (sprite[nHitSprite].extra > 0)
4181          {
4182              spritetype *pSprite2 = &sprite[nHitSprite];
4183  
4184              #ifdef NOONE_EXTENSIONS
4185              // add size shroom abilities
4186              if ((IsPlayerSprite(pSprite2) && isShrinked(pSprite2)) || (IsPlayerSprite(pSprite) && isGrown(pSprite)))
4187              {
4188                  if (xvel[pSprite->xvel] != 0 && IsDudeSprite(pSprite2))
4189                  {
4190                      int mass1 = getDudeInfo(pSprite->type)->mass;
4191                      int mass2 = (IsCustomDude(pSprite2)) ? cdudeGet(pSprite2)->mass : getDudeInfo(pSprite2->type)->mass;
4192  
4193                      if (mass1 > mass2)
4194                      {
4195                          actKickObject(pSprite, pSprite2);
4196                          sfxPlay3DSound(pSprite, 357, -1, 1);
4197                          int dmg = (mass1 - mass2) + abs(xvel[pSprite->index] >> 16);
4198                          if (dmg > 0)
4199                              actDamageSprite(nSprite, pSprite2, (Chance(0x2000)) ? kDamageFall : kDamageBullet, dmg);
4200                      }
4201                  }
4202              }
4203              #endif
4204              
4205              switch (pSprite2->type) {
4206                  case kThingKickablePail:
4207                      actKickObject(pSprite, pSprite2);
4208                      break;
4209                  case kThingZombieHead:
4210                      sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 357, pSprite->sectnum);
4211                      actKickObject(pSprite, pSprite2);
4212                      actDamageSprite(-1, pSprite2, kDamageFall, 80);
4213                      break;
4214                  case kDudeBurningInnocent:
4215                  case kDudeBurningCultist:
4216                  case kDudeBurningZombieAxe:
4217                  case kDudeBurningZombieButcher:
4218                      // This does not make sense
4219                      pXSprite->burnTime = ClipLow(pXSprite->burnTime-kTicsPerFrame, 0);
4220                      actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, kDamageBurn, 8);
4221                      break;
4222              }
4223          }
4224          break;
4225      }
4226      nHitSprite = pSpriteHit->florhit & 0x3fff;
4227      switch (pSpriteHit->florhit & 0xc000) {
4228      case 0x8000:
4229          break;
4230      case 0x4000:
4231          actTouchFloor(pSprite, nHitSprite);
4232          break;
4233      case 0xc000:
4234          if (sprite[nHitSprite].extra > 0)
4235          {
4236              spritetype *pSprite2 = &sprite[nHitSprite];
4237              XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
4238              
4239              #ifdef NOONE_EXTENSIONS
4240              // add size shroom abilities
4241              if ((IsPlayerSprite(pSprite2) && isShrinked(pSprite2)) || (IsPlayerSprite(pSprite) && isGrown(pSprite)))
4242              {
4243                  int mass1 = getDudeInfo(pSprite->type)->mass;
4244                  int mass2 = (IsCustomDude(pSprite2)) ? cdudeGet(pSprite2)->mass : getDudeInfo(pSprite2->type)->mass;
4245  
4246                  if (mass1 > mass2 && IsDudeSprite(pSprite2))
4247                  {
4248                      if ((IsPlayerSprite(pSprite2) && Chance(0x500)) || !IsPlayerSprite(pSprite2))
4249                          actKickObject(pSprite, pSprite2);
4250  
4251                      int dmg = (mass1 - mass2) + pSprite->clipdist;
4252                      if (dmg > 0)
4253                          actDamageSprite(nSprite, pSprite2, (Chance(0x2000)) ? kDamageFall : kDamageBullet, dmg);
4254                  }
4255              }
4256              #endif
4257  
4258              switch (pSprite2->type) {
4259              case kThingKickablePail:
4260                  if (pPlayer) {
4261                      if (pPlayer->kickPower > gFrameClock) return;
4262                      pPlayer->kickPower = (int)gFrameClock+60;
4263                  }
4264                  actKickObject(pSprite, pSprite2);
4265                  sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 357, pSprite->sectnum);
4266                  sfxPlay3DSound(pSprite, 374, 0, 0);
4267                  break;
4268              case kThingZombieHead:
4269                  if (pPlayer) {
4270                      if (pPlayer->kickPower > gFrameClock) return;
4271                      pPlayer->kickPower = (int)gFrameClock+60;
4272                  }
4273                  actKickObject(pSprite, pSprite2);
4274                  sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 357, pSprite->sectnum);
4275                  actDamageSprite(-1, pSprite2, kDamageFall, 80);
4276                  break;
4277              case kTrapSawCircular:
4278                  if (!pXSprite2->state) actDamageSprite(nSprite, pSprite, kDamageBullet, 1);
4279                  else {
4280                      pXSprite2->data1 = 1;
4281                      pXSprite2->data2 = ClipHigh(pXSprite2->data2+8, 600);
4282                      actDamageSprite(nSprite, pSprite, kDamageBullet, 16);
4283                  }
4284                  break;
4285              case kDudeCultistTommy:
4286              case kDudeCultistShotgun:
4287              case kDudeZombieAxeNormal:
4288              case kDudeZombieButcher:
4289              case kDudeZombieAxeBuried:
4290              case kDudeGargoyleFlesh:
4291              case kDudeGargoyleStone:
4292              case kDudePhantasm:
4293              case kDudeHellHound:
4294              case kDudeHand:
4295              case kDudeSpiderBrown:
4296              case kDudeSpiderRed:
4297              case kDudeSpiderBlack:
4298              case kDudeGillBeast:
4299              case kDudeBat:
4300              case kDudeRat:
4301              case kDudePodGreen:
4302              case kDudeTentacleGreen:
4303              case kDudePodFire:
4304              case kDudeTentacleFire:
4305              case kDudePodMother:
4306              case kDudeTentacleMother:
4307              case kDudeCerberusTwoHead:
4308              case kDudeCerberusOneHead:
4309              case kDudeTchernobog:
4310              case kDudePlayer1:
4311              case kDudePlayer2:
4312              case kDudePlayer3:
4313              case kDudePlayer4:
4314              case kDudePlayer5:
4315              case kDudePlayer6:
4316              case kDudePlayer7:
4317              case kDudePlayer8:
4318              #ifdef NOONE_EXTENSIONS
4319                  if (pPlayer && !isShrinked(pSprite))
4320              #else
4321                  if (pPlayer)
4322              #endif
4323                  actDamageSprite(nSprite, pSprite2,kDamageBullet, 8);
4324                  break;
4325              }
4326          }
4327          break;
4328      }
4329  
4330      #ifdef NOONE_EXTENSIONS
4331      // add more trigger statements for Touch flag
4332      if (gModernMap && IsDudeSprite(pSprite))
4333      {
4334          // Touch sprites
4335          if ((gSpriteHit[nXSprite].hit & 0xc000) == 0xc000)
4336              triggerTouchSprite(pSprite, gSpriteHit[nXSprite].hit & 0x3fff);
4337          
4338          if ((gSpriteHit[nXSprite].florhit & 0xc000) == 0xc000)
4339              triggerTouchSprite(pSprite, gSpriteHit[nXSprite].florhit & 0x3fff);
4340          
4341          if ((gSpriteHit[nXSprite].ceilhit & 0xc000) == 0xc000)
4342              triggerTouchSprite(pSprite, gSpriteHit[nXSprite].ceilhit & 0x3fff);
4343  
4344  
4345          // Touch walls
4346          if ((gSpriteHit[nXSprite].hit & 0xc000) == 0x8000)
4347              triggerTouchWall(pSprite, gSpriteHit[nXSprite].hit & 0x3fff);
4348      }
4349      #endif
4350  }
4351  
4352  void actAirDrag(spritetype *pSprite, int a2)
4353  {
4354      int vbp = 0;
4355      int v4 = 0;
4356      int nSector = pSprite->sectnum;
4357      dassert(nSector >= 0 && nSector < kMaxSectors);
4358      sectortype *pSector = &sector[nSector];
4359      int nXSector = pSector->extra;
4360      if (nXSector > 0)
4361      {
4362          dassert(nXSector < kMaxXSectors);
4363          XSECTOR *pXSector = &xsector[nXSector];
4364          if (pXSector->windVel && (pXSector->windAlways || pXSector->busy))
4365          {
4366              int vcx = pXSector->windVel<<12;
4367              if (!pXSector->windAlways && pXSector->busy)
4368                  vcx = mulscale16(vcx, pXSector->busy);
4369              vbp = mulscale30(vcx, Cos(pXSector->windAng));
4370              v4 = mulscale30(vcx, Sin(pXSector->windAng));
4371          }
4372      }
4373      xvel[pSprite->index] += mulscale16(vbp-xvel[pSprite->index], a2);
4374      yvel[pSprite->index] += mulscale16(v4-yvel[pSprite->index], a2);
4375      zvel[pSprite->index] -= mulscale16(zvel[pSprite->index], a2);
4376  }
4377  
4378  static int NotBloodAdjustHitbox(spritetype *pSprite, int top, int bottom, int walldist)
4379  {
4380      if (pSprite == NULL)
4381          return 0;
4382      const int nSprite = pSprite->index;
4383      if (!spriRangeIsFine(nSprite)) // invalid sprite, don't bother processing
4384          return 0;
4385      if (!actSpriteOwnerIsPlayer(pSprite)) // if sprite is not player owned/spawned
4386          return 0;
4387  
4388      int smallwd;
4389      switch (pSprite->type)
4390      {
4391      case kMissileFlareRegular: // for the flare gun, make the walldist argument extra small
4392          smallwd = min(walldist, 16);
4393          break;
4394      case kThingArmedTNTBundle:
4395      case kThingArmedProxBomb:
4396      case kThingArmedRemoteBomb:
4397      case kThingArmedSpray:
4398          smallwd = min(walldist, 32);
4399          break;
4400      case kMissileFireballNapalm:
4401      case kMissileTeslaRegular:
4402          smallwd = min(walldist, 48); // unless sprite is less than 48 units, clamp at 48 units
4403          break;
4404      default: // unexpected sprite, don't use small hitbox
4405          return 0;
4406      }
4407  
4408      vec3_t tempxyz = {pSprite->x, pSprite->y, pSprite->z};
4409      int tempsec = pSprite->sectnum;
4410      const int moved = ClipMoveEDuke(&tempxyz.x, &tempxyz.y, &tempxyz.z, &tempsec, xvel[nSprite]>>12, yvel[nSprite]>>12, walldist, (pSprite->z-top)/4, (bottom-pSprite->z)/4, CLIPMASK0);
4411      if ((moved & 0xc000) == 0x8000) // use a small hitbox if the sprite collided with a wall
4412          return smallwd;
4413      return walldist;
4414  }
4415  
4416  int MoveThing(spritetype *pSprite)
4417  {
4418      int nXSprite = pSprite->extra;
4419      dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
4420      XSPRITE *pXSprite = &xsprite[nXSprite];
4421      int nSprite = pSprite->index;
4422      int v8 = 0;
4423      dassert(pSprite->type >= kThingBase && pSprite->type < kThingMax);
4424      THINGINFO *pThingInfo = &thingInfo[pSprite->type-kThingBase];
4425      int nSector = pSprite->sectnum;
4426      dassert(nSector >= 0 && nSector < kMaxSectors);
4427      int top, bottom;
4428      GetSpriteExtents(pSprite, &top, &bottom);
4429      if (xvel[nSprite] || yvel[nSprite])
4430      {
4431          int wd = pSprite->clipdist<<2;
4432          short bakCstat = pSprite->cstat;
4433          pSprite->cstat &= ~257;
4434          if (ProjectilesRaze() && (pSprite->owner >= 0) && !VanillaMode()) // improved clipmove accuracy (raze)
4435          {
4436              v8 = gSpriteHit[nXSprite].hit = ClipMoveEDuke((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, wd, (pSprite->z-top)/4, (bottom-pSprite->z)/4, CLIPMASK0);
4437          }
4438          else if (ProjectilesNotBlood() && (pSprite->owner >= 0) && !VanillaMode()) // improved clipmove accuracy and adjust hitboxes (notblood)
4439          {
4440              spritetype *raySprite = NULL;
4441              const int tinywd = NotBloodAdjustHitbox(pSprite, top, bottom, wd);
4442              if (tinywd) // if object owned by player, use smaller hitboxes for specific player owned items
4443              {
4444                  wd = tinywd;
4445                  raySprite = pSprite; // set raycast collisions to be used
4446              }
4447              v8 = gSpriteHit[nXSprite].hit = ClipMoveEDuke((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, wd, (pSprite->z-top)/4, (bottom-pSprite->z)/4, CLIPMASK0, raySprite);
4448          }
4449          else // original
4450          {
4451              v8 = gSpriteHit[nXSprite].hit = ClipMove((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, wd, (pSprite->z-top)/4, (bottom-pSprite->z)/4, CLIPMASK0);
4452          }
4453          pSprite->cstat = bakCstat;
4454          dassert(nSector >= 0);
4455          if (pSprite->sectnum != nSector)
4456          {
4457              dassert(nSector >= 0 && nSector < kMaxSectors);
4458              ChangeSpriteSect(nSprite, nSector);
4459          }
4460          if ((gSpriteHit[nXSprite].hit&0xc000) == 0x8000) {
4461              int nHitWall = gSpriteHit[nXSprite].hit&0x3fff;
4462              bool bounce = true;
4463              if (ProjectilesNotBlood() && (wall[nHitWall].nextsector != -1) && !VanillaMode()) { // if sprite didn't hit a solid wall
4464                  if (actSpriteOwnerIsPlayer(pSprite) || (pSprite->type == kThingZombieHead) || (pSprite->type == kThingKickablePail)) { // if sprite is owned by a player, or is a non-played owned zombie head/metal pail
4465                      switch (pSprite->type) {
4466                          case kThingArmedTNTBundle: // filter out these sprites
4467                          case kThingArmedProxBomb:
4468                          case kThingArmedRemoteBomb:
4469                          case kThingArmedSpray:
4470                          case kThingNapalmBall:
4471                          case kThingDroppedLifeLeech:
4472                          case kThingZombieHead:
4473                          case kThingKickablePail:
4474                          {
4475                              int32_t fz, cz;
4476                              getzsofslope(wall[nHitWall].nextsector, pSprite->x, pSprite->y, &cz, &fz);
4477                              if (!(pSprite->z <= cz || pSprite->z >= fz)) { // if sprite didn't hit the ceiling/floor
4478                                  walltype *pWall = &wall[nHitWall];
4479                                  if (pWall->extra > 0) {
4480                                      XWALL *pXWall = &xwall[pWall->extra];
4481                                      if (pXWall->triggerVector) { // break tile (glass, etc)
4482                                          const int nOwner = actSpriteOwnerToSpriteId(pSprite);
4483                                          trTriggerWall(nHitWall, pXWall, kCmdWallImpact, (nOwner >= 0) ? nOwner : nSprite);
4484                                          bounce = false;
4485                                          xvel[nSprite] >>= 1; // reduce speed by half
4486                                          yvel[nSprite] >>= 1;
4487                                          zvel[nSprite] += 58254;
4488                                      }
4489                                  }
4490                              }
4491                              break;
4492                          }
4493                          default:
4494                              break;
4495                      }
4496                  }
4497              }
4498              if (bounce)
4499                  actWallBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], nHitWall, pThingInfo->elastic);
4500              switch (pSprite->type) {
4501                  case kThingZombieHead:
4502                      sfxPlay3DSound(pSprite, 607, 0, 0);
4503                      actDamageSprite(-1, pSprite, kDamageFall, 80);
4504                      break;
4505                  case kThingDroppedLifeLeech: // play sfx on wall bounce for lifeleech
4506                      if (!WeaponsNotBlood() || VanillaMode())
4507                          break;
4508                      if (klabs(zvel[nSprite]) > 0x20000)
4509                          sfxPlay3DSound(pSprite, 816 + Random(2), 0, 0);
4510                      break;
4511                  case kThingKickablePail:
4512                      sfxPlay3DSound(pSprite, 374, 0, 0);
4513                      break;
4514              }
4515          }
4516      }
4517      else
4518      {
4519          dassert(nSector >= 0 && nSector < kMaxSectors);
4520          FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector);
4521      }
4522      if (zvel[nSprite])
4523      {
4524          char bUnderwater = 0;
4525          if (IsUnderwaterSector(nSector) && gGameOptions.bSectorBehavior && !VanillaMode()) // lower gravity for underwater bodies/things
4526              bUnderwater = !actSpriteOwnerIsPlayer(pSprite) && !actSpriteOwnerIsDude(pSprite); // check if sprite is not owned by dude/player
4527          if (bUnderwater)
4528              pSprite->z += zvel[nSprite]>>10;
4529          else
4530              pSprite->z += zvel[nSprite]>>8;
4531      }
4532      int ceilZ, ceilHit, floorZ, floorHit;
4533      GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0);
4534      GetSpriteExtents(pSprite, &top, &bottom);
4535      if ((pSprite->flags & 2) && bottom < floorZ)
4536      {
4537          pSprite->z += 455;
4538          zvel[nSprite] += 58254;
4539          if (pSprite->type == kThingZombieHead)
4540          {
4541              spritetype *pFX = gFX.fxSpawn(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
4542              if (pFX)
4543              {
4544                  int v34 = ((int)gFrameClock*3)&2047;
4545                  int v30 = ((int)gFrameClock*5)&2047;
4546                  int vbx = ((int)gFrameClock*11)&2047;
4547                  int v2c = 0x44444;
4548                  int v28 = 0;
4549                  int v24 = 0;
4550                  RotateVector(&v2c,&v28,vbx);
4551                  RotateVector(&v2c,&v24,v30);
4552                  RotateVector(&v28,&v24,v34);
4553                  xvel[pFX->index] = xvel[pSprite->index]+v2c;
4554                  yvel[pFX->index] = yvel[pSprite->index]+v28;
4555                  zvel[pFX->index] = zvel[pSprite->index]+v24;
4556              }
4557          }
4558      }
4559      char bIgnoreROR = 0;
4560      if ((pSprite->type == kThingDripWater || pSprite->type == kThingDripBlood) && gGameOptions.bSectorBehavior && !VanillaMode()) // check if droplet is moving through ror sector
4561      {
4562          const vec3_t kBakPos = pSprite->xyz;
4563          const int kBakSect = nSector;
4564          bIgnoreROR = CheckLink(&pSprite->x, &pSprite->y, &pSprite->z, &nSector) && IsUnderwaterSector(nSector); // only allow ror traversal if ror sector is not underwater (fixes bug where droplets move through water)
4565          pSprite->xyz = kBakPos;
4566          nSector = kBakSect;
4567      }
4568      if (bIgnoreROR || CheckLink(pSprite))
4569          GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0);
4570      GetSpriteExtents(pSprite, &top, &bottom);
4571      if (bottom >= floorZ)
4572      {
4573          actTouchFloor(pSprite, pSprite->sectnum);
4574          gSpriteHit[nXSprite].florhit = floorHit;
4575          pSprite->z += floorZ-bottom;
4576          int v20 = zvel[nSprite]-velFloor[pSprite->sectnum];
4577          if (v20 > 0)
4578          {
4579  
4580              pSprite->flags |= 4;
4581              int vax = actFloorBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], (int*)&v20, pSprite->sectnum, pThingInfo->elastic);
4582              int nDamage = mulscale30(vax, vax)-pThingInfo->dmgResist;
4583              if (nDamage > 0)
4584                  actDamageSprite(nSprite, pSprite, kDamageFall, nDamage);
4585              zvel[nSprite] = v20;
4586              if (velFloor[pSprite->sectnum] == 0 && klabs(zvel[nSprite]) < 0x10000)
4587              {
4588                  zvel[nSprite] = 0;
4589  
4590                  pSprite->flags &= ~4;
4591              }
4592              
4593              switch (pSprite->type) {
4594                  case kThingNapalmBall:
4595                      if (zvel[nSprite] == 0 || Chance(0xA000)) actNapalmMove(pSprite, pXSprite);
4596                      break;
4597                  case kThingZombieHead:
4598                      if (klabs(zvel[nSprite]) > 0x80000) {
4599                          sfxPlay3DSound(pSprite, 607, 0, 0);
4600                          actDamageSprite(-1, pSprite, kDamageFall, 80);
4601                      }
4602                      break;
4603                  case kThingDroppedLifeLeech: // play sfx on floor bounce for lifeleech
4604                      if (!WeaponsNotBlood() || VanillaMode())
4605                          break;
4606                      if (klabs(zvel[nSprite]) > 0x40000)
4607                          sfxPlay3DSound(pSprite, 816 + Random(2), 0, 0);
4608                      break;
4609                  case kThingKickablePail:
4610                      if (klabs(zvel[nSprite]) > 0x80000)
4611                          sfxPlay3DSound(pSprite, 374, 0, 0);
4612                      break;
4613                  case kThingArmedTNTBundle:
4614                  case kThingArmedSpray:
4615                      if (ProjectilesNotBlood() && (pSprite->owner >= 0) && !VanillaMode()) // force tnt/spray to 'hit' (explode) if landed directly on top of enemy
4616                      {
4617                          if ((floorHit & 0xc000) == 0xc000)
4618                          {
4619                              int nHitDude = floorHit & 0x3fff;
4620                              if (spriRangeIsFine(nHitDude) && IsDudeSprite(&sprite[nHitDude]))
4621                                  return 0x8000|nHitDude;
4622                          }
4623                      }
4624                      break;
4625              }
4626  
4627              v8 = 0x4000|nSector;
4628          }
4629          else if (zvel[nSprite] == 0)
4630              pSprite->flags &= ~4;
4631      }
4632      else
4633      {
4634          gSpriteHit[nXSprite].florhit = 0;
4635  
4636          if (pSprite->flags&2)
4637              pSprite->flags |= 4;
4638      }
4639      if (top <= ceilZ)
4640      {
4641          gSpriteHit[nXSprite].ceilhit = ceilHit;
4642          pSprite->z += ClipLow(ceilZ-top, 0);
4643          if (zvel[nSprite] < 0)
4644          {
4645              xvel[nSprite] = mulscale16(xvel[nSprite], 0xc000);
4646              yvel[nSprite] = mulscale16(yvel[nSprite], 0xc000);
4647              zvel[nSprite] = mulscale16(-zvel[nSprite], 0x4000);
4648              switch (pSprite->type) {
4649                  case kThingZombieHead:
4650                      if (klabs(zvel[nSprite]) > 0x80000) {
4651                          sfxPlay3DSound(pSprite, 607, 0, 0);
4652                          actDamageSprite(-1, pSprite, kDamageFall, 80);
4653                      }
4654                      break;
4655                  case kThingKickablePail:
4656                      if (klabs(zvel[nSprite]) > 0x80000)
4657                          sfxPlay3DSound(pSprite, 374, 0, 0);
4658                      break;
4659              }
4660          }
4661      }
4662      else
4663          gSpriteHit[nXSprite].ceilhit = 0;
4664      if (bottom >= floorZ)
4665      {
4666          int nVel = approxDist(xvel[nSprite], yvel[nSprite]);
4667          int nVelClipped = ClipHigh(nVel, 0x11111);
4668          if ((floorHit & 0xc000) == 0xc000)
4669          {
4670              int nHitSprite = floorHit & 0x3fff;
4671              if ((sprite[nHitSprite].cstat & 0x30) == 0)
4672              {
4673                  xvel[nSprite] += mulscale2(4, pSprite->x - sprite[nHitSprite].x);
4674                  yvel[nSprite] += mulscale2(4, pSprite->y - sprite[nHitSprite].y);
4675                  v8 = gSpriteHit[nXSprite].hit;
4676              }
4677          }
4678          if (nVel > 0)
4679          {
4680              int t = divscale16(nVelClipped, nVel);
4681              xvel[nSprite] -= mulscale16(t, xvel[nSprite]);
4682              yvel[nSprite] -= mulscale16(t, yvel[nSprite]);
4683          }
4684      }
4685      if (xvel[nSprite] || yvel[nSprite])
4686          pSprite->ang = getangle(xvel[nSprite], yvel[nSprite]);
4687      return v8;
4688  }
4689  
4690  void MoveDude(spritetype *pSprite)
4691  {
4692      int nXSprite = pSprite->extra;
4693      XSPRITE *pXSprite = &xsprite[nXSprite];
4694      int nSprite = pSprite->index;
4695      PLAYER *pPlayer = NULL;
4696      if (IsPlayerSprite(pSprite))
4697          pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
4698      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
4699          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
4700          return;
4701      }
4702      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
4703      int top, bottom;
4704      GetSpriteExtents(pSprite, &top, &bottom);
4705      int bz = (bottom-pSprite->z)/4;
4706      int tz = (pSprite->z-top)/4;
4707      int wd = pSprite->clipdist<<2;
4708      int nSector = pSprite->sectnum;
4709      int nAiStateType = (pXSprite->aiState) ? pXSprite->aiState->stateType : -1;
4710  
4711      dassert(nSector >= 0 && nSector < kMaxSectors);
4712      if (pPlayer && gFlyMode && pXSprite->health > 0)
4713      {
4714          static int nSpeed = 0;
4715          const char nShift = gSonicMode && !VanillaMode() ? 2 : 3;
4716          nSpeed = !pPlayer->input.buttonFlags.crouch && !pPlayer->input.buttonFlags.jump ? 0 : ClipHigh(nSpeed+(pPlayer->input.buttonFlags.crouch ? 0x1553>>1 : 0x1553), 0x5b05);
4717  
4718          if (pPlayer->input.buttonFlags.jump)
4719              pSprite->z -= nSpeed>>nShift;
4720          if (pPlayer->input.buttonFlags.crouch)
4721              pSprite->z += nSpeed>>nShift;
4722      }
4723      if (xvel[nSprite] || yvel[nSprite])
4724      {
4725          int vx = xvel[nSprite];
4726          int vy = yvel[nSprite];
4727          if (gGameOptions.nEnemySpeed && !pPlayer) // if enemy speed modifier is enabled
4728          {
4729              vx = mulscale16(vx, (fix16_from_int(gGameOptions.nEnemySpeed)>>1) + fix16_from_int(1));
4730              vy = mulscale16(vy, (fix16_from_int(gGameOptions.nEnemySpeed)>>1) + fix16_from_int(1));
4731          }
4732          else if (pPlayer && gGameOptions.nPlayerSpeed && !VanillaMode())
4733          {
4734              vx = mulscale16(vx, (fix16_from_int(gGameOptions.nPlayerSpeed)>>2) + fix16_from_int(1));
4735              vy = mulscale16(vy, (fix16_from_int(gGameOptions.nPlayerSpeed)>>2) + fix16_from_int(1));
4736          }
4737          vx >>= 12;
4738          vy >>= 12;
4739          if (pPlayer && gSonicMode && !VanillaMode()) // if sonic mode cheat is active
4740          {
4741              vx <<= 1; // double player velocity
4742              vy <<= 1;
4743          }
4744          if (pPlayer && gNoClip)
4745          {
4746              pSprite->x += vx;
4747              pSprite->y += vy;
4748              if (!FindSector(pSprite->x, pSprite->y, &nSector))
4749                  nSector = pSprite->sectnum;
4750          }
4751          else
4752          {
4753              short bakCstat = pSprite->cstat;
4754              pSprite->cstat &= ~257;
4755              gSpriteHit[nXSprite].hit = ClipMove((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, vx, vy, wd, tz, bz, CLIPMASK0);
4756              if (nSector == -1)
4757              {
4758                  nSector = pSprite->sectnum;
4759                  if (pSprite->statnum == kStatDude || pSprite->statnum == kStatThing)
4760                      actDamageSprite(pSprite->index, pSprite, kDamageFall, 1000<<4);
4761              }
4762  
4763              if (sector[nSector].type >= kSectorPath && sector[nSector].type <= kSectorRotate)
4764              {
4765                  short nSector2 = nSector;
4766                  if (pushmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector2, wd, tz, bz, CLIPMASK0) == -1)
4767                      actDamageSprite(nSprite, pSprite, kDamageFall, 1000 << 4);
4768                  if (nSector2 != -1)
4769                      nSector = nSector2;
4770              }
4771              dassert(nSector >= 0);
4772              pSprite->cstat = bakCstat;
4773          }
4774          switch (gSpriteHit[nXSprite].hit&0xc000)
4775          {
4776          case 0xc000:
4777          {
4778              int nHitSprite = gSpriteHit[nXSprite].hit&0x3fff;
4779              spritetype *pHitSprite = &sprite[nHitSprite];
4780              XSPRITE *pHitXSprite = NULL;
4781              // Should be pHitSprite here
4782              if (pSprite->extra > 0 && (unsigned)pHitSprite->extra < kMaxXSprites)
4783                  pHitXSprite = &xsprite[pHitSprite->extra];
4784              int nOwner = actSpriteOwnerToSpriteId(pHitSprite);
4785  
4786              if (pHitSprite->statnum == kStatProjectile && !(pHitSprite->flags&32) && pSprite->index != nOwner)
4787              {
4788                  HITINFO hitInfo = gHitInfo;
4789                  gHitInfo.hitsprite = nSprite;
4790                  actImpactMissile(pHitSprite, 3);
4791                  gHitInfo = hitInfo;
4792              }
4793              #ifdef NOONE_EXTENSIONS
4794              if (!gModernMap && pHitXSprite && pHitXSprite->Touch && !pHitXSprite->state && !pHitXSprite->isTriggered)
4795                  trTriggerSprite(nHitSprite, pHitXSprite, kCmdSpriteTouch, pSprite->index);
4796              #else
4797                  if (pHitXSprite && pHitXSprite->Touch && !pHitXSprite->state && !pHitXSprite->isTriggered)
4798                      trTriggerSprite(nHitSprite, pHitXSprite, kCmdSpriteTouch, pSprite->index);
4799              #endif
4800  
4801              if (pDudeInfo->lockOut && pHitXSprite && pHitXSprite->Push && !pHitXSprite->key && !pHitXSprite->DudeLockout && !pHitXSprite->state && !pHitXSprite->busy && !pPlayer)
4802                  trTriggerSprite(nHitSprite, pHitXSprite, kCmdSpritePush, pSprite->index);
4803  
4804              break;
4805          }
4806          case 0x8000:
4807          {
4808              int nHitWall = gSpriteHit[nXSprite].hit&0x3fff;
4809              walltype *pHitWall = &wall[nHitWall];
4810              XWALL *pHitXWall = NULL;
4811              if (pHitWall->extra > 0)
4812                  pHitXWall = &xwall[pHitWall->extra];
4813              if (pDudeInfo->lockOut && pHitXWall && pHitXWall->triggerPush && !pHitXWall->key && !pHitXWall->dudeLockout && !pHitXWall->state && !pHitXWall->busy && !pPlayer)
4814                  trTriggerWall(nHitWall, pHitXWall, kCmdWallPush, pSprite->index);
4815              if (pHitWall->nextsector != -1)
4816              {
4817                  sectortype *pHitSector = &sector[pHitWall->nextsector];
4818                  XSECTOR *pHitXSector = NULL;
4819                  if (pHitSector->extra > 0)
4820                      pHitXSector = &xsector[pHitSector->extra];
4821                  if (pDudeInfo->lockOut && pHitXSector && pHitXSector->Wallpush && !pHitXSector->Key && !pHitXSector->dudeLockout && !pHitXSector->state && !pHitXSector->busy && !pPlayer)
4822                      trTriggerSector(pHitWall->nextsector, pHitXSector, kCmdSectorPush, pSprite->index);
4823                  if (top < pHitSector->ceilingz || bottom > pHitSector->floorz)
4824                  {
4825                      // ???
4826                  }
4827              }
4828              actWallBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], nHitWall, 0);
4829              break;
4830          }
4831          }
4832      }
4833      else
4834      {
4835          dassert(nSector >= 0 && nSector < kMaxSectors);
4836          FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector);
4837      }
4838      if (pSprite->sectnum != nSector)
4839      {
4840          dassert(nSector >= 0 && nSector < kMaxSectors);
4841          XSECTOR *pXSector;
4842          int nXSector = sector[pSprite->sectnum].extra;
4843          if (nXSector > 0)
4844              pXSector = &xsector[nXSector];
4845          else
4846              pXSector = NULL;
4847          if (pXSector && pXSector->Exit && (pPlayer || !pXSector->dudeLockout))
4848              trTriggerSector(pSprite->sectnum, pXSector, kCmdSectorExit, pSprite->index);
4849          ChangeSpriteSect(nSprite, nSector);
4850          
4851          nXSector = sector[nSector].extra;
4852          pXSector = (nXSector > 0) ? &xsector[nXSector] : NULL;
4853          if (pXSector && pXSector->Enter && (pPlayer || !pXSector->dudeLockout)) {
4854  
4855              if (sector[nSector].type == kSectorTeleport)
4856                  pXSector->data = pPlayer ? nSprite : -1;
4857              trTriggerSector(nSector, pXSector, kCmdSectorEnter, pSprite->index);
4858          }
4859  
4860          nSector = pSprite->sectnum;
4861      }
4862      char bUnderwater = 0;
4863      char bDepth = 0;
4864      if (sector[nSector].extra > 0)
4865      {
4866          XSECTOR *pXSector = &xsector[sector[nSector].extra];
4867          if (pXSector->Underwater)
4868              bUnderwater = 1;
4869          if (pXSector->Depth)
4870              bDepth = 1;
4871      }
4872      int nUpperLink = gUpperLink[nSector];
4873      int nLowerLink = gLowerLink[nSector];
4874      if (nUpperLink >= 0 && (sprite[nUpperLink].type == kMarkerUpWater || sprite[nUpperLink].type == kMarkerUpGoo))
4875          bDepth = 1;
4876      if (nLowerLink >= 0 && (sprite[nLowerLink].type == kMarkerLowWater || sprite[nLowerLink].type == kMarkerLowGoo))
4877          bDepth = 1;
4878      if (pPlayer)
4879          wd += 16;
4880      if (pPlayer && gFlyMode)
4881      {
4882          pPlayer->underwaterTime = 1200;
4883          pPlayer->chokeEffect = 0;
4884          pSprite->flags |= 2;
4885          if (!bUnderwater)
4886          {
4887              xvel[nSprite] -= mulscale16r(xvel[nSprite], 3696)<<1;
4888              yvel[nSprite] -= mulscale16r(yvel[nSprite], 3696)<<1;
4889          }
4890          bUnderwater = 1;
4891      }
4892      else if (zvel[nSprite])
4893          pSprite->z += zvel[nSprite]>>8;
4894      int ceilZ, ceilHit, floorZ, floorHit;
4895      GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR);
4896      GetSpriteExtents(pSprite, &top, &bottom);
4897  
4898      if (pSprite->flags & 2)
4899      {
4900          int vc = 58254;
4901          if (bDepth)
4902          {
4903              if (bUnderwater)
4904              {
4905                  int cz = getceilzofslope(nSector, pSprite->x, pSprite->y);
4906                  if (cz > top)
4907                      vc += ((bottom-cz)*-80099) / (bottom-top);
4908                  else
4909                      vc = 0;
4910              }
4911              else
4912              {
4913                  int fz = getflorzofslope(nSector, pSprite->x, pSprite->y);
4914                  if (fz < bottom)
4915                      vc += ((bottom-fz)*-80099) / (bottom-top);
4916              }
4917          }
4918          else
4919          {
4920              if (bUnderwater)
4921                  vc = 0;
4922              else if (bottom >= floorZ)
4923                  vc =  0;
4924          }
4925          if (vc)
4926          {
4927              pSprite->z += ((vc*4)/2)>>8;
4928              zvel[nSprite] += vc;
4929          }
4930      }
4931      if (pPlayer && zvel[nSprite] > 0x155555 && !pPlayer->fallScream && pXSprite->height > 0)
4932      {
4933          const bool playerAlive = (pXSprite->health > 0) || VanillaMode(); // only trigger falling scream if player is alive
4934          if (playerAlive)
4935          {
4936              pPlayer->fallScream = 1;
4937              sfxPlay3DSound(pSprite, 719, 0, 0);
4938          }
4939      }
4940      vec3_t const oldpos = pSprite->xyz;
4941      int nLink = CheckLink(pSprite);
4942      if (nLink)
4943      {
4944          GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR);
4945          if (pPlayer)
4946          {
4947              if (VanillaMode())
4948                  playerResetInertia(pPlayer);
4949              else
4950              {
4951                  playerCorrectInertia(pPlayer, &oldpos);
4952                  if (pPlayer == gMe) // if player is listener, update ear position so audio pitch of surrounding sfx does not freak out when transitioning between ror sectors
4953                      sfxCorrectListenerPos();
4954              }
4955          }
4956          switch (nLink) {
4957          case kMarkerLowStack:
4958              if (pPlayer == gView)
4959                  SetBitString(gotpic, sector[pSprite->sectnum].floorpicnum);
4960              break;
4961          case kMarkerUpStack:
4962              if (pPlayer == gView)
4963                  SetBitString(gotpic, sector[pSprite->sectnum].ceilingpicnum);
4964              break;
4965          case kMarkerLowWater:
4966          case kMarkerLowGoo:
4967              pXSprite->medium = kMediumNormal;
4968              if (pPlayer) {
4969                  pPlayer->posture = kPostureStand;
4970                  pPlayer->bubbleTime = 0;
4971                  if (!pPlayer->cantJump && pPlayer->input.buttonFlags.jump) {
4972                      zvel[nSprite] = -0x6aaaa;
4973                      pPlayer->cantJump = 1;
4974                  }
4975                  sfxPlay3DSound(pSprite, 721, -1, 0);
4976              } else {
4977  
4978                  switch (pSprite->type) {
4979                      case kDudeCultistTommy:
4980                      case kDudeCultistShotgun:
4981                          aiNewState(pSprite, pXSprite, &cultistGoto);
4982                          break;
4983                      case kDudeGillBeast:
4984                          aiNewState(pSprite, pXSprite, &gillBeastGoto);
4985                          pSprite->flags |= 6;
4986                          break;
4987                      case kDudeBoneEel:
4988                          actKillDude(pSprite->index, pSprite, kDamageFall, 1000<<4);
4989                          break;
4990                      case kDudeBeast:
4991                          if (!EnemiesNotBlood() || VanillaMode())
4992                              break;
4993                          aiNewState(pSprite, pXSprite, &beastGoto);
4994                          pSprite->flags |= 6;
4995                          break;
4996                  }
4997  
4998                  #ifdef NOONE_EXTENSIONS
4999                  if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType))
5000                      aiPatrolState(pSprite, kAiStatePatrolMoveL); // continue patrol when going from water
5001                  #endif
5002              }
5003              break;
5004          case kMarkerUpWater:
5005          case kMarkerUpGoo:
5006          {
5007              int chance = 0xa00; int medium = kMediumWater;
5008              if (nLink == kMarkerUpGoo){
5009                  medium = kMediumGoo;
5010                  chance = 0x400;
5011              }
5012  
5013              pXSprite->medium = medium;
5014  
5015              if (pPlayer)
5016              {
5017                  #ifdef NOONE_EXTENSIONS
5018                  // look for palette in data2 of marker. If value <= 0, use default ones.
5019                  if (gModernMap) {
5020                  pPlayer->nWaterPal = 0;
5021                  int nXUpper = sprite[gUpperLink[nSector]].extra;
5022                  if (nXUpper >= 0)
5023                      pPlayer->nWaterPal = xsprite[nXUpper].data2;
5024                  }
5025                  #endif
5026  
5027                  pPlayer->posture = kPostureSwim;
5028                  pXSprite->burnTime = 0;
5029                  pPlayer->bubbleTime = klabs(zvel[nSprite]) >> 12;
5030                  evPost(nSprite, 3, 0, kCallbackPlayerBubble);
5031                  sfxPlay3DSound(pSprite, 720, -1, 0);
5032              }
5033              else
5034              {
5035  
5036                  switch (pSprite->type) {
5037                  case kDudeCultistTommy:
5038                  case kDudeCultistShotgun:
5039                      pXSprite->burnTime = 0;
5040                      evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
5041                      sfxPlay3DSound(pSprite, 720, -1, 0);
5042                      aiNewState(pSprite, pXSprite, &cultistSwimGoto);
5043                      break;
5044                  case kDudeBurningCultist:
5045                  {
5046                      const bool fixRandomCultist = EnemiesNBlood() && (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && (pSprite->inittype != pSprite->type) && !VanillaMode(); // fix burning cultists randomly switching types underwater
5047                      if (fixRandomCultist)
5048                          pSprite->type = pSprite->inittype;
5049                      else if (Chance(chance)) // vanilla behavior
5050                          pSprite->type = kDudeCultistTommy;
5051                      else
5052                          pSprite->type = kDudeCultistShotgun;
5053                      pXSprite->burnTime = 0;
5054                      evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
5055                      sfxPlay3DSound(pSprite, 720, -1, 0);
5056                      aiNewState(pSprite, pXSprite, &cultistSwimGoto);
5057                      break;
5058                  }
5059                  case kDudeZombieAxeNormal:
5060                      pXSprite->burnTime = 0;
5061                      evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
5062                      sfxPlay3DSound(pSprite, 720, -1, 0);
5063                      aiNewState(pSprite, pXSprite, &zombieAGoto);
5064                      break;
5065                  case kDudeZombieButcher:
5066                      pXSprite->burnTime = 0;
5067                      evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
5068                      sfxPlay3DSound(pSprite, 720, -1, 0);
5069                      aiNewState(pSprite, pXSprite, &zombieFGoto);
5070                      break;
5071                  case kDudeGillBeast:
5072                      pXSprite->burnTime = 0;
5073                      evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
5074                      sfxPlay3DSound(pSprite, 720, -1, 0);
5075                      aiNewState(pSprite, pXSprite, &gillBeastSwimGoto);
5076                      pSprite->flags &= ~6;
5077                      break;
5078                  case kDudeGargoyleFlesh:
5079                  case kDudeHellHound:
5080                  case kDudeSpiderBrown:
5081                  case kDudeSpiderRed:
5082                  case kDudeSpiderBlack:
5083                  case kDudeBat:
5084                  case kDudeRat:
5085                  case kDudeBurningInnocent:
5086                      actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4);
5087                      break;
5088                  case kDudeBeast:
5089                      if (!EnemiesNotBlood() || VanillaMode())
5090                          break;
5091                      pXSprite->burnTime = 0;
5092                      sfxPlay3DSound(pSprite, 720, -1, 0);
5093                      aiNewState(pSprite, pXSprite, &beastSwimGoto);
5094                      pSprite->flags &= ~6;
5095                      break;
5096                  }
5097  
5098                  #ifdef NOONE_EXTENSIONS
5099                  if (gModernMap)
5100                  {
5101                      if (pSprite->type == kDudeModernCustom)
5102                      {
5103                          evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
5104                          if (!cdudeGet(pSprite)->CanSwim())
5105                          {
5106                              actKillDude(pSprite->index, pSprite, kDamageDrown, 1000 << 4);
5107                              return;
5108                          }
5109                      }
5110  
5111                      // continue patrol when fall into water
5112                      if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType))
5113                          aiPatrolState(pSprite, kAiStatePatrolMoveW);
5114                  }
5115                  #endif
5116  
5117              }
5118              break;
5119          }
5120          }
5121      }
5122      GetSpriteExtents(pSprite, &top, &bottom);
5123      if (pPlayer && bottom >= floorZ)
5124      {
5125          int floorZ2 = floorZ;
5126          int floorHit2 = floorHit;
5127          GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR);
5128          if (bottom <= floorZ && pSprite->z - floorZ2 < bz)
5129          {
5130              floorZ = floorZ2;
5131              floorHit = floorHit2;
5132          }
5133      }
5134      if (floorZ <= bottom)
5135      {
5136          gSpriteHit[nXSprite].florhit = floorHit;
5137          pSprite->z += floorZ-bottom;
5138          int v30 = zvel[nSprite]-velFloor[pSprite->sectnum];
5139          if (v30 > 0)
5140          {
5141              int vax = actFloorBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], (int*)&v30, pSprite->sectnum, 0);
5142              int nDamage = mulscale30(vax, vax);
5143              if (pPlayer)
5144              {
5145                  pPlayer->fallScream = 0;
5146  
5147                  if (nDamage > (15<<4) && (pSprite->flags&4))
5148                      playerLandingSound(pPlayer);
5149                  if (nDamage > (30<<4))
5150                      sfxPlay3DSound(pSprite, 701, 0, 0);
5151              }
5152              nDamage -= 100<<4;
5153              if (nDamage > 0)
5154                  actDamageSprite(nSprite, pSprite, kDamageFall, nDamage);
5155              zvel[nSprite] = v30;
5156              if (klabs(zvel[nSprite]) < 0x10000)
5157              {
5158                  zvel[nSprite] = velFloor[pSprite->sectnum];
5159  
5160                  pSprite->flags &= ~4;
5161              }
5162              else
5163  
5164                  pSprite->flags |= 4;
5165              switch (tileGetSurfType(floorHit))
5166              {
5167              case kSurfWater:
5168                  gFX.fxSpawn(FX_9, pSprite->sectnum, pSprite->x, pSprite->y, floorZ);
5169                  break;
5170              case kSurfLava:
5171              {
5172                  spritetype *pFX = gFX.fxSpawn(FX_10, pSprite->sectnum, pSprite->x, pSprite->y, floorZ);
5173                  if (pFX)
5174                  {
5175                      for (int i = 0; i < 7; i++)
5176                      {
5177                          spritetype *pFX2 = gFX.fxSpawn(FX_14, pFX->sectnum, pFX->x, pFX->y, pFX->z);
5178                          if (pFX2)
5179                          {
5180                              xvel[pFX2->index] = Random2(0x6aaaa);
5181                              yvel[pFX2->index] = Random2(0x6aaaa);
5182                              zvel[pFX2->index] = -Random(0xd5555);
5183                          }
5184                      }
5185                  }
5186                  break;
5187              }
5188              }
5189          }
5190          else if (zvel[nSprite] == 0)
5191  
5192              pSprite->flags &= ~4;
5193      }
5194      else
5195      {
5196          gSpriteHit[nXSprite].florhit = 0;
5197  
5198          if (pSprite->flags&2)
5199              pSprite->flags |= 4;
5200      }
5201      if (top <= ceilZ)
5202      {
5203          gSpriteHit[nXSprite].ceilhit = ceilHit;
5204          pSprite->z += ClipLow(ceilZ-top, 0);
5205  
5206          if (zvel[nSprite] <= 0 && (pSprite->flags&4))
5207              zvel[nSprite] = mulscale16(-zvel[nSprite], 0x2000);
5208      }
5209      else
5210          gSpriteHit[nXSprite].ceilhit = 0;
5211      GetSpriteExtents(pSprite,&top,&bottom);
5212  
5213      pXSprite->height = ClipLow(floorZ-bottom, 0)>>8;
5214      if (pPlayer && gFlyMode)
5215      {
5216          if ((xvel[nSprite] || yvel[nSprite]) && (approxDist(xvel[nSprite], yvel[nSprite]) < 0x1000))
5217              xvel[nSprite] = yvel[nSprite] = 0;
5218          return;
5219      }
5220      if (xvel[nSprite] || yvel[nSprite])
5221      {
5222          if ((floorHit & 0xc000) == 0xc000)
5223          {
5224              int nHitSprite = floorHit & 0x3fff;
5225              if ((sprite[nHitSprite].cstat & 0x30) == 0)
5226              {
5227                  xvel[nSprite] += mulscale2(4, pSprite->x - sprite[nHitSprite].x);
5228                  yvel[nSprite] += mulscale2(4, pSprite->y - sprite[nHitSprite].y);
5229                  return;
5230              }
5231          }
5232          int nXSector = sector[pSprite->sectnum].extra;
5233          if (nXSector > 0 && xsector[nXSector].Underwater)
5234              return;
5235          if (pXSprite->height >= 0x100)
5236              return;
5237          int nDrag = gDudeDrag;
5238          if (pXSprite->height > 0)
5239              nDrag -= scale(gDudeDrag, pXSprite->height, 0x100);
5240          xvel[nSprite] -= mulscale16r(xvel[nSprite], nDrag);
5241          yvel[nSprite] -= mulscale16r(yvel[nSprite], nDrag);
5242  
5243          if (approxDist(xvel[nSprite], yvel[nSprite]) < 0x1000)
5244              xvel[nSprite] = yvel[nSprite] = 0;
5245      }
5246  }
5247  
5248  int MoveMissile(spritetype *pSprite)
5249  {
5250      int nXSprite = pSprite->extra;
5251      XSPRITE *pXSprite = &xsprite[nXSprite];
5252      int vdi = -1;
5253      spritetype *pOwner = NULL;
5254      int bakCstat = 0;
5255      if (pSprite->owner >= 0)
5256      {
5257          int nOwner = actSpriteOwnerToSpriteId(pSprite);
5258          pOwner = &sprite[nOwner];
5259          if (IsDudeSprite(pOwner))
5260          {
5261              bakCstat = pOwner->cstat;
5262              pOwner->cstat &= ~257;
5263          }
5264          else
5265              pOwner = NULL;
5266      }
5267      gHitInfo.hitsect = -1;
5268      gHitInfo.hitwall = -1;
5269      gHitInfo.hitsprite = -1;
5270      int nSprite = pSprite->index;
5271      if (pSprite->type == kMissileFlameSpray)
5272      {
5273          actAirDrag(pSprite, 0x1000);
5274      }
5275      else if ((pSprite->type == kMissileFireballNapalm) && gGameOptions.bNapalmFalloff && IsPlayerSprite(pOwner) && !VanillaMode()) // apply gravity for player napalm projectile
5276      {
5277          pSprite->z += 455;
5278          zvel[nSprite] += 58254;
5279      }
5280      if (pXSprite->target != -1 && (xvel[nSprite] || yvel[nSprite] || zvel[nSprite]))
5281      {
5282          spritetype *pTarget = &sprite[pXSprite->target];
5283          XSPRITE *pXTarget;
5284          if (pTarget->extra > 0)
5285              pXTarget = &xsprite[pTarget->extra];
5286          else
5287              pXTarget = NULL;
5288          if (pTarget->statnum == kStatDude && pXTarget && pXTarget->health > 0)
5289          {
5290              int nTargetAngle = getangle(-(pTarget->y-pSprite->y), pTarget->x-pSprite->x);
5291              int UNUSED(nAngle) = getangle(xvel[nSprite]>>12,yvel[nSprite]>>12);
5292              int vx = missileInfo[pSprite->type - kMissileBase].velocity;
5293              int vy = 0;
5294              RotatePoint(&vx, &vy, (nTargetAngle+1536)&2047, 0, 0);
5295              xvel[nSprite] = vx;
5296              yvel[nSprite] = vy;
5297              int dx = pTarget->x-pSprite->x;
5298              int dy = pTarget->y-pSprite->y;
5299              int dz = pTarget->z-pSprite->z;
5300              // Inlined
5301              int vax = dz/10;
5302              if (pTarget->z < pSprite->z)
5303                  vax = -vax;
5304              zvel[nSprite] += vax;
5305              ksqrt(dx*dx+dy*dy+dz*dz);
5306          }
5307      }
5308      int vx = xvel[nSprite]>>12;
5309      int vy = yvel[nSprite]>>12;
5310      int vz = zvel[nSprite]>>8;
5311      int top, bottom;
5312      GetSpriteExtents(pSprite, &top, &bottom);
5313      int i = 1;
5314      const char bIsFlameSprite = (pSprite->type == kMissileFlameSpray) || (pSprite->type == kMissileFlameHound); // do not use eduke clipmove for flame based sprites (changes damage too much)
5315      const char bButcherKnife = (pSprite->type == kMissileButcherKnife) && spriRangeIsFine(pSprite->owner) && (sprite[pSprite->owner].type == kDudeZombieButcher);
5316      while (1)
5317      {
5318          int x = pSprite->x;
5319          int y = pSprite->y;
5320          int z = pSprite->z;
5321          int wd = pSprite->clipdist<<2;
5322          int nSector2 = pSprite->sectnum;
5323          clipmoveboxtracenum = 1;
5324          int vdx;
5325          if (ProjectilesRaze() && pOwner && !bIsFlameSprite && !VanillaMode()) // improved clipmove accuracy (raze)
5326          {
5327              const short bakSpriteCstat = pSprite->cstat;
5328              pSprite->cstat &= ~257; // remove self collisions for accurate clipmove
5329              vdx = ClipMoveEDuke(&x, &y, &z, &nSector2, vx, vy, wd, (z-top)/4, (bottom-z)/4, CLIPMASK0);
5330              pSprite->cstat = bakSpriteCstat;
5331          }
5332          else if (ProjectilesNotBlood() && pOwner && !bIsFlameSprite && !VanillaMode()) // improved clipmove accuracy and adjust hitboxes (notblood)
5333          {
5334              const short bakSpriteCstat = pSprite->cstat;
5335              pSprite->cstat &= ~257; // remove self collisions for accurate clipmove
5336              spritetype *raySprite = NULL;
5337              const int tinywd = NotBloodAdjustHitbox(pSprite, top, bottom, wd);
5338              if (tinywd) // if object owned by player, use smaller hitboxes for specific player owned items
5339              {
5340                  wd = tinywd;
5341                  raySprite = pSprite; // set raycast collisions to be used
5342              }
5343              vdx = ClipMoveEDuke(&x, &y, &z, &nSector2, vx, vy, wd, (z-top)/4, (bottom-z)/4, CLIPMASK0, raySprite);
5344              pSprite->cstat = bakSpriteCstat;
5345          }
5346          else // original
5347          {
5348              vdx = ClipMove(&x, &y, &z, &nSector2, vx, vy, wd, (z-top)/4, (bottom-z)/4, CLIPMASK0);
5349          }
5350          clipmoveboxtracenum = 3;
5351          short nSector = nSector2;
5352          if (nSector2 < 0)
5353          {
5354              vdi = -1;
5355              break;
5356          }
5357          if (vdx)
5358          {
5359              int nHitSprite = vdx & 0x3fff;
5360              if ((vdx&0xc000) == 0xc000)
5361              {
5362                  gHitInfo.hitsprite = nHitSprite;
5363                  vdi = 3;
5364              }
5365              else if ((vdx & 0xc000) == 0x8000)
5366              {
5367                  gHitInfo.hitwall = nHitSprite;
5368                  if (wall[nHitSprite].nextsector == -1)
5369                      vdi = 0;
5370                  else
5371                  {
5372                      int32_t fz, cz;
5373                      getzsofslope(wall[nHitSprite].nextsector, x, y, &cz, &fz);
5374                      if (z <= cz || z >= fz)
5375                          vdi = 0;
5376                      else
5377                          vdi = 4;
5378                  }
5379              }
5380          }
5381          if (vdi == 4)
5382          {
5383              walltype *pWall = &wall[gHitInfo.hitwall];
5384              if (pWall->extra > 0)
5385              {
5386                  XWALL *pXWall = &xwall[pWall->extra];
5387                  if (pXWall->triggerVector)
5388                  {
5389                      trTriggerWall(gHitInfo.hitwall, pXWall, kCmdWallImpact, pOwner ? pOwner->index : pSprite->index);
5390                      if (!(pWall->cstat&64))
5391                      {
5392                          vdi = -1;
5393                          if (i-- > 0)
5394                              continue;
5395                          vdi = 0;
5396                          break;
5397                      }
5398                  }
5399              }
5400          }
5401          if (vdi >= 0 && vdi != 3)
5402          {
5403              int nAngle = getangle(xvel[nSprite], yvel[nSprite]);
5404              x -= mulscale30(Cos(nAngle), 16);
5405              y -= mulscale30(Sin(nAngle), 16);
5406              int nVel = approxDist(xvel[nSprite], yvel[nSprite]);
5407              vz -= scale(0x100, zvel[nSprite], nVel);
5408              updatesector(x, y, &nSector);
5409              nSector2 = nSector;
5410          }
5411          int ceilZ, ceilHit, floorZ, floorHit;
5412          GetZRangeAtXYZ(x, y, z, nSector2, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0);
5413          GetSpriteExtents(pSprite, &top, &bottom);
5414          top += vz;
5415          bottom += vz;
5416          if (bottom >= floorZ)
5417          {
5418              if (bButcherKnife && ((floorHit&0xC000) == 0xC000) && IsPlayerSprite(&sprite[floorHit&0x3fff]) && EnemiesNotBlood() && !VanillaMode()) // tweak butcher knife so it will hit players
5419                  gHitInfo.hitsprite = floorHit&0x3fff, vdi = 3;
5420              else
5421                  gSpriteHit[nXSprite].florhit = floorHit, vdi = 2;
5422              vz += floorZ-bottom;
5423          }
5424          if (top <= ceilZ)
5425          {
5426              if (bButcherKnife && ((ceilHit&0xC000) == 0xC000) && IsPlayerSprite(&sprite[ceilHit&0x3fff]) && EnemiesNotBlood() && !VanillaMode()) // tweak butcher knife so it will hit players
5427                  gHitInfo.hitsprite = ceilHit&0x3fff, vdi = 3;
5428              else
5429                  gSpriteHit[nXSprite].ceilhit = ceilHit, vdi = 1;
5430              vz += ClipLow(ceilZ-top, 0);
5431          }
5432          pSprite->x = x;
5433          pSprite->y = y;
5434          pSprite->z = z+vz;
5435          updatesector(x, y, &nSector);
5436          if (nSector >= 0 && nSector != pSprite->sectnum)
5437          {
5438              dassert(nSector >= 0 && nSector < kMaxSectors);
5439              ChangeSpriteSect(nSprite, nSector);
5440          }
5441          CheckLink(pSprite);
5442          gHitInfo.hitsect = pSprite->sectnum;
5443          gHitInfo.hitx = pSprite->x;
5444          gHitInfo.hity = pSprite->y;
5445          gHitInfo.hitz = pSprite->z;
5446          break;
5447      }
5448      if (pOwner)
5449          pOwner->cstat = bakCstat;
5450      return vdi;
5451  }
5452  
5453  static bool MoveMissileBulletVectorTest(spritetype *pSource, spritetype *pShooter, int a2, int a3, int a4, int a5, int a6, VECTOR_TYPE vectorType, int nRange)
5454  {
5455      bool didHit = false;
5456      int nShooter = -1;
5457      if (pShooter)
5458          nShooter = pShooter->index;
5459      dassert(vectorType >= 0 && vectorType < kVectorMax);
5460      VECTORDATA *pVectorData = &gVectorData[vectorType];
5461      vec3_t sourcePos;
5462      int hit = VectorScanROR(pSource, a2, a3, a4, a5, a6, nRange, 1, &sourcePos);
5463      if (hit == 3)
5464      {
5465          int nSprite = gHitInfo.hitsprite;
5466          dassert(nSprite >= 0 && nSprite < kMaxSprites);
5467          spritetype *pSprite = &sprite[nSprite];
5468          if (pShooter && !gGameOptions.bFriendlyFire && IsTargetTeammate(pShooter, pSprite)) return true;
5469          if (IsPlayerSprite(pSprite)) {
5470              PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
5471              if (powerupCheck(pPlayer, kPwUpReflectShots))
5472              {
5473                  xvel[pSource->index] = -xvel[pSource->index]; // return to sender
5474                  yvel[pSource->index] = -yvel[pSource->index];
5475                  zvel[pSource->index] = -zvel[pSource->index];
5476                  pSource->owner = pSprite->index; // set projectile owner as player with reflective shot
5477                  return false;
5478              }
5479          }
5480      }
5481      int x = gHitInfo.hitx-mulscale14(a4, 16);
5482      int y = gHitInfo.hity-mulscale14(a5, 16);
5483      int z = gHitInfo.hitz-mulscale14(a6, 256);
5484      short nSector = gHitInfo.hitsect;
5485      char nSurf = kSurfNone;
5486      if (nRange == 0 || approxDist(gHitInfo.hitx-sourcePos.x, gHitInfo.hity-sourcePos.y) < nRange)
5487      {
5488          switch (hit)
5489          {
5490          case 1:
5491          {
5492              didHit = true;
5493              int nSector = gHitInfo.hitsect;
5494              if (sector[nSector].ceilingstat&1)
5495                  nSurf = kSurfNone;
5496              else
5497                  nSurf = surfType[sector[nSector].ceilingpicnum];
5498              break;
5499          }
5500          case 2:
5501          {
5502              didHit = true;
5503              int nSector = gHitInfo.hitsect;
5504              if (sector[nSector].floorstat&1)
5505                  nSurf = kSurfNone;
5506              else
5507                  nSurf = surfType[sector[nSector].floorpicnum];
5508              break;
5509          }
5510          case 0:
5511          {
5512              didHit = true;
5513              int nWall = gHitInfo.hitwall;
5514              dassert(nWall >= 0 && nWall < kMaxWalls);
5515              nSurf = surfType[wall[nWall].picnum];
5516              if (actCanSplatWall(nWall, nSector, gHitInfo.hitx, gHitInfo.hity, gHitInfo.hitz, &nSurf))
5517              {
5518                  int x = gHitInfo.hitx-mulscale14(a4, 16);
5519                  int y = gHitInfo.hity-mulscale14(a5, 16);
5520                  int z = gHitInfo.hitz-mulscale14(a6, 256);
5521                  int nSurf = surfType[wall[nWall].picnum];
5522                  dassert(nSurf < kSurfMax);
5523                  if (pVectorData->surfHit[nSurf].fx1 >= 0)
5524                  {
5525                      spritetype *pFX = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx1, nSector, x, y, z);
5526                      if (pFX)
5527                      {
5528                          pFX->ang = (GetWallAngle(nWall)+512)&2047;
5529                          pFX->cstat |= 16;
5530                      }
5531                  }
5532              }
5533              break;
5534          }
5535          case 4:
5536          {
5537              didHit = true;
5538              int nWall = gHitInfo.hitwall;
5539              dassert(nWall >= 0 && nWall < kMaxWalls);
5540              nSurf = surfType[wall[nWall].overpicnum];
5541              int nXWall = wall[nWall].extra;
5542              if (nXWall > 0)
5543              {
5544                  XWALL *pXWall = &xwall[nXWall];
5545                  if (pXWall->triggerVector)
5546                      trTriggerWall(nWall, pXWall, kCmdWallImpact, (nShooter >= 0) ? nShooter : kCauserGame);
5547              }
5548              break;
5549          }
5550          case 3:
5551          {
5552              didHit = true;
5553              int nSprite = gHitInfo.hitsprite;
5554              nSurf = surfType[sprite[nSprite].picnum];
5555              dassert(nSprite >= 0 && nSprite < kMaxSprites);
5556              spritetype *pSprite = &sprite[nSprite];
5557              x -= mulscale14(a4, 112);
5558              y -= mulscale14(a5, 112);
5559              z -= mulscale14(a6, 112<<4);
5560              int shift = 4;
5561              int boost = 1;
5562              int boostz = 1;
5563              DAMAGE_TYPE dmgType = pVectorData->dmgType;
5564              if (vectorType == kVectorTine && !IsPlayerSprite(pSprite))
5565                  shift = 3;
5566              if (pShooter && IsPlayerSprite(pShooter) && gGameOptions.bQuadDamagePowerup)
5567              {
5568                  PLAYER *pPlayer = &gPlayer[pShooter->type - kDudePlayer1];
5569                  if (powerupCheck(pPlayer, kPwUpTwoGuns)) // if quad is active, increase pushback and do random explosive damage for hitscan weapons
5570                  {
5571                      if (a6 < 5000) // only increase velocity impulse if vector is aiming upwards
5572                      {
5573                          shift = 2;
5574                          boost = 2;
5575                          boostz = 4;
5576                      }
5577                      if ((dmgType == kDamageBullet) && !Random(10))
5578                      {
5579                          dmgType = kDamageExplode;
5580                          if (a6 < 5000)
5581                              shift = 4;
5582                      }
5583                  }
5584              }
5585              actDamageSprite(nShooter, pSprite, dmgType, pVectorData->dmg<<shift);
5586              int nXSprite = pSprite->extra;
5587              if (nXSprite > 0)
5588              {
5589                  XSPRITE *pXSprite = &xsprite[nXSprite];
5590                  if (pXSprite->Vector)
5591                      trTriggerSprite(nSprite, pXSprite, kCmdSpriteImpact, (nShooter >= 0) ? nShooter : kCauserGame);
5592              }
5593              if (pSprite->statnum == kStatThing)
5594              {
5595                  // NoOne:
5596                  // shoting in TNT makes it explode, so type changes to range of 0-8
5597                  // however statnum changes to 2 (explosion) later in actPostSprite()...
5598                  // this is why this type range check is required here
5599                  if (VanillaMode() || (pSprite->type >= kThingBase && pSprite->type < kThingMax))
5600                  {
5601                      int t = thingInfo[pSprite->type - kThingBase].mass;
5602                      if (t > 0 && pVectorData->impulse)
5603                      {
5604                          int t2 = divscale8(pVectorData->impulse, t);
5605                          xvel[nSprite] += mulscale16(a4, t2) * boost;
5606                          yvel[nSprite] += mulscale16(a5, t2) * boost;
5607                          zvel[nSprite] += mulscale16(a6, t2) * boostz;
5608                      }
5609                      if (pVectorData->burnTime)
5610                      {
5611                          XSPRITE* pXSprite = &xsprite[nXSprite];
5612                          if (!pXSprite->burnTime)
5613                              evPost(nSprite, 3, 0, kCallbackFXFlameLick);
5614                          actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->burnTime);
5615                      }
5616                  }
5617              }
5618              if (pSprite->statnum == kStatDude)
5619              {
5620                  #ifdef NOONE_EXTENSIONS
5621                  int t = (IsCustomDude(pSprite)) ? cdudeGet(pSprite->index)->mass : getDudeInfo(pSprite->type)->mass;
5622                  #else
5623                  int t = getDudeInfo(pSprite->type)->mass;
5624                  #endif
5625  
5626                  if (t > 0 && pVectorData->impulse)
5627                  {
5628                      int t2 = divscale8(pVectorData->impulse, t);
5629                      int t3 = mulscale16(a6, t2);
5630                      if (EnemiesNotBlood() && !VanillaMode()) // clamp downward impulse damage to stop players from cheesing bosses
5631                          t3 = ClipHigh(t3, 32767);
5632                      xvel[nSprite] += mulscale16(a4, t2) * boost;
5633                      yvel[nSprite] += mulscale16(a5, t2) * boost;
5634                      zvel[nSprite] += t3 * boostz;
5635                  }
5636                  if (pVectorData->burnTime)
5637                  {
5638                      XSPRITE *pXSprite = &xsprite[nXSprite];
5639                      if (!pXSprite->burnTime)
5640                          evPost(nSprite, 3, 0, kCallbackFXFlameLick);
5641                      actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->burnTime);
5642                  }
5643                  if (Chance(pVectorData->fxChance))
5644                  {
5645                      int t = gVectorData[19].maxDist;
5646                      a4 += Random3(4000);
5647                      a5 += Random3(4000);
5648                      a6 += Random3(4000);
5649                      if (HitScan(pSprite, gHitInfo.hitz, a4, a5, a6, CLIPMASK1, t) == 0)
5650                      {
5651                          if (approxDist(gHitInfo.hitx-pSprite->x, gHitInfo.hity-pSprite->y) <= t)
5652                          {
5653                              int nWall = gHitInfo.hitwall;
5654                              int nSector = gHitInfo.hitsect;
5655                              if (actCanSplatWall(nWall, nSector, gHitInfo.hitx, gHitInfo.hity, gHitInfo.hitz, &nSurf))
5656                              {
5657                                  int x = gHitInfo.hitx - mulscale14(a4, 16);
5658                                  int y = gHitInfo.hity - mulscale14(a5, 16);
5659                                  int z = gHitInfo.hitz - mulscale14(a6, 16<<4);
5660                                  int nSurf = surfType[wall[nWall].picnum];
5661                                  VECTORDATA *pVectorData = &gVectorData[19];
5662                                  FX_ID t2 = pVectorData->surfHit[nSurf].fx2;
5663                                  FX_ID t3 = pVectorData->surfHit[nSurf].fx3;
5664                                  spritetype *pFX = NULL;
5665                                  const char bSpawnBlood = (gGameOptions.nGoreBehavior > 1) || (t3 == FX_NONE || Chance(0x4000));
5666                                  if (t2 > FX_NONE && bSpawnBlood)
5667                                      pFX = gFX.fxSpawn(t2, nSector, x, y, z);
5668                                  else if(t3 > FX_NONE)
5669                                      pFX = gFX.fxSpawn(t3, nSector, x, y, z);
5670                                  if (pFX)
5671                                  {
5672                                      zvel[pFX->index] = 0x2222;
5673                                      pFX->ang = (GetWallAngle(nWall)+512)&2047;
5674                                      pFX->cstat |= 16;
5675                                  }
5676                              }
5677                          }
5678                      }
5679                  }
5680                  if (gGameOptions.nGoreBehavior > 1) // splatIncrement for default
5681                  {
5682                      switch (pSprite->type)
5683                      {
5684                      case kDudePhantasm:
5685                      case kDudeHand:
5686                      case kDudeSpiderBrown:
5687                      case kDudeSpiderRed:
5688                      case kDudeBoneEel:
5689                      case kDudeBat:
5690                      case kDudeRat:
5691                      case kDudeTentacleGreen:
5692                      case kDudeTentacleFire:
5693                      case kDudeBurningTinyCaleb:
5694                          for (int i = 0; i < pVectorData->bloodSplats; i++)
5695                              if (Chance(pVectorData->splatChance))
5696                                  fxSpawnBlood(pSprite, pVectorData->dmg<<4);
5697                          break;
5698                      default:
5699                      {
5700                          const int kMaxSplatIncrementBase = 5; // base for random splat increment
5701                          const int kShotgunSplatChance = 0x4000; // 25% chance for shotgun-like effects
5702                          const int kTommySplatChance = 0x4000; // 25% chance for tommy-like effects
5703  
5704                          int nSplatIncrement = kMaxSplatIncrementBase - gGameOptions.nDifficulty;
5705                          nSplatIncrement >>= 1;
5706                          const int nBloodSplats = pVectorData->bloodSplats + nSplatIncrement;
5707                          if (pShooter)
5708                              break;
5709                          int nType = pShooter->type;
5710                          if (IsPlayerSprite(pShooter))
5711                          {
5712                              PLAYER* pPlayer = &gPlayer[nType - kDudePlayer1];
5713                              if (pPlayer->curWeapon == kWeaponShotgun)
5714                                  nType = kDudeCultistShotgun;
5715                              else if (pPlayer->curWeapon == kWeaponTommy)
5716                                  nType = kDudeCultistTommy;
5717                          }
5718  
5719                          for (int j = 0; j < nBloodSplats; j++)
5720                          {
5721                              switch (nType)
5722                              {
5723                              case kDudeCultistShotgun:
5724                              case kDudeCultistShotgunProne:
5725                                  if (Chance(kShotgunSplatChance)) // apply a chance to limit blood splats when using shotgun
5726                                      fxSpawnBlood(pSprite, pVectorData->dmg<<4);
5727                                  break;
5728                              case kDudeCultistTommy:
5729                              case kDudeCultistTommyProne:
5730                                  if (Chance(kTommySplatChance)) // apply a chance to limit blood splats when using tommy
5731                                      fxSpawnBlood(pSprite, pVectorData->dmg<<4);
5732                                  break;
5733                              default:
5734                                  fxSpawnBlood(pSprite, pVectorData->dmg<<4);
5735                                  break;
5736                              }
5737                          }
5738                          break;
5739                      }
5740                      }
5741                  }
5742                  else
5743                  {
5744                      for (int i = 0; i < pVectorData->bloodSplats; i++)
5745                          if (Chance(pVectorData->splatChance))
5746                              fxSpawnBlood(pSprite, pVectorData->dmg<<4);
5747                  }
5748              }
5749              #ifdef NOONE_EXTENSIONS
5750              // add impulse for sprites from physics list
5751              if (gPhysSpritesList.Length() && pVectorData->impulse && xspriRangeIsFine(pSprite->extra))
5752              {
5753                  XSPRITE* pXSprite = &xsprite[pSprite->extra];
5754                  if (pXSprite->physAttr & kPhysDebrisVector)
5755                  {
5756                      int impulse = divscale6(pVectorData->impulse, ClipLow(gSpriteMass[pSprite->extra].mass, 10));
5757                      xvel[nSprite] += mulscale16(a4, impulse) * boost;
5758                      yvel[nSprite] += mulscale16(a5, impulse) * boost;
5759                      zvel[nSprite] += mulscale16(a6, impulse) * boostz;
5760  
5761                      if (pVectorData->burnTime != 0)
5762                      {
5763                          if (!pXSprite->burnTime)
5764                              evPost(nSprite, 3, 0, kCallbackFXFlameLick);
5765  
5766                          actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->burnTime);
5767                      }
5768  
5769                      if (IsThingSprite(pSprite))
5770                      {
5771                          pSprite->statnum = kStatThing; // temporary change statnum property
5772                          actDamageSprite(nShooter, pSprite, pVectorData->dmgType, pVectorData->dmg << 4);
5773                          pSprite->statnum = kStatDecoration; // return statnum property back
5774                      }
5775                  }
5776              }
5777              #endif
5778              break;
5779          }
5780          }
5781      }
5782      else
5783          didHit = false;
5784      dassert(nSurf < kSurfMax);
5785  #ifdef NOONE_EXTENSIONS
5786      
5787      // let the patrol enemies hear surface hit sounds!
5788      
5789      if (pVectorData->surfHit[nSurf].fx2 >= 0) {
5790          
5791          spritetype* pFX2 = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx2, nSector, x, y, z);
5792          if (pFX2 && gModernMap && pShooter)
5793              actPropagateSpriteOwner(pFX2, pShooter);
5794      }
5795      
5796      if (pVectorData->surfHit[nSurf].fx3 >= 0) {
5797          
5798          spritetype* pFX3 = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx3, nSector, x, y, z);
5799          if (pFX3 && gModernMap && pShooter)
5800              actPropagateSpriteOwner(pFX3, pShooter);
5801  
5802      }
5803  
5804  #else
5805      if (pVectorData->surfHit[nSurf].fx2 >= 0)
5806          gFX.fxSpawn(pVectorData->surfHit[nSurf].fx2, nSector, x, y, z);
5807      if (pVectorData->surfHit[nSurf].fx3 >= 0)
5808          gFX.fxSpawn(pVectorData->surfHit[nSurf].fx3, nSector, x, y, z);
5809  #endif
5810  
5811      if (pVectorData->surfHit[nSurf].fxSnd >= 0)
5812          sfxPlay3DSound(x, y, z, pVectorData->surfHit[nSurf].fxSnd, nSector);
5813      return didHit;
5814  }
5815  
5816  void MoveMissileBullet(spritetype *pSprite)
5817  {
5818      dassert(pSprite != NULL);
5819      const int nXMissile = pSprite->extra;
5820      dassert(nXMissile > 0 && nXMissile < kMaxXSprites);
5821      spritetype *pOwner = NULL;
5822      int bakCstat = 0;
5823      if (pSprite->owner >= 0)
5824      {
5825          int nOwner = actSpriteOwnerToSpriteId(pSprite);
5826          pOwner = &sprite[nOwner];
5827          if (sprite[nOwner].type != sprite[nOwner].inittype)
5828              sprite[nOwner].type = sprite[nOwner].type;
5829          bool bDudeSprite = IsDudeSprite(pOwner);
5830          if (!bDudeSprite) // if false, check inittype range (dudes can die before projectiles have hit their target - which mean their type may be set to gibs instead)
5831              bDudeSprite = (pOwner->inittype >= kDudeBase) && (pOwner->inittype < kDudeMax);
5832          if (bDudeSprite)
5833          {
5834              bakCstat = pOwner->cstat;
5835              pOwner->cstat &= ~257;
5836          }
5837          else
5838              pOwner = NULL;
5839      }
5840      const bool underwaterSector = (sector[pSprite->sectnum].extra >= 0 && xsector[sector[pSprite->sectnum].extra].Underwater);
5841      const int nSprite = pSprite->index;
5842      const int bakX = pSprite->x;
5843      const int bakY = pSprite->y;
5844      const int bakZ = pSprite->z;
5845      const int dx = Cos(pSprite->ang)>>16;
5846      const int dy = Sin(pSprite->ang)>>16;
5847      int dz = zvel[nSprite]>>7;
5848      const VECTOR_TYPE nType = pSprite->type == kMissileShell ? kVectorShell : kVectorBullet;
5849      int speed = missileInfo[pSprite->type - kMissileBase].velocity;
5850      if (gGameOptions.nHitscanProjectiles == 1) // if hitscan projectile speed is 75%, adjust hitscan range
5851          speed = (speed>>1) + (speed>>2);
5852      else if (gGameOptions.nHitscanProjectiles == 3) // if hitscan projectile speed is 125%, adjust hitscan range
5853          speed += (speed>>2);
5854      if (underwaterSector) // if bullet is underwater, adjust hitscan range by 50%
5855          speed >>= 1;
5856      bool weHitSomething = MoveMissileBulletVectorTest(pSprite, pOwner, 0, 0, dx, dy, dz, nType, (speed>>12) + (speed>>13));
5857      while (!weHitSomething) // move missile and test for ceiling/floor
5858      {
5859          int vx = xvel[nSprite]>>12;
5860          int vy = yvel[nSprite]>>12;
5861          int vz = zvel[nSprite]>>8;
5862          int nSector = pSprite->sectnum;
5863          if (underwaterSector) // if bullet is underwater, slow down by 50%
5864          {
5865              vx >>= 1;
5866              vy >>= 1;
5867              vz >>= 1;
5868          }
5869          pSprite->x += vx;
5870          pSprite->y += vy;
5871          if (!FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector)) // if we couldn't find where we're supposed to be
5872          {
5873              if (!CheckLink(pSprite)) // check if we clipped into a ror sector, if not just delete the sprite just to be safe
5874              {
5875                  weHitSomething = true;
5876                  break;
5877              }
5878          }
5879          else if (!cansee(bakX, bakY, bakZ, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, nSector)) // if the new updated sector is broken (this will happen when there are shared sectors in the same XYZ location such as DWE2M8's skull key room)
5880              nSector = pSprite->sectnum; // restore sector and hope that everything will work out
5881          else if (pSprite->sectnum != nSector) // if sector was updated, update sprite's sector
5882          {
5883              dassert(nSector >= 0 && nSector < kMaxSectors);
5884              ChangeSpriteSect(nSprite, nSector);
5885          }
5886          int ceilZ, ceilHit, floorZ, floorHit;
5887          GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0);
5888          if (CheckLink(pSprite)) // check for ceiling/floor portals
5889              GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0);
5890          int top, bottom;
5891          GetSpriteExtents(pSprite, &top, &bottom);
5892          top += vz;
5893          bottom += vz;
5894          pSprite->z += vz;
5895          if (bottom >= floorZ) // we're clipping into the floor, so just delete the sprite
5896          {
5897              pSprite->z = floorZ;
5898              actFireVector(pSprite, 0, 0, vx, vy, 0x8000, nType); // shoot fake bullet towards the floor
5899              weHitSomething = true;
5900              break;
5901          }
5902          if (top <= ceilZ) // we're clipping into the ceiling, so just delete the sprite
5903          {
5904              pSprite->z = ceilZ;
5905              actFireVector(pSprite, 0, 0, vx, vy, -0x8000, nType); // shoot fake bullet towards the ceiling
5906              weHitSomething = true;
5907              break;
5908          }
5909          short nSectorShort = nSector;
5910          updatesector(pSprite->x, pSprite->y, &nSectorShort);
5911          if (nSector == -1) // if invalid sector, attempt to get back on the right track
5912          {
5913              nSector = pSprite->sectnum;
5914              if (!FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector)) // if somehow we ended up in a van down by the river, abort and delete the sprite
5915              {
5916                  weHitSomething = true;
5917                  break;
5918              }
5919              nSectorShort = nSector;
5920          }
5921          if (nSectorShort != pSprite->sectnum) // if sector was updated, update sprite's sector
5922          {
5923              dassert(nSectorShort >= 0 && nSectorShort < kMaxSectors);
5924              ChangeSpriteSect(nSprite, nSectorShort);
5925          }
5926          CheckLink(pSprite);
5927          break;
5928      }
5929      if (weHitSomething) // if bullet hit anything, delete sprite
5930      {
5931          seqKill(3, nXMissile);
5932          actPostSprite(nSprite, kStatFree);
5933      }
5934      if (pOwner)
5935          pOwner->cstat = bakCstat;
5936  }
5937  
5938  void actExplodeSprite(spritetype *pSprite)
5939  {
5940      int nXSprite = pSprite->extra;
5941      if (nXSprite <= 0 || nXSprite >= kMaxXSprites)
5942          return;
5943      if (pSprite->statnum == kStatExplosion)
5944          return;
5945      sfxKill3DSound(pSprite, -1, -1);
5946      evKill(pSprite->index, 3);
5947      int nType = kExplosionStandard;
5948  
5949      switch (pSprite->type)
5950      {
5951      case kMissileFireballNapalm:
5952          nType = kExplosionNapalm;
5953          seqSpawn(4, 3, nXSprite, -1);
5954          if (Chance(0x8000))
5955              pSprite->cstat |= 4;
5956          sfxPlay3DSound(pSprite, 303, -1, 0);
5957          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
5958          break;
5959      case kMissileFlareAlt:
5960          nType = kExplosionFireball;
5961          seqSpawn(9, 3, nXSprite, -1);
5962          if (Chance(0x8000))
5963              pSprite->cstat |= 4;
5964          sfxPlay3DSound(pSprite, 306, 24+(pSprite->index&3), 1);
5965          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
5966          break;
5967      case kMissileFireballCerberus:
5968      case kMissileFireballTchernobog:
5969          nType = kExplosionFireball;
5970          seqSpawn(5, 3, nXSprite, -1);
5971          sfxPlay3DSound(pSprite, 304, -1, 0);
5972          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
5973          break;
5974      case kThingArmedTNTStick:
5975          nType = kExplosionSmall;
5976          if (gSpriteHit[nXSprite].florhit == 0) seqSpawn(4,3,nXSprite,-1);
5977          else seqSpawn(3,3,nXSprite,-1);
5978          sfxPlay3DSound(pSprite, 303, -1, 0);
5979          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
5980          break;
5981      case kThingArmedProxBomb:
5982      case kThingArmedRemoteBomb:
5983      case kThingArmedTNTBundle:
5984      #ifdef NOONE_EXTENSIONS
5985      case kModernThingTNTProx:
5986      #endif
5987          nType = kExplosionStandard;
5988          if (gSpriteHit[nXSprite].florhit == 0)
5989              seqSpawn(4,3,nXSprite,-1);
5990          else
5991              seqSpawn(3,3,nXSprite,-1);
5992          sfxPlay3DSound(pSprite, 304, -1, 0);
5993          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
5994          break;
5995      case kThingArmedSpray:
5996          nType = kExplosionSpray;
5997          seqSpawn(5, 3, nXSprite, -1);
5998          sfxPlay3DSound(pSprite, 307, -1, 0);
5999          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
6000          break;
6001      case kThingTNTBarrel:
6002      {
6003          spritetype *pSprite2 = actSpawnSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0, 1);
6004          pSprite2->owner = pSprite->owner;
6005          if (actCheckRespawn(pSprite))
6006          {
6007              XSPRITE *pXSprite = &xsprite[nXSprite];
6008              pXSprite->state = 1;
6009              pXSprite->health = thingInfo[0].startHealth<<4;
6010          }
6011          else
6012              actPostSprite(pSprite->index, kStatFree);
6013          nType = kExplosionLarge;
6014          nXSprite = pSprite2->extra;
6015          seqSpawn(4, 3, nXSprite, -1);
6016          sfxPlay3DSound(pSprite2, 305, -1, 0);
6017          GibSprite(pSprite2, GIBTYPE_14, NULL, NULL);
6018          pSprite = pSprite2;
6019          break;
6020      }
6021      case kTrapExploder:	
6022  	{
6023  		// Defaults for exploder
6024  		nType = 1; int nSnd = 304; int nSeq = 4;
6025  
6026          #ifdef NOONE_EXTENSIONS
6027          // allow to customize hidden exploder trap
6028          if (gModernMap) {
6029              // Temp variables for override via data fields
6030              int tSnd = 0; int tSeq = 0;
6031  
6032  
6033              XSPRITE* pXSPrite = &xsprite[nXSprite];
6034              nType = pXSPrite->data1;  // Explosion type
6035              tSeq = pXSPrite->data2; // SEQ id
6036              tSnd = pXSPrite->data3; // Sound Id
6037  
6038              if (nType <= 1 || nType > kExplodeMax) { nType = 1; nSeq = 4; nSnd = 304; }
6039              else if (nType == 2) { nSeq = 4; nSnd = 305; }
6040              else if (nType == 3) { nSeq = 9; nSnd = 307; }
6041              else if (nType == 4) { nSeq = 5; nSnd = 307; }
6042              else if (nType <= 6) { nSeq = 4; nSnd = 303; }
6043              else if (nType == 7) { nSeq = 4; nSnd = 303; }
6044              else if (nType == 8) { nType = 0; nSeq = 3; nSnd = 303; }
6045  
6046              // Override previous sound and seq assigns
6047              if (tSeq > 0) nSeq = tSeq;
6048              if (tSnd > 0) nSnd = tSnd;
6049          }
6050          #endif
6051  		
6052          if (gSysRes.Lookup(nSeq, "SEQ"))
6053  		    seqSpawn(nSeq, 3, nXSprite, -1);
6054  
6055  		sfxPlay3DSound(pSprite, nSnd, -1, 0);
6056  	}
6057          break;
6058      case kThingPodFireBall:
6059          nType = kExplosionFireball;
6060          seqSpawn(9, 3, nXSprite, -1);
6061          sfxPlay3DSound(pSprite, 307, -1, 0);
6062          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
6063          fxSpawnPodBlood(pSprite, 240);
6064          break;
6065      default:
6066          nType = kExplosionStandard;
6067          seqSpawn(4, 3, nXSprite, -1);
6068          if (Chance(0x8000))
6069              pSprite->cstat |= 4;
6070          sfxPlay3DSound(pSprite, 303, -1, 0);
6071          GibSprite(pSprite, GIBTYPE_5, NULL, NULL);
6072          break;
6073      }
6074      int nSprite = pSprite->index;
6075      xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0;
6076      actPostSprite(nSprite, kStatExplosion);
6077      pSprite->xrepeat = pSprite->yrepeat = explodeInfo[nType].repeat;
6078  
6079      pSprite->flags &= ~3;
6080      pSprite->type = nType;
6081      EXPLOSION *pExplodeInfo = &explodeInfo[nType];
6082      xsprite[nXSprite].target = 0;
6083      xsprite[nXSprite].data1 = pExplodeInfo->ticks;
6084      xsprite[nXSprite].data2 = pExplodeInfo->quakeEffect;
6085      xsprite[nXSprite].data3 = pExplodeInfo->flashEffect;
6086  }
6087  
6088  void actActivateGibObject(spritetype *pSprite, XSPRITE *pXSprite)
6089  {
6090      int vdx = ClipRange(pXSprite->data1, 0, 31);
6091      int vc = ClipRange(pXSprite->data2, 0, 31);
6092      int v4 = ClipRange(pXSprite->data3, 0, 31);
6093      int vbp = pXSprite->data4;
6094      int v8 = pXSprite->dropMsg;
6095      if (vdx > 0)
6096          GibSprite(pSprite, (GIBTYPE)(vdx-1), NULL, NULL);
6097      if (vc > 0)
6098          GibSprite(pSprite, (GIBTYPE)(vc-1), NULL, NULL);
6099      if (v4 > 0 && pXSprite->burnTime > 0)
6100          GibSprite(pSprite, (GIBTYPE)(v4-1), NULL, NULL);
6101      if (vbp > 0)
6102          sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, vbp, pSprite->sectnum);
6103      if (v8 > 0)
6104          actDropObject(pSprite, v8);
6105  
6106      if (!(pSprite->cstat&32768) && !(pSprite->flags&kHitagRespawn))
6107          actPostSprite(pSprite->index, kStatFree);
6108  }
6109  
6110  bool IsUnderWater(spritetype *pSprite)
6111  {
6112      int nSector = pSprite->sectnum;
6113      int nXSector = sector[nSector].extra;
6114      if (nXSector > 0 && nXSector < kMaxXSectors)
6115          if (xsector[nXSector].Underwater)
6116              return 1;
6117      return 0;
6118  }
6119  
6120  void MakeSplash(spritetype *pSprite, XSPRITE *pXSprite);
6121  
6122  void actProcessSprites(void)
6123  {
6124      int nSprite;
6125      int nNextSprite;
6126      
6127      #ifdef NOONE_EXTENSIONS
6128      if (gModernMap) nnExtProcessSuperSprites();
6129      #endif
6130  
6131      for (nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
6132      {
6133          spritetype *pSprite = &sprite[nSprite];
6134  
6135          if (pSprite->flags&32)
6136              continue;
6137          int nXSprite = pSprite->extra;
6138          if (nXSprite > 0) {
6139              XSPRITE *pXSprite = &xsprite[nXSprite];
6140              if ((pXSprite->respawnPending > 0) && !VanillaMode()) // don't process currently respawning thing
6141                  continue;
6142              switch (pSprite->type) {
6143                  case kThingBloodBits:
6144                  case kThingBloodChunks:
6145                  case kThingZombieHead:
6146                      if (pXSprite->locked && gFrameClock >= pXSprite->targetX) pXSprite->locked = 0;
6147                      break;
6148              }
6149  
6150              if (pXSprite->burnTime > 0)
6151              {
6152                  pXSprite->burnTime = ClipLow(pXSprite->burnTime-kTicsPerFrame,0);
6153                  actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, kDamageBurn, 8);
6154              }
6155                                         
6156              if (pXSprite->Proximity) {
6157                  #ifdef NOONE_EXTENSIONS
6158                  // don't process locked or 1-shot things for proximity
6159                  if (gModernMap && (pXSprite->locked || pXSprite->isTriggered)) 
6160                      continue;
6161                  #endif
6162                  
6163                  if (pSprite->type == kThingDroppedLifeLeech) pXSprite->target = -1;
6164                  for (int nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nNextSprite)
6165                  {
6166                      
6167                      nNextSprite = nextspritestat[nSprite2];
6168                      spritetype *pSprite2 = &sprite[nSprite2];
6169  
6170                      if (pSprite2->flags&32) continue;
6171                      XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
6172                      if ((unsigned int)pXSprite2->health > 0) {
6173                     
6174                          #ifdef NOONE_EXTENSIONS
6175                          // allow dudeLockout for proximity flag
6176                          if (gModernMap && pSprite->type != kThingDroppedLifeLeech && pXSprite->DudeLockout && !IsPlayerSprite(pSprite2))
6177                              continue;
6178                          #endif
6179  
6180                          int proxyDist = 96;
6181                          #ifdef NOONE_EXTENSIONS
6182                          if (pSprite->type == kModernThingEnemyLifeLeech) proxyDist = 512;
6183                          #endif
6184                          if (pSprite->type == kThingDroppedLifeLeech && pXSprite->target == -1) {
6185                              PLAYER *pPlayer2 = NULL;
6186                              if (IsPlayerSprite(pSprite2))
6187                                  pPlayer2 = &gPlayer[pSprite2->type - kDudePlayer1];
6188                              int nOwner = actOwnerIdToSpriteId(pSprite->owner);
6189                              if (nSprite2 == nOwner || pSprite2->type == kDudeZombieAxeBuried || pSprite2->type == kDudeRat || pSprite2->type == kDudeBat)
6190                                  continue;
6191                              if (gGameOptions.nGameType == kGameTypeCoop && pPlayer2)
6192                                  continue;
6193                              if (spriRangeIsFine(nOwner)) {
6194                                  spritetype *pOwner = &sprite[nOwner];
6195                                  if (IsPlayerSprite(pOwner)) {
6196                                      PLAYER *pPlayer = &gPlayer[pOwner->type - kDudePlayer1];
6197                                      if (gGameOptions.nGameType == kGameTypeTeams && pPlayer2 && pPlayer->teamId == pPlayer2->teamId)
6198                                          continue;
6199                                  }
6200                              }
6201                              proxyDist = 512;
6202                          }
6203                          
6204                          if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, proxyDist)) {
6205  
6206                              switch (pSprite->type) {
6207                                  case kThingDroppedLifeLeech:
6208                                      if ((Chance(0x4000) || nNextSprite < 0) && (pSprite2->cstat & CLIPMASK0))
6209                                          pXSprite->target = pSprite2->index;
6210                                      else
6211                                          continue;
6212                                      break;
6213                                  #ifdef NOONE_EXTENSIONS
6214                                  case kModernThingTNTProx:
6215                                      if (!IsPlayerSprite(pSprite2)) continue;
6216                                      pSprite->pal = 0;
6217                                      break;
6218                                  case kModernThingEnemyLifeLeech:
6219                                      if (pXSprite->target != pSprite2->index) continue;
6220                                      break;
6221                                  #endif
6222                              }
6223                              if (pSprite->owner == -1) actPropagateSpriteOwner(pSprite, pSprite2);
6224                              trTriggerSprite(nSprite, pXSprite, kCmdSpriteProximity, nSprite2);
6225                          }
6226                      }
6227                  }
6228              }
6229          }
6230      }
6231      for (nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
6232      {
6233          spritetype *pSprite = &sprite[nSprite];
6234  
6235          if (pSprite->flags & 32)
6236              continue;
6237          int nSector = pSprite->sectnum;
6238          int nXSprite = pSprite->extra;
6239          dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
6240          int nXSector = sector[nSector].extra;
6241          XSECTOR *pXSector = NULL;
6242          if (nXSector > 0)
6243          {
6244              dassert(nXSector > 0 && nXSector < kMaxXSectors);
6245              dassert(xsector[nXSector].reference == nSector);
6246              pXSector = &xsector[nXSector];
6247          }
6248          if (pXSector && pXSector->panVel && (pXSector->panAlways || pXSector->state || pXSector->busy))
6249          {
6250              int nType = pSprite->type - kThingBase;
6251              THINGINFO *pThingInfo = &thingInfo[nType];
6252              if (pThingInfo->flags & 1)
6253  
6254                  pSprite->flags |= 1;
6255              if (pThingInfo->flags & 2)
6256  
6257                  pSprite->flags |= 4;
6258          }
6259  
6260          if (pSprite->flags&3)
6261          {
6262              viewBackupSpriteLoc(nSprite, pSprite);
6263              if (pXSector && pXSector->panVel)
6264              {
6265                  int top, bottom;
6266                  GetSpriteExtents(pSprite, &top, &bottom);
6267                  if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom)
6268                  {
6269                      int angle = pXSector->panAngle;
6270                      int speed = 0;
6271                      if (pXSector->panAlways || pXSector->state || pXSector->busy)
6272                      {
6273                          speed = pXSector->panVel << 9;
6274                          if (!pXSector->panAlways && pXSector->busy)
6275                              speed = mulscale16(speed, pXSector->busy);
6276                      }
6277                      if (sector[nSector].floorstat&64)
6278                          angle = (angle+GetWallAngle(sector[nSector].wallptr)+512)&2047;
6279                      int dx = mulscale30(speed, Cos(angle));
6280                      int dy = mulscale30(speed, Sin(angle));
6281                      xvel[nSprite] += dx;
6282                      yvel[nSprite] += dy;
6283                  }
6284              }
6285              actAirDrag(pSprite, 128);
6286  
6287              if (((pSprite->index>>8)&15) == (gFrame&15) && (pSprite->flags&2))
6288                  pSprite->flags |= 4;
6289              if ((pSprite->flags&4) || xvel[nSprite] || yvel[nSprite] || zvel[nSprite] ||
6290                  velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum])
6291              {
6292                  int hit = MoveThing(pSprite);
6293                  if (hit)
6294                  {
6295                      int nXSprite = pSprite->extra;
6296                      if (nXSprite)
6297                      {
6298                          XSPRITE *pXSprite = &xsprite[nXSprite];
6299                          if (pXSprite->Impact)
6300                              trTriggerSprite(nSprite, pXSprite, kCmdOff, ((hit & 0xc000) == 0xc000) ? (hit & 0x3fff) : kCauserGame);
6301                          switch (pSprite->type) {
6302                          case kThingDripWater:
6303                          case kThingDripBlood:
6304                              MakeSplash(pSprite, pXSprite);
6305                              break;
6306                          #ifdef NOONE_EXTENSIONS
6307                          case kModernThingThrowableRock:
6308                              seqSpawn(24, 3, nXSprite, -1);
6309                              if ((hit & 0xc000) == 0xc000)
6310                              {
6311                                  pSprite->xrepeat = 32;
6312                                  pSprite->yrepeat = 32;
6313                                  int nObject = hit & 0x3fff;
6314                                  dassert(nObject >= 0 && nObject < kMaxSprites);
6315                                  spritetype * pObject = &sprite[nObject];
6316                                  actDamageSprite(actSpriteOwnerToSpriteId(pSprite), pObject, kDamageFall, pXSprite->data1);
6317                              }
6318                              break;
6319                          #endif
6320                          case kThingBone:
6321                              seqSpawn(24, 3, nXSprite, -1);
6322                              if ((hit&0xc000) == 0xc000)
6323                              {
6324                                  int nObject = hit & 0x3fff;
6325                                  dassert(nObject >= 0 && nObject < kMaxSprites);
6326                                  spritetype *pObject = &sprite[nObject];
6327                                  actDamageSprite(actSpriteOwnerToSpriteId(pSprite), pObject, kDamageFall, 12);
6328                              }
6329                              break;
6330                          case kThingPodGreenBall:
6331                              if ((hit&0xc000) == 0x4000)
6332                              {
6333                                  actRadiusDamage(actSpriteOwnerToSpriteId(pSprite), pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 200, 1, 20, kDamageExplode, 6, 0);
6334                              }
6335                              else
6336                              {
6337                                  int nObject = hit & 0x3fff;
6338                                  if (((hit&0xc000) == 0xc000) || (VanillaMode() && (nObject >= 0 && nObject < 4096)))
6339                                  {
6340                                      dassert(nObject >= 0 && nObject < kMaxSprites);
6341                                      spritetype *pObject = &sprite[nObject];
6342                                      actDamageSprite(actSpriteOwnerToSpriteId(pSprite), pObject, kDamageFall, 12);
6343                                  }
6344                              }
6345                              evPost(pSprite->index, 3, 0, kCallbackFXPodBloodSplat);
6346                              break;
6347                          case kThingPodFireBall:
6348                              actExplodeSprite(pSprite);
6349                              break;
6350                          case kThingDroppedLifeLeech: // allow player to kill enemies with thrown lifeleech
6351                          {
6352                              if (!WeaponsNotBlood() || VanillaMode())
6353                                  break;
6354                              int nObject = hit & 0x3fff;
6355                              if (((hit & 0xc000) != 0xc000) || !spriRangeIsFine(nObject))
6356                                  break;
6357                              spritetype *pObject = &sprite[nObject];
6358                              const int nOwner = actSpriteOwnerToSpriteId(pSprite);
6359                              int speed = approxDist(xvel[pSprite->index], yvel[pSprite->index]);
6360                              speed = min(mulscale30r(speed, 0x10000), 125); // clamp values to a reasonable value
6361                              if (nOwner == nObject) // stop hitting yourself
6362                              {
6363                                  xvel[pObject->index] = xvel[pSprite->index] >> 2; // push player back
6364                                  yvel[pObject->index] = yvel[pSprite->index] >> 2;
6365                                  xvel[pSprite->index] = -xvel[pSprite->index] >> 2; // invert direction and slow down
6366                                  yvel[pSprite->index] = -yvel[pSprite->index] >> 2;
6367                                  if (speed > 30)
6368                                      sfxPlay3DSound(pSprite, 357, 0, 0); // zombie head sfx
6369                                  break;
6370                              }
6371                              if ((speed < 5) || !IsDudeSprite(pObject)) // if too slow or hit a non-dude sprite, break
6372                              {
6373                                  xvel[pSprite->index] = -xvel[pSprite->index] >> 1; // invert direction and slow down
6374                                  yvel[pSprite->index] = -yvel[pSprite->index] >> 1;
6375                                  if (pObject->type >= kThingBase && pObject->type < kThingMax) // do damage to thing sprite
6376                                      actDamageSprite(nOwner, pObject, kDamageFall, speed * 3);
6377                                  break;
6378                              }
6379                              actDamageSprite(nOwner, pObject, (speed < 110) ? kDamageFall : kDamageExplode, speed * 12);
6380                              xvel[pObject->index] = xvel[pSprite->index] >> 2; // push enemy back
6381                              yvel[pObject->index] = yvel[pSprite->index] >> 2;
6382                              yvel[pObject->index] += 58254;
6383                              xvel[pSprite->index] = -xvel[pSprite->index] >> 2; // invert direction and slow down
6384                              yvel[pSprite->index] = -yvel[pSprite->index] >> 2;
6385                              bool playKickSfx = true;
6386                              if (pObject->extra > 0) // if object has extra
6387                              {
6388                                  XSPRITE *pXObject = &xsprite[pObject->extra];
6389                                  if (pXObject->health <= 0) // if killed enemy, play meaty sfx
6390                                  {
6391                                      sfxPlay3DSound(pSprite, 318 + Random(1), 0, 0);
6392                                      playKickSfx = false; // don't play two sounds at once
6393                                  }
6394                              }
6395                              if (playKickSfx && (speed > 30))
6396                                  sfxPlay3DSound(pSprite, 357, 0, 0); // zombie head sfx
6397                              break;
6398                          }
6399                          }
6400                      }
6401                  }
6402              }
6403          }
6404      }
6405      for (nSprite = headspritestat[kStatProjectile]; nSprite >= 0; nSprite = nextspritestat[nSprite])
6406      {
6407          spritetype *pSprite = &sprite[nSprite];
6408  
6409          if (pSprite->flags & 32)
6410              continue;
6411          viewBackupSpriteLoc(nSprite, pSprite);
6412          if ((pSprite->type != kMissileShell) && (pSprite->type != kMissileBullet)) // if regular missile sprites
6413          {
6414              int hit = MoveMissile(pSprite);
6415              if (hit >= 0)
6416                  actImpactMissile(pSprite, hit);
6417          }
6418          else // bullet projectile sprites
6419          {
6420              MoveMissileBullet(pSprite);
6421          }
6422      }
6423      for (nSprite = headspritestat[kStatExplosion]; nSprite >= 0; nSprite = nextspritestat[nSprite])
6424      {
6425          char sectmap[bitmap_size(kMaxSectors)];
6426          spritetype *pSprite = &sprite[nSprite];
6427  
6428          if (pSprite->flags & 32)
6429              continue;
6430          int nOwner = actSpriteOwnerToSpriteId(pSprite);
6431          int nType = pSprite->type;
6432          dassert(nType >= 0 && nType < kExplodeMax);
6433          EXPLOSION *pExplodeInfo = &explodeInfo[nType];
6434          int nXSprite = pSprite->extra;
6435          dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
6436          XSPRITE *pXSprite = &xsprite[nXSprite];
6437          int x = pSprite->x;
6438          int y = pSprite->y;
6439          int z = pSprite->z;
6440          int nSector = pSprite->sectnum;
6441          gAffectedSectors[0] = -1;
6442          gAffectedXWalls[0] = -1;
6443          int radius = pExplodeInfo->radius;
6444  
6445          #ifdef NOONE_EXTENSIONS
6446          // Allow to override explosion radius by data4 field of any sprite which have statnum 2 set in editor
6447          // or of Hidden Exploder.
6448          if (gModernMap && pXSprite->data4 > 0)
6449              radius = pXSprite->data4;
6450          #endif
6451  
6452          if (gNukeMode && !VanillaMode()) // nuke cheat
6453              radius <<= 2;
6454          // GetClosestSpriteSectors() has issues checking some sectors due to optimizations
6455          // the new flag bAccurateCheck for GetClosestSpriteSectors() does rectify these issues, but this may cause unintended side effects for level scripted explosions
6456          // so only allow this new checking method for dude spawned explosions
6457          const bool bAccurateCheck = (nOwner >= 0) && !VanillaMode() && IsDudeSprite(&sprite[nOwner]); // use new sector checking logic
6458          GetClosestSpriteSectors(nSector, x, y, radius, gAffectedSectors, sectmap, gAffectedXWalls, bAccurateCheck);
6459  
6460          for (int i = 0; i < kMaxXWalls; i++)
6461          {
6462              int nWall = gAffectedXWalls[i];
6463              if (nWall == -1)
6464                  break;
6465              XWALL *pXWall = &xwall[wall[nWall].extra];
6466              trTriggerWall(nWall, pXWall, kCmdWallImpact, (nOwner >= 0) ? nOwner : nSprite);
6467          }
6468  
6469          for (int nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2])
6470          {
6471              spritetype *pDude = &sprite[nSprite2];
6472              const char bNukeMode = gNukeMode && IsPlayerSprite(pDude) && (nOwner >= 0) && (&sprite[nOwner] == pDude) && !VanillaMode(); // do not use larger radius for player created explosions with nuke cheat
6473  
6474              if (pDude->flags & 32)
6475                  continue;
6476              if (TestBitString(sectmap, pDude->sectnum))
6477              {
6478                  if (pXSprite->data1 && CheckProximity(pDude, x, y, z, nSector, bNukeMode ? radius>>2 : radius))
6479                  {
6480                      if (pExplodeInfo->dmg && pXSprite->target == 0)
6481                      {
6482                          pXSprite->target = 1;
6483                          actDamageSprite(nOwner, pDude, kDamageFall, (pExplodeInfo->dmg+Random(pExplodeInfo->dmgRng))<<4);
6484                      }
6485                      if (pExplodeInfo->dmgType)
6486                          ConcussSprite(nOwner, pDude, x, y, z, pExplodeInfo->dmgType);
6487                      if (pExplodeInfo->burnTime)
6488                      {
6489                          dassert(pDude->extra > 0 && pDude->extra < kMaxXSprites);
6490                          XSPRITE *pXDude = &xsprite[pDude->extra];
6491                          if (!pXDude->burnTime)
6492                              evPost(nSprite2, 3, 0, kCallbackFXFlameLick);
6493                          actBurnSprite(pSprite->owner, pXDude, pExplodeInfo->burnTime<<2);
6494                      }
6495                  }
6496              }
6497          }
6498          
6499          for (int nSprite2 = headspritestat[kStatThing]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2])
6500          {
6501              spritetype *pThing = &sprite[nSprite2];
6502  
6503              if (pThing->flags & 32)
6504                  continue;
6505              if (TestBitString(sectmap, pThing->sectnum))
6506              {
6507                  if (pXSprite->data1 && CheckProximity(pThing, x, y, z, nSector, radius))
6508                  {
6509                      XSPRITE *pXSprite2 = &xsprite[pThing->extra];
6510                      if (!pXSprite2->locked)
6511                      {
6512                          if (pExplodeInfo->dmgType)
6513                              ConcussSprite(nOwner, pThing, x, y, z, pExplodeInfo->dmgType);
6514                          if (pExplodeInfo->burnTime)
6515                          {
6516                              dassert(pThing->extra > 0 && pThing->extra < kMaxXSprites);
6517                              XSPRITE *pXThing = &xsprite[pThing->extra];
6518                              if (pThing->type == kThingTNTBarrel && !pXThing->burnTime)
6519                                  evPost(nSprite2, 3, 0, kCallbackFXFlameLick);
6520                              actBurnSprite(pSprite->owner, pXThing, pExplodeInfo->burnTime<<2);
6521                          }
6522                      }
6523                  }
6524              }
6525          }
6526  
6527          if ((gGameOptions.nGoreBehavior > 1) && !VanillaMode() && (nType == 1 || nType == 7) && (nOwner >= 0) && IsPlayerSprite(&sprite[nOwner]))
6528          {
6529              for (int nSprite2 = headspritestat[kStatFX]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2])
6530              {
6531                  spritetype *pFx = &sprite[nSprite2];
6532  
6533                  if (pFx->statnum == kStatFree) // skip free'd fx sprite
6534                      continue;
6535                  switch (pFx->type)
6536                  {
6537                  case FX_13:
6538                  case FX_27:
6539                  case FX_34:
6540                      break;
6541                  default:
6542                      continue;
6543                  }
6544                  if (pFx->cstat&CSTAT_SPRITE_ALIGNMENT_MASK)
6545                      continue;
6546                  if (!TestBitString(sectmap, pFx->sectnum))
6547                      continue;
6548                  if (!pXSprite->data1 || !CheckProximity(pFx, x, y, z, nSector, radius))
6549                      continue;
6550                  if (!pExplodeInfo->dmgType)
6551                      continue;
6552                  int dx = pFx->x-x;
6553                  int dy = pFx->y-y;
6554                  int dz = (pFx->z-z)>>4;
6555                  int size = (tilesiz[pFx->picnum].x*pFx->xrepeat*tilesiz[pFx->picnum].y*pFx->yrepeat)>>1;
6556                  int t = scale(pExplodeInfo->dmgType, size, 16);
6557                  dx = mulscale16(t, dx);
6558                  dy = mulscale16(t, dy);
6559                  dz = mulscale16(t<<1, dz);
6560                  const int nSprite3 = pFx->index;
6561                  xvel[nSprite3] += dx;
6562                  yvel[nSprite3] += dy;
6563                  zvel[nSprite3] += dz;
6564              }
6565          }
6566          
6567          for (int p = connecthead; p >= 0; p = connectpoint2[p])
6568          {
6569              spritetype *pSprite2 = gPlayer[p].pSprite;
6570              int dx = (x - pSprite2->x)>>4;
6571              int dy = (y - pSprite2->y)>>4;
6572              int dz = (z - pSprite2->z)>>8;
6573              int nDist = dx*dx+dy*dy+dz*dz+0x40000;
6574              int t = divscale16(pXSprite->data2, nDist);
6575              gPlayer[p].flickerEffect += t;
6576          }
6577          if (gMe->flickerEffect)
6578              ctrlJoystickRumble(gMe->flickerEffect);
6579  
6580          #ifdef NOONE_EXTENSIONS
6581          if (gModernMap && pXSprite->data1)
6582          {
6583              IDLIST* pList;  int32_t len;
6584              int32_t* pDb; spritetype* pSpr;
6585  
6586              if (pExplodeInfo->dmgType != 0)
6587              {
6588                  pList = &gPhysSpritesList;
6589                  pDb = pList->Last(); len = pList->Length();
6590                  
6591                  // add impulse for sprites from physics list
6592                  while (--len >= 0)
6593                  {
6594                      pSpr = &sprite[*pDb];
6595                      if (!(pSpr->flags & kHitagFree))
6596                      {
6597                          if (!isOnRespawn(pSpr) && TestBitString(sectmap, pSpr->sectnum) && CheckProximity(pSpr, x, y, z, nSector, radius))
6598                              debrisConcuss(nOwner, pSpr->index, x, y, z, pExplodeInfo->dmgType);
6599  
6600                          pDb--;
6601                          continue;
6602                      }
6603                  }
6604              }
6605  
6606              
6607              pList = &gImpactSpritesList;
6608              pDb = pList->Last(); len = pList->Length();
6609  
6610              // trigger sprites from impact list
6611              while (--len >= 0 && *pDb != kListEndDefault)
6612              {
6613                  pSpr = &sprite[*pDb];
6614                  if (!(pSpr->flags & kHitagFree))
6615                  {
6616                      XSPRITE* pXSpr = &xsprite[pSpr->extra];
6617                      if (pXSpr->Impact && !pXSpr->isTriggered)
6618                      {
6619                          if (!isOnRespawn(pSpr) && TestBitString(sectmap, pSpr->sectnum) && CheckProximity(pSpr, x, y, z, nSector, radius))
6620                              trTriggerSprite(pSpr->index, pXSpr, kCmdSpriteImpact, (nOwner >= 0) ? nOwner : nSprite);
6621                          
6622                          pDb--;
6623                          continue;
6624                      }
6625                  }
6626  
6627                  // remove and refresh ptr!
6628                  pDb = pList->Remove(*pDb);
6629              }
6630          }
6631          
6632          if (!gModernMap || !(pSprite->flags & kModernTypeFlag1))
6633          {
6634              // do not remove explosion.
6635              // can be useful when designer wants put explosion
6636              // generator in map manually via sprite statnum 2.
6637              pXSprite->data1 = ClipLow(pXSprite->data1 - kTicsPerFrame, 0);
6638              pXSprite->data2 = ClipLow(pXSprite->data2 - kTicsPerFrame, 0);
6639              pXSprite->data3 = ClipLow(pXSprite->data3 - kTicsPerFrame, 0);
6640          }
6641          #else
6642          pXSprite->data1 = ClipLow(pXSprite->data1 - kTicsPerFrame, 0);
6643          pXSprite->data2 = ClipLow(pXSprite->data2 - kTicsPerFrame, 0);
6644          pXSprite->data3 = ClipLow(pXSprite->data3 - kTicsPerFrame, 0);
6645          #endif
6646  
6647          if (pXSprite->data1 == 0 && pXSprite->data2 == 0 && pXSprite->data3 == 0 && seqGetStatus(3, nXSprite) < 0)
6648              actPostSprite(nSprite, kStatFree);
6649      }
6650     
6651      for (nSprite = headspritestat[kStatTraps]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
6652          spritetype *pSprite = &sprite[nSprite];
6653  
6654          if (pSprite->flags & 32)
6655              continue;
6656          int nXSprite = pSprite->extra;
6657          //dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
6658          if (nXSprite <= 0 || nXSprite >= kMaxXSprites)
6659              continue;
6660          XSPRITE *pXSprite = &xsprite[nXSprite];
6661          switch (pSprite->type) {
6662          case kTrapSawCircular:
6663              pXSprite->data2 = ClipLow(pXSprite->data2-kTicsPerFrame, 0);
6664              break;
6665          case kTrapFlame:
6666              if (pXSprite->state && seqGetStatus(3, nXSprite) < 0) {
6667                  int x = pSprite->x;
6668                  int y = pSprite->y;
6669                  int z = pSprite->z;
6670                  int t = (pXSprite->data1<<23)/120;
6671                  int dx = mulscale30(t, Cos(pSprite->ang));
6672                  int dy = mulscale30(t, Sin(pSprite->ang));
6673                  for (int i = 0; i < 2; i++)
6674                  {
6675                      spritetype *pFX = gFX.fxSpawn(FX_32, pSprite->sectnum, x, y, z);
6676                      if (pFX)
6677                      {
6678                          xvel[pFX->index] = dx + Random2(0x8888);
6679                          yvel[pFX->index] = dy + Random2(0x8888);
6680                          zvel[pFX->index] = Random2(0x8888);
6681                      }
6682                      x += (dx/2)>>12;
6683                      y += (dy/2)>>12;
6684                  }
6685                  dy = Sin(pSprite->ang)>>16;
6686                  dx = Cos(pSprite->ang)>>16;
6687                  gVectorData[kVectorTchernobogBurn].maxDist = pXSprite->data1<<9;
6688                  actFireVector(pSprite, 0, 0, dx, dy, Random2(0x8888), kVectorTchernobogBurn);
6689              }
6690              break;
6691          }
6692      }
6693      for (nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
6694      {
6695          spritetype *pSprite = &sprite[nSprite];
6696  
6697          if (pSprite->flags & 32)
6698              continue;
6699          int nXSprite = pSprite->extra;
6700          if (nXSprite > 0)
6701          {
6702              XSPRITE *pXSprite = &xsprite[nXSprite];
6703              #ifdef NOONE_EXTENSIONS
6704              const bool burningType = IsBurningDude(pSprite);
6705              #else
6706              const bool burningType = (pSprite->type == kDudeBurningInnocent) || (pSprite->type == kDudeBurningCultist) || (pSprite->type == kDudeBurningZombieAxe) || (pSprite->type == kDudeBurningZombieButcher) || (pSprite->type == kDudeBurningTinyCaleb) || (pSprite->type == kDudeBurningBeast);
6707              #endif
6708              const bool fixBurnGlitch = EnemiesNBlood() && burningType && !VanillaMode(); // if enemies are burning, always apply burning damage per tick
6709              if ((pXSprite->burnTime > 0) || fixBurnGlitch)
6710              {
6711                  switch (pSprite->type)
6712                  {
6713                  case kDudeBurningInnocent:
6714                  case kDudeBurningCultist:
6715                  case kDudeBurningZombieAxe:
6716                  case kDudeBurningZombieButcher:
6717                      actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, kDamageBurn, 8);
6718                      break;
6719                  default:
6720                      pXSprite->burnTime = ClipLow(pXSprite->burnTime-kTicsPerFrame, 0);
6721                      actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, kDamageBurn, 8);
6722                      break;
6723                  }
6724              }
6725  
6726              if (pSprite->type == kDudeCerberusTwoHead)
6727              {
6728                  if (pXSprite->health <= 0 && seqGetStatus(3, nXSprite) < 0)
6729                  {
6730                      pXSprite->health = dudeInfo[28].startHealth<<4;
6731                      pSprite->type = kDudeCerberusOneHead;
6732                      if (pXSprite->target != -1)
6733                          aiSetTarget(pXSprite, pXSprite->target);
6734                      aiActivateDude(pSprite, pXSprite);
6735                  }
6736              }
6737              if (pXSprite->Proximity && !pXSprite->isTriggered)
6738              {
6739                  for (int nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nNextSprite)
6740                  {
6741                      nNextSprite = nextspritestat[nSprite2];
6742                      spritetype *pSprite2 = &sprite[nSprite2];
6743  
6744                      if (pSprite2->flags&32)
6745                          continue;
6746                      XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
6747                      if ((unsigned int)pXSprite2->health > 0 && IsPlayerSprite(pSprite2)) {
6748                          if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 128))
6749                              trTriggerSprite(nSprite, pXSprite, kCmdSpriteProximity, nSprite2);
6750                      }
6751                  }
6752              }
6753              if (IsPlayerSprite(pSprite))
6754              {
6755                  PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
6756                  if (pPlayer->voodooTargets)
6757                      voodooTarget(pPlayer);
6758                  if (pPlayer->hand && Chance(0x8000))
6759                      actDamageSprite(nSprite, pSprite, kDamageDrown, 12);
6760                  if (pPlayer->isUnderwater)
6761                  {
6762                      const char bDivingSuit = packItemActive(pPlayer, kPackDivingSuit);
6763                      if (bDivingSuit || pPlayer->godMode)
6764                          pPlayer->underwaterTime = kTicRate*10;
6765                      else
6766                          pPlayer->underwaterTime = ClipLow(pPlayer->underwaterTime-kTicsPerFrame, 0);
6767                      if (pPlayer->underwaterTime < 1080 && packCheckItem(pPlayer, kPackDivingSuit) && !bDivingSuit && (((pPlayer->pXSprite->health > 0) && (gAutoDivingSuit || numplayers > 1)) || VanillaMode())) // don't activate diving suit if player is dead
6768                          packUseItem(pPlayer, kPackDivingSuit);
6769                      if (!pPlayer->underwaterTime)
6770                      {
6771                          pPlayer->chokeEffect += kTicsPerFrame;
6772                          if (Chance(pPlayer->chokeEffect))
6773                              actDamageSprite(nSprite, pSprite, kDamageDrown, 3<<4);
6774                      }
6775                      else
6776                          pPlayer->chokeEffect = 0;
6777                      if (xvel[nSprite] || yvel[nSprite])
6778                          sfxPlay3DSound(pSprite, 709, 100, 2);
6779                      pPlayer->bubbleTime = ClipLow(pPlayer->bubbleTime-kTicsPerFrame, 0);
6780                  }
6781                  else if (gGameOptions.nGameType == kGameTypeSinglePlayer)
6782                  {
6783                      if (pPlayer->pXSprite && (pPlayer->pXSprite->health > 0) && (pPlayer->restTime >= 1200) && Chance(0x200))
6784                      {
6785                          const int nSound = 3100+Random(11);
6786                          pPlayer->restTime = -1;
6787                          if ((gCalebTalk == 0) || (gCalebTalk == 2))
6788                              sfxPlay3DSound(pSprite, nSound, 0, 2);
6789                      }
6790                  }
6791              }
6792              ProcessTouchObjects(pSprite, nXSprite);
6793          }
6794      }
6795      for (nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
6796      {
6797          spritetype *pSprite = &sprite[nSprite];
6798  
6799          if (pSprite->flags & 32)
6800              continue;
6801          int nXSprite = pSprite->extra;
6802          dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
6803          int nSector = pSprite->sectnum;
6804          viewBackupSpriteLoc(nSprite, pSprite);
6805          int nXSector = sector[nSector].extra;
6806          XSECTOR *pXSector = NULL;
6807          if (nXSector > 0)
6808          {
6809              dassert(nXSector > 0 && nXSector < kMaxXSectors);
6810              dassert(xsector[nXSector].reference == nSector);
6811              pXSector = &xsector[nXSector];
6812          }
6813          if (pXSector)
6814          {
6815              int top, bottom;
6816              GetSpriteExtents(pSprite, &top, &bottom);
6817              if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom)
6818              {
6819                  int angle = pXSector->panAngle;
6820                  int speed = 0;
6821                  if (pXSector->panAlways || pXSector->state || pXSector->busy)
6822                  {
6823                      speed = pXSector->panVel << 9;
6824                      if (!pXSector->panAlways && pXSector->busy)
6825                          speed = mulscale16(speed, pXSector->busy);
6826                  }
6827                  if (sector[nSector].floorstat&64)
6828                      angle = (angle+GetWallAngle(sector[nSector].wallptr)+512)&2047;
6829                  int dx = mulscale30(speed, Cos(angle));
6830                  int dy = mulscale30(speed, Sin(angle));
6831                  xvel[nSprite] += dx;
6832                  yvel[nSprite] += dy;
6833              }
6834          }
6835          if (pXSector && pXSector->Underwater)
6836              actAirDrag(pSprite, 5376);
6837          else
6838              actAirDrag(pSprite, 128);
6839  
6840          if ((pSprite->flags&4) || xvel[nSprite] || yvel[nSprite] || zvel[nSprite] ||
6841              velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum])
6842              MoveDude(pSprite);
6843      }
6844      for (nSprite = headspritestat[kStatFlare]; nSprite >= 0; nSprite = nextspritestat[nSprite])
6845      {
6846          spritetype *pSprite = &sprite[nSprite];
6847  
6848          if (pSprite->flags & 32)
6849              continue;
6850          int nXSprite = pSprite->extra;
6851          dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
6852          XSPRITE *pXSprite = &xsprite[nXSprite];
6853          int nTarget = pXSprite->target;
6854          dassert(nTarget >= 0);
6855          viewBackupSpriteLoc(nSprite, pSprite);
6856          dassert(nTarget < kMaxSprites);
6857          spritetype *pTarget = &sprite[nTarget];
6858          if (pTarget->statnum == kMaxStatus)
6859          {
6860              GibSprite(pSprite, GIBTYPE_17, NULL, NULL);
6861              actPostSprite(pSprite->index, kStatFree);
6862          }
6863          if (pTarget->extra > 0 && xsprite[pTarget->extra].health > 0)
6864          {
6865              int x = pTarget->x+mulscale30r(Cos(pXSprite->goalAng+pTarget->ang), pTarget->clipdist*2);
6866              int y = pTarget->y+mulscale30r(Sin(pXSprite->goalAng+pTarget->ang), pTarget->clipdist*2);
6867              int z = pTarget->z+pXSprite->targetZ;
6868              vec3_t pos = { x, y, z };
6869              setsprite(nSprite,&pos);
6870              xvel[nSprite] = xvel[nTarget];
6871              yvel[nSprite] = yvel[nTarget];
6872              zvel[nSprite] = zvel[nTarget];
6873          }
6874          else
6875          {
6876              GibSprite(pSprite, GIBTYPE_17, NULL, NULL);
6877              actPostSprite(pSprite->index, kStatFree);
6878          }
6879      }
6880      aiProcessDudes();
6881      gFX.fxProcess();
6882  }
6883  
6884  spritetype * actSpawnSprite(int nSector, int x, int y, int z, int nStat, char a6)
6885  {
6886      int nSprite = InsertSprite(nSector, nStat);
6887      if (nSprite >= 0)
6888          sprite[nSprite].extra = -1;
6889      else
6890      {
6891          nSprite = headspritestat[kStatPurge];
6892          dassert(nSprite >= 0);
6893          dassert(nSector >= 0 && nSector < kMaxSectors);
6894          ChangeSpriteSect(nSprite, nSector);
6895          actPostSprite(nSprite, nStat);
6896      }
6897      vec3_t pos = { x, y, z };
6898      setsprite(nSprite, &pos);
6899      spritetype *pSprite = &sprite[nSprite];
6900      pSprite->type = kSpriteDecoration;
6901      if (a6 && pSprite->extra == -1)
6902      {
6903          int nXSprite = dbInsertXSprite(nSprite);
6904          gSpriteHit[nXSprite].florhit = 0;
6905          gSpriteHit[nXSprite].ceilhit = 0;
6906          if (!VanillaMode())
6907              xsprite[nXSprite].target = -1;
6908      }
6909      return pSprite;
6910  }
6911  
6912  spritetype * actSpawnSprite(spritetype *pSource, int nStat);
6913  
6914  spritetype *actSpawnDude(spritetype *pSource, short nType, int a3, int a4)
6915  {
6916      spritetype *pSprite2 = actSpawnSprite(pSource, kStatDude);
6917      if (!pSprite2) return NULL;
6918      XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
6919      int angle = pSource->ang;
6920      int nDude = nType-kDudeBase;
6921      int x, y, z;
6922      z = a4 + pSource->z;
6923      if (a3 < 0)
6924      {
6925          x = pSource->x;
6926          y = pSource->y;
6927      }
6928      else
6929      {
6930          x = pSource->x+mulscale30r(Cos(angle), a3);
6931          y = pSource->y+mulscale30r(Sin(angle), a3);
6932      }
6933      pSprite2->type = nType;
6934      if (EnemiesNBlood() && !VanillaMode())
6935          pSprite2->inittype = nType;
6936      pSprite2->ang = angle;
6937      vec3_t pos = { x, y, z };
6938      setsprite(pSprite2->index, &pos);
6939      pSprite2->cstat |= 0x1101;
6940      pSprite2->clipdist = getDudeInfo(nDude+kDudeBase)->clipdist;
6941      pXSprite2->health = getDudeInfo(nDude+kDudeBase)->startHealth<<4;
6942      if (!VanillaMode()) // don't allow newly spawned enemies to respawn
6943          pXSprite2->respawn = 1;
6944      if (gSysRes.Lookup(getDudeInfo(nDude+kDudeBase)->seqStartID, "SEQ"))
6945          seqSpawn(getDudeInfo(nDude+kDudeBase)->seqStartID, 3, pSprite2->extra, -1);
6946      
6947      #ifdef NOONE_EXTENSIONS
6948      // add a way to inherit some values of spawner type 18 by dude.
6949      // This way designer can count enemies via switches and do many other interesting things.
6950      if (gModernMap && pSource->flags & kModernTypeFlag1) {
6951          XSPRITE* pXSource = &xsprite[pSource->extra];
6952          switch (pSource->type) { // allow inheriting only for selected source types
6953              case kMarkerDudeSpawn:
6954                  //inherit pal?
6955                  if (pSprite2->pal <= 0) pSprite2->pal = pSource->pal;
6956  
6957                  // inherit spawn sprite trigger settings, so designer can count monsters.
6958                  pXSprite2->txID = pXSource->txID;
6959                  pXSprite2->command = pXSource->command;
6960                  pXSprite2->triggerOn = pXSource->triggerOn;
6961                  pXSprite2->triggerOff = pXSource->triggerOff;
6962  
6963                  // inherit drop items
6964                  pXSprite2->dropMsg = pXSource->dropMsg;
6965  
6966                  // inherit dude flags
6967                  pXSprite2->dudeDeaf = pXSource->dudeDeaf;
6968                  pXSprite2->dudeGuard = pXSource->dudeGuard;
6969                  pXSprite2->dudeAmbush = pXSource->dudeAmbush;
6970                  pXSprite2->dudeFlag4 = pXSource->dudeFlag4;
6971                  pXSprite2->unused1 = pXSource->unused1;
6972                  break;
6973          }
6974      }
6975      #endif
6976  
6977      aiInitSprite(pSprite2);
6978      return pSprite2;
6979  }
6980  
6981  spritetype * actSpawnSprite(spritetype *pSource, int nStat)
6982  {
6983      int nSprite = InsertSprite(pSource->sectnum, nStat);
6984      if (nSprite < 0)
6985      {
6986          nSprite = headspritestat[kStatPurge];
6987          dassert(nSprite >= 0);
6988          dassert(pSource->sectnum >= 0 && pSource->sectnum < kMaxSectors);
6989          ChangeSpriteSect(nSprite, pSource->sectnum);
6990          actPostSprite(nSprite, nStat);
6991      }
6992      spritetype *pSprite = &sprite[nSprite];
6993      pSprite->x = pSource->x;
6994      pSprite->y = pSource->y;
6995      pSprite->z = pSource->z;
6996      xvel[nSprite] = xvel[pSource->index];
6997      yvel[nSprite] = yvel[pSource->index];
6998      zvel[nSprite] = zvel[pSource->index];
6999      pSprite->flags = 0;
7000      int nXSprite = dbInsertXSprite(nSprite);
7001      gSpriteHit[nXSprite].florhit = 0;
7002      gSpriteHit[nXSprite].ceilhit = 0;
7003      if (!VanillaMode())
7004          xsprite[nXSprite].target = -1;
7005      return pSprite;
7006  }
7007  
7008  spritetype * actSpawnThing(int nSector, int x, int y, int z, int nThingType)
7009  {
7010      dassert(nThingType >= kThingBase && nThingType < kThingMax);
7011      spritetype *pSprite = actSpawnSprite(nSector, x, y, z, 4, 1);
7012      int nType = nThingType-kThingBase;
7013      int nThing = pSprite->index;
7014      int nXThing = pSprite->extra;
7015      pSprite->type = nThingType;
7016      dassert(nXThing > 0 && nXThing < kMaxXSprites);
7017      XSPRITE *pXThing = &xsprite[nXThing];
7018      THINGINFO *pThingInfo = &thingInfo[nType];
7019      pXThing->health = pThingInfo->startHealth<<4;
7020      pSprite->clipdist = pThingInfo->clipdist;
7021      pSprite->flags = pThingInfo->flags;
7022      if (pSprite->flags & 2)
7023          pSprite->flags |= 4;
7024      pSprite->cstat |= pThingInfo->cstat;
7025      pSprite->picnum = pThingInfo->picnum;
7026      pSprite->shade = pThingInfo->shade;
7027      pSprite->pal = pThingInfo->pal;
7028      if (pThingInfo->xrepeat)
7029          pSprite->xrepeat = pThingInfo->xrepeat;
7030      if (pThingInfo->yrepeat)
7031          pSprite->yrepeat = pThingInfo->yrepeat;
7032      SetBitString(show2dsprite, pSprite->index);
7033      switch (nThingType) {
7034      case kThingVoodooHead:
7035          pXThing->data1 = 0;
7036          pXThing->data2 = 0;
7037          pXThing->data3 = 0;
7038          pXThing->data4 = 0;
7039          pXThing->state = 1;
7040          pXThing->triggerOnce = 1;
7041          pXThing->isTriggered = 0;
7042          break;
7043      case kThingDroppedLifeLeech:
7044      #ifdef NOONE_EXTENSIONS
7045      case kModernThingEnemyLifeLeech:
7046      #endif
7047          pXThing->data1 = 0;
7048          pXThing->data2 = 0;
7049          pXThing->data3 = 0;
7050          pXThing->data4 = 0;
7051          pXThing->state = 1;
7052          pXThing->triggerOnce = 0;
7053          pXThing->isTriggered = 0;
7054          break;
7055      case kThingZombieHead:
7056          pXThing->data1 = 8;
7057          pXThing->data2 = 0;
7058          pXThing->data3 = 0;
7059          pXThing->data4 = 318;
7060          pXThing->targetX = (int)gFrameClock+180.0;
7061          pXThing->locked = 1;
7062          pXThing->state = 1;
7063          pXThing->triggerOnce = 0;
7064          pXThing->isTriggered = 0;
7065          break;
7066      case kThingBloodBits:
7067      case kThingBloodChunks:
7068          pXThing->data1 = (nThingType == kThingBloodBits) ? 19 : 8;
7069          pXThing->data2 = 0;
7070          pXThing->data3 = 0;
7071          pXThing->data4 = 319;
7072          pXThing->targetX = (int)gFrameClock+180.0;
7073          pXThing->locked = 1;
7074          pXThing->state = 1;
7075          pXThing->triggerOnce = 0;
7076          pXThing->isTriggered = 0;
7077          break;
7078      case kThingArmedTNTStick:
7079          evPost(nThing, 3, 0, kCallbackFXDynPuff);
7080          sfxPlay3DSound(pSprite, 450, 0, 0);
7081          break;
7082      case kThingArmedTNTBundle:
7083          sfxPlay3DSound(pSprite, 450, 0, 0);
7084          evPost(nThing, 3, 0, kCallbackFXDynPuff);
7085          break;
7086      case kThingArmedSpray:
7087          evPost(nThing, 3, 0, kCallbackFXDynPuff);
7088          break;
7089      }
7090      return pSprite;
7091  }
7092  
7093  spritetype * actFireThing(spritetype *pSprite, int a2, int a3, int a4, int thingType, int a6)
7094  {
7095      dassert(thingType >= kThingBase && thingType < kThingMax);
7096      int x = pSprite->x+mulscale30(a2, Cos(pSprite->ang+512));
7097      int y = pSprite->y+mulscale30(a2, Sin(pSprite->ang+512));
7098      int z = pSprite->z+a3;
7099      x += mulscale28(pSprite->clipdist, Cos(pSprite->ang));
7100      y += mulscale28(pSprite->clipdist, Sin(pSprite->ang));
7101      int hit = HitScan(pSprite, z, x-pSprite->x, y-pSprite->y, 0, CLIPMASK0, pSprite->clipdist);
7102      if (ProjectilesNotBlood() && IsPlayerSprite(pSprite) && !VanillaMode() && (hit == 3) && spriRangeIsFine(gHitInfo.hitsprite) && !IsDudeSprite(&sprite[gHitInfo.hitsprite])) // if hit a non-dude sprite, check that the pixel hit is not transparent (e.g.: tree sprites in CPSL)
7103      {
7104          if (CheckHitSpriteAlpha(pSprite->x, pSprite->y, x-pSprite->x, y-pSprite->y, &gHitInfo))
7105              hit = -1;
7106      }
7107      if (hit != -1)
7108      {
7109          x = gHitInfo.hitx-mulscale28(pSprite->clipdist<<1, Cos(pSprite->ang));
7110          y = gHitInfo.hity-mulscale28(pSprite->clipdist<<1, Sin(pSprite->ang));
7111      }
7112      spritetype *pThing = actSpawnThing(pSprite->sectnum, x, y, z, thingType);
7113      actPropagateSpriteOwner(pThing, pSprite);
7114      pThing->ang = pSprite->ang;
7115      xvel[pThing->index] = mulscale30(a6, Cos(pThing->ang));
7116      yvel[pThing->index] = mulscale30(a6, Sin(pThing->ang));
7117      zvel[pThing->index] = mulscale14(a6, a4);
7118      xvel[pThing->index] += xvel[pSprite->index]/2;
7119      yvel[pThing->index] += yvel[pSprite->index]/2;
7120      zvel[pThing->index] += zvel[pSprite->index]/2;
7121      return pThing;
7122  }
7123  
7124  spritetype* actFireMissile(spritetype *pSprite, int a2, int a3, int a4, int a5, int a6, int nType)
7125  {
7126      dassert(nType >= kMissileBase && nType < kMissileMax);
7127      char v4 = 0;
7128      int nSprite = pSprite->index;
7129      MissileType *pMissileInfo = &missileInfo[nType-kMissileBase];
7130      int x = pSprite->x+mulscale30(a2, Cos(pSprite->ang+512));
7131      int y = pSprite->y+mulscale30(a2, Sin(pSprite->ang+512));
7132      int z = pSprite->z+a3;
7133      int clipdist = pMissileInfo->clipDist+pSprite->clipdist;
7134      x += mulscale28(clipdist, Cos(pSprite->ang));
7135      y += mulscale28(clipdist, Sin(pSprite->ang));
7136      const vec3_t bakPos = pSprite->xyz;
7137      const short bakSect = pSprite->sectnum;
7138      bool restorePosSect = false;
7139      if (gGameOptions.bSectorBehavior && IsPlayerSprite(pSprite) && !VanillaMode()) // fix weird edge case when spawning missiles above waterline while underwater
7140      {
7141          restorePosSect = true;
7142          int nSector = pSprite->sectnum;
7143          const int dx = x-pSprite->x, dy = y-pSprite->y, dz = z-pSprite->z;
7144          if (CheckLink(&x, &y, &z, &nSector)) // if hitscan start position is overlapping into ror sector, move sprite to ror sector
7145          {
7146              pSprite->x = x-dx;
7147              pSprite->y = y-dy;
7148              pSprite->z = z-dz;
7149          }
7150          int nSector2 = nSector;
7151          if (FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector2) && AreSectorsNeighbors(nSector, nSector2, 0, true)) // sanity check missile spawn position (likely shifted over to new sector)
7152              nSector = nSector2;
7153          pSprite->sectnum = nSector;
7154      }
7155      int hit = HitScan(pSprite, z, x-pSprite->x, y-pSprite->y, 0, CLIPMASK0, clipdist);
7156      if (ProjectilesNotBlood() && IsPlayerSprite(pSprite) && !VanillaMode() && (hit == 3) && spriRangeIsFine(gHitInfo.hitsprite) && !IsDudeSprite(&sprite[gHitInfo.hitsprite])) // if hit a non-dude sprite, check that the pixel hit is not transparent (e.g.: tree sprites in CPSL)
7157      {
7158          if (CheckHitSpriteAlpha(pSprite->x, pSprite->y, x-pSprite->x, y-pSprite->y, &gHitInfo))
7159              hit = -1;
7160      }
7161      if (hit != -1)
7162      {
7163          if (hit == 3 || hit == 0)
7164          {
7165              v4 = 1;
7166              x = gHitInfo.hitx-mulscale30(Cos(pSprite->ang), 16);
7167              y = gHitInfo.hity-mulscale30(Sin(pSprite->ang), 16);
7168          }
7169          else
7170          {
7171              x = gHitInfo.hitx-mulscale28(pMissileInfo->clipDist<<1, Cos(pSprite->ang));
7172              y = gHitInfo.hity-mulscale28(pMissileInfo->clipDist<<1, Sin(pSprite->ang));
7173          }
7174      }
7175      spritetype *pMissile = actSpawnSprite(pSprite->sectnum, x, y, z, 5, 1);
7176      if (restorePosSect)
7177      {
7178          pSprite->xyz = bakPos;
7179          pSprite->sectnum = bakSect;
7180      }
7181      int nMissile = pMissile->index;
7182      SetBitString(show2dsprite, nMissile);
7183      pMissile->type = nType;
7184      pMissile->shade = pMissileInfo->shade;
7185      pMissile->pal = 0;
7186      pMissile->clipdist = pMissileInfo->clipDist;
7187      pMissile->flags = 1;
7188      pMissile->xrepeat = pMissileInfo->xrepeat;
7189      pMissile->yrepeat = pMissileInfo->yrepeat;
7190      pMissile->picnum = pMissileInfo->picnum;
7191      pMissile->ang = (pSprite->ang+pMissileInfo->angleOfs)&2047;
7192      xvel[nMissile] = mulscale14(pMissileInfo->velocity, a4);
7193      yvel[nMissile] = mulscale14(pMissileInfo->velocity, a5);
7194      zvel[nMissile] = mulscale14(pMissileInfo->velocity, a6);
7195      actPropagateSpriteOwner(pMissile, pSprite);
7196      pMissile->cstat |= CSTAT_SPRITE_BLOCK;
7197      if ((nType == kMissileShell) || (nType == kMissileBullet)) // do not set collisions on for bullet projectiles (so bullets will not collide with each other)
7198      {
7199          if (gProjectileOldSprite)
7200          {
7201              pMissile->picnum = 9288; // use old bullet sprite from notblood.pk3/TILES099.ART
7202              pMissile->xrepeat = pMissile->yrepeat = 32;
7203          }
7204          pMissile->cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
7205          if (gGameOptions.nHitscanProjectiles == 1) // if hitscan projectile speed is 75%, adjust speed
7206          {
7207              xvel[nMissile] = (xvel[nMissile]>>1) + (xvel[nMissile]>>2);
7208              yvel[nMissile] = (yvel[nMissile]>>1) + (yvel[nMissile]>>2);
7209              zvel[nMissile] = (zvel[nMissile]>>1) + (zvel[nMissile]>>2);
7210          }
7211          else if (gGameOptions.nHitscanProjectiles == 3) // if hitscan projectile speed is 125%, adjust speed
7212          {
7213              xvel[nMissile] += (xvel[nMissile]>>2);
7214              yvel[nMissile] += (yvel[nMissile]>>2);
7215              zvel[nMissile] += (zvel[nMissile]>>2);
7216          }
7217      }
7218      int nXSprite = pMissile->extra;
7219      dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
7220      xsprite[nXSprite].target = -1;
7221      evPost(nMissile, 3, 600, kCallbackRemove);
7222     
7223      actBuildMissile(pMissile, nXSprite, nSprite);
7224      
7225      if (v4 && (nType != kMissileShell) && (nType != kMissileBullet))
7226      {
7227          actImpactMissile(pMissile, hit);
7228          pMissile = NULL;
7229      }
7230      return pMissile;
7231  }
7232  
7233  void actBuildMissile(spritetype* pMissile, int nXSprite, int nSprite) {
7234      int nMissile = pMissile->index;
7235      switch (pMissile->type) {
7236          case kMissileLifeLeechRegular:
7237              evPost(nMissile, 3, 0, kCallbackFXFlameLick);
7238              break;
7239          case kMissileTeslaAlt:
7240              evPost(nMissile, 3, 0, kCallbackFXTeslaAlt);
7241              break;
7242          case kMissilePukeGreen:
7243              seqSpawn(29, 3, nXSprite, -1);
7244              break;
7245          case kMissileButcherKnife:
7246              pMissile->cstat |= 16;
7247              break;
7248          case kMissileTeslaRegular:
7249              sfxPlay3DSound(pMissile, 251, 0, 0);
7250              break;
7251          case kMissileEctoSkull:
7252              seqSpawn(2, 3, nXSprite, -1);
7253              sfxPlay3DSound(pMissile, 493, 0, 0);
7254              break;
7255          case kMissileFireballNapalm:
7256              seqSpawn(61, 3, nXSprite, nNapalmClient);
7257              sfxPlay3DSound(pMissile, 441, 0, 0);
7258              break;
7259          case kMissileFireball:
7260              seqSpawn(22, 3, nXSprite, nFireballClient);
7261              sfxPlay3DSound(pMissile, 441, 0, 0);
7262              break;
7263          case kMissileFlameHound:
7264              seqSpawn(27, 3, nXSprite, -1);
7265              xvel[nMissile] += xvel[nSprite] / 2 + Random2(0x11111);
7266              yvel[nMissile] += yvel[nSprite] / 2 + Random2(0x11111);
7267              zvel[nMissile] += zvel[nSprite] / 2 + Random2(0x11111);
7268              break;
7269          case kMissileFireballCerberus:
7270              seqSpawn(61, 3, nXSprite, dword_2192E0);
7271              sfxPlay3DSound(pMissile, 441, 0, 0);
7272              break;
7273          case kMissileFireballTchernobog:
7274              seqSpawn(23, 3, nXSprite, dword_2192D8);
7275              xvel[nMissile] += xvel[nSprite] / 2 + Random2(0x11111);
7276              yvel[nMissile] += yvel[nSprite] / 2 + Random2(0x11111);
7277              zvel[nMissile] += zvel[nSprite] / 2 + Random2(0x11111);
7278              break;
7279          case kMissileFlameSpray:
7280              if (Chance(0x8000))
7281                  seqSpawn(0, 3, nXSprite, -1);
7282              else
7283                  seqSpawn(1, 3, nXSprite, -1);
7284              xvel[nMissile] += xvel[nSprite] + Random2(0x11111);
7285              yvel[nMissile] += yvel[nSprite] + Random2(0x11111);
7286              zvel[nMissile] += zvel[nSprite] + Random2(0x11111);
7287              break;
7288          case kMissileFlareAlt:
7289              evPost(nMissile, 3, 30, kCallbackFXFlareBurst);
7290              evPost(nMissile, 3, 0, kCallbackFXFlareSpark);
7291              sfxPlay3DSound(pMissile, 422, 0, 0);
7292              break;
7293          case kMissileFlareRegular:
7294              evPost(nMissile, 3, 0, kCallbackFXFlareSpark);
7295              sfxPlay3DSound(pMissile, 422, 0, 0);
7296              break;
7297          case kMissileLifeLeechAltSmall:
7298              evPost(nMissile, 3, 0, kCallbackFXArcSpark);
7299              break;
7300          case kMissileArcGargoyle:
7301              sfxPlay3DSound(pMissile, 252, 0, 0);
7302              break;
7303      }
7304  }
7305  
7306  int actGetRespawnTime(spritetype *pSprite) {
7307      if (pSprite->extra <= 0) return -1; 
7308      XSPRITE *pXSprite = &xsprite[pSprite->extra];
7309      if (IsDudeSprite(pSprite) && !IsPlayerSprite(pSprite)) {
7310          if (pXSprite->respawn == 2 || (pXSprite->respawn != 1 && gGameOptions.nMonsterSettings == 2))
7311              return gGameOptions.nMonsterRespawnTime;
7312          return -1;
7313      }
7314  
7315  #ifdef NOONE_EXTENSIONS
7316      if (IsUserItemSprite(pSprite))
7317          return userItemGetRespawnTime(pSprite);
7318  #endif
7319  
7320      if (IsWeaponSprite(pSprite)) {
7321          if (pXSprite->respawn == 3 || gGameOptions.nWeaponSettings == 1) return 0;
7322          else if (pXSprite->respawn != 1 && gGameOptions.nWeaponSettings != 0) 
7323              return gGameOptions.nWeaponRespawnTime;
7324          return -1;
7325      }
7326  
7327      if (IsAmmoSprite(pSprite)) {
7328          if (pXSprite->respawn == 2 || (pXSprite->respawn != 1 && gGameOptions.nWeaponSettings != 0))
7329              return gGameOptions.nWeaponRespawnTime;
7330          return -1;
7331      }
7332  
7333      if (IsItemSprite(pSprite)) {
7334          if (pXSprite->respawn == 3 && gGameOptions.nGameType == kGameTypeCoop) return 0;
7335          else if (pXSprite->respawn == 2 || (pXSprite->respawn != 1 && gGameOptions.nItemSettings != 0)) {
7336              switch (pSprite->type) {
7337                  case kItemShadowCloak:
7338                  case kItemTwoGuns:
7339                  case kItemReflectShots:
7340                      return gGameOptions.nSpecialRespawnTime;
7341                  case kItemDeathMask:
7342                      return gGameOptions.nSpecialRespawnTime<<1;
7343                  default:
7344                      return gGameOptions.nItemRespawnTime;
7345              }
7346          }
7347          return -1;
7348      }
7349      return -1;
7350  }
7351  
7352  bool actCheckRespawn(spritetype *pSprite)
7353  {
7354      int nSprite = pSprite->index;
7355      int nXSprite = pSprite->extra;
7356      if (nXSprite > 0)
7357      {
7358          XSPRITE *pXSprite = &xsprite[nXSprite];
7359          int nRespawnTime = actGetRespawnTime(pSprite);
7360          if (nRespawnTime < 0)
7361              return 0;
7362          pXSprite->respawnPending = 1;
7363          if (pSprite->type >= kThingBase && pSprite->type < kThingMax)
7364          {
7365              pXSprite->respawnPending = 3;
7366              if (pSprite->type == kThingTNTBarrel)
7367                  pSprite->cstat |= 32768;
7368          }
7369          if (nRespawnTime > 0)
7370          {
7371              if (pXSprite->respawnPending == 1)
7372                  nRespawnTime = mulscale16(nRespawnTime, 0xa000);
7373              pSprite->owner = pSprite->statnum;
7374              actPostSprite(pSprite->index, kStatRespawn);
7375              pSprite->flags |= kHitagRespawn;
7376              if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax))
7377              {
7378                  pSprite->cstat &= ~257;
7379                  pSprite->x = baseSprite[nSprite].x;
7380                  pSprite->y = baseSprite[nSprite].y;
7381                  pSprite->z = baseSprite[nSprite].z;
7382              }
7383              evPost(nSprite, 3, nRespawnTime, kCallbackRespawn);
7384          }
7385          return 1;
7386      }
7387      return  0;
7388  }
7389  
7390  bool actCanSplatWall(int nWall, int nSector, int x, int y, int z, char *nSurf)
7391  {
7392      dassert(nWall >= 0 && nWall < kMaxWalls);
7393      walltype *pWall = &wall[nWall];
7394      if (pWall->cstat & 16384)
7395          return 0;
7396      if (pWall->cstat & 32768)
7397          return 0;
7398      int nType = GetWallType(nWall);
7399      if (nType >= kWallBase && nType < kWallMax)
7400          return 0;
7401      if (pWall->nextsector != -1)
7402      {
7403          sectortype *pSector = &sector[pWall->nextsector];
7404          if (sectRangeIsFine(nSector) && gGameOptions.bSectorBehavior && !VanillaMode()) // check if wall is parallax type
7405          {
7406              char bHitSkybox = 0;
7407              if ((pSector->ceilingstat&kSecCParallax) && (sector[nSector].ceilingstat&kSecCParallax)) // if ceilings for both sectors are parallax type
7408              {
7409                  const int ceilingZ = getceilzofslope(pWall->nextsector, x, y);
7410                  bHitSkybox = ceilingZ >= z; // if hit was above connected sector's ceiling, set as hit sky tile
7411              }
7412              if (!bHitSkybox && (pSector->floorstat&kSecCParallax) && (sector[nSector].floorstat&kSecCParallax)) // if floor for both sectors are parallax type
7413              {
7414                  const int floorZ = getflorzofslope(pWall->nextsector, x, y);
7415                  bHitSkybox = floorZ <= z; // if hit was below connected sector's floor, set as hit sky tile
7416              }
7417              if (bHitSkybox)
7418              {
7419                  if (nSurf)
7420                      *nSurf = kSurfNone;
7421                  return 0;
7422              }
7423          }
7424          if (pSector->type >= kSectorBase && pSector->type < kSectorMax)
7425              return 0;
7426      }
7427      return 1;
7428  }
7429  
7430  void actFireVector(spritetype *pShooter, int a2, int a3, int a4, int a5, int a6, VECTOR_TYPE vectorType)
7431  {
7432      int nShooter = pShooter->index;
7433      dassert(vectorType >= 0 && vectorType < kVectorMax);
7434      VECTORDATA *pVectorData = &gVectorData[vectorType];
7435      int nRange = pVectorData->maxDist;
7436      int hit;
7437      vec3_t shooterPos;
7438      if (!VanillaMode())
7439      {
7440          hit = VectorScanROR(pShooter, a2, a3, a4, a5, a6, nRange, 1, &shooterPos);
7441      }
7442      else
7443      {
7444          hit = VectorScan(pShooter, a2, a3, a4, a5, a6, nRange, 1);
7445          shooterPos = pShooter->xyz;
7446      }
7447      bool returnedFire = false;
7448      if (hit == 3)
7449      {
7450          int nSprite = gHitInfo.hitsprite;
7451          dassert(nSprite >= 0 && nSprite < kMaxSprites);
7452          spritetype *pSprite = &sprite[nSprite];
7453          if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pShooter, pSprite)) return;
7454          if (IsPlayerSprite(pSprite)) {
7455              PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
7456              if (powerupCheck(pPlayer, kPwUpReflectShots))
7457              {
7458                  gHitInfo.hitsprite = nShooter;
7459                  gHitInfo.hitx = shooterPos.x;
7460                  gHitInfo.hity = shooterPos.y;
7461                  gHitInfo.hitz = shooterPos.z;
7462                  if (WeaponsNotBlood() && !VanillaMode()) // invert impulse direction
7463                      returnedFire = true;
7464              }
7465          }
7466      }
7467      int x = gHitInfo.hitx-mulscale14(a4, 16);
7468      int y = gHitInfo.hity-mulscale14(a5, 16);
7469      int z = gHitInfo.hitz-mulscale14(a6, 256);
7470      short nSector = gHitInfo.hitsect;
7471      char nSurf = kSurfNone;
7472      if (nRange == 0 || approxDist(gHitInfo.hitx-shooterPos.x, gHitInfo.hity-shooterPos.y) < nRange)
7473      {
7474          switch (hit)
7475          {
7476          case 1:
7477          {
7478              int nSector = gHitInfo.hitsect;
7479              if (sector[nSector].ceilingstat&1)
7480                  nSurf = kSurfNone;
7481              else
7482                  nSurf = surfType[sector[nSector].ceilingpicnum];
7483              break;
7484          }
7485          case 2:
7486          {
7487              int nSector = gHitInfo.hitsect;
7488              if (sector[nSector].floorstat&1)
7489                  nSurf = kSurfNone;
7490              else
7491                  nSurf = surfType[sector[nSector].floorpicnum];
7492              break;
7493          }
7494          case 0:
7495          {
7496              int nWall = gHitInfo.hitwall;
7497              dassert(nWall >= 0 && nWall < kMaxWalls);
7498              nSurf = surfType[wall[nWall].picnum];
7499              if (actCanSplatWall(nWall, nSector, gHitInfo.hitx, gHitInfo.hity, gHitInfo.hitz, &nSurf))
7500              {
7501                  int x = gHitInfo.hitx-mulscale14(a4, 16);
7502                  int y = gHitInfo.hity-mulscale14(a5, 16);
7503                  int z = gHitInfo.hitz-mulscale14(a6, 256);
7504                  int nSurf = surfType[wall[nWall].picnum];
7505                  dassert(nSurf < kSurfMax);
7506                  if (pVectorData->surfHit[nSurf].fx1 >= 0)
7507                  {
7508                      spritetype *pFX = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx1, nSector, x, y, z);
7509                      if (pFX)
7510                      {
7511                          pFX->ang = (GetWallAngle(nWall)+512)&2047;
7512                          pFX->cstat |= 16;
7513                      }
7514                  }
7515              }
7516              break;
7517          }
7518          case 4:
7519          {
7520              int nWall = gHitInfo.hitwall;
7521              dassert(nWall >= 0 && nWall < kMaxWalls);
7522              nSurf = surfType[wall[nWall].overpicnum];
7523              int nXWall = wall[nWall].extra;
7524              if (nXWall > 0)
7525              {
7526                  XWALL *pXWall = &xwall[nXWall];
7527                  if (pXWall->triggerVector)
7528                      trTriggerWall(nWall, pXWall, kCmdWallImpact, pShooter->index);
7529              }
7530              break;
7531          }
7532          case 3:
7533          {
7534              int nSprite = gHitInfo.hitsprite;
7535              nSurf = surfType[sprite[nSprite].picnum];
7536              dassert(nSprite >= 0 && nSprite < kMaxSprites);
7537              spritetype *pSprite = &sprite[nSprite];
7538              x -= mulscale14(a4, 112);
7539              y -= mulscale14(a5, 112);
7540              z -= mulscale14(a6, 112<<4);
7541              int shift = 4;
7542              int boost = 1;
7543              int boostz = 1;
7544              int invertVal = returnedFire ? -1 : 1; // if shot player with reflective sphere, invert impulse direction
7545              DAMAGE_TYPE dmgType = pVectorData->dmgType;
7546              if (vectorType == kVectorTine && !IsPlayerSprite(pSprite))
7547                  shift = 3;
7548              if (pShooter && IsPlayerSprite(pShooter) && gGameOptions.bQuadDamagePowerup && !VanillaMode())
7549              {
7550                  PLAYER *pPlayer = &gPlayer[pShooter->type - kDudePlayer1];
7551                  if (powerupCheck(pPlayer, kPwUpTwoGuns)) // if quad is active, increase pushback and do random explosive damage for hitscan weapons
7552                  {
7553                      if (a6 < 5000) // only increase velocity impulse if vector is aiming upwards
7554                      {
7555                          shift = 2;
7556                          boost = 2;
7557                          boostz = 4;
7558                      }
7559                      if ((dmgType == kDamageBullet) && !Random(10))
7560                      {
7561                          dmgType = kDamageExplode;
7562                          if (a6 < 5000)
7563                              shift = 4;
7564                      }
7565                  }
7566              }
7567              if ((vectorType == kVectorTine) && pShooter && IsPlayerSprite(pShooter) && !IsPlayerSprite(pSprite) && IsDudeSprite(pSprite) && WeaponsNotBlood() && !VanillaMode()) // apply double damage for pitchfork when attacking rear of enemy
7568              {
7569                  const int nDiff = (klabs(pShooter->ang-pSprite->ang)%2048+2048)%2048;
7570                  if (nDiff < 256) // if attacker is within 45 degrees behind victim, apply double damage
7571                      shift++;
7572              }
7573              actDamageSprite(nShooter, pSprite, dmgType, pVectorData->dmg<<shift);
7574              int nXSprite = pSprite->extra;
7575              if (nXSprite > 0)
7576              {
7577                  XSPRITE *pXSprite = &xsprite[nXSprite];
7578                  if (pXSprite->Vector)
7579                      trTriggerSprite(nSprite, pXSprite, kCmdSpriteImpact, pShooter->index);
7580              }
7581              if (pSprite->statnum == kStatThing)
7582              {
7583                  // NoOne:
7584                  // shoting in TNT makes it explode, so type changes to range of 0-8
7585                  // however statnum changes to 2 (explosion) later in actPostSprite()...
7586                  // this is why this type range check is required here
7587                  if (VanillaMode() || (pSprite->type >= kThingBase && pSprite->type < kThingMax))
7588                  {
7589                      int t = thingInfo[pSprite->type - kThingBase].mass;
7590                      if (t > 0 && pVectorData->impulse)
7591                      {
7592                          int t2 = divscale8(pVectorData->impulse, t);
7593                          xvel[nSprite] += mulscale16(a4, t2) * boost * invertVal;
7594                          yvel[nSprite] += mulscale16(a5, t2) * boost * invertVal;
7595                          zvel[nSprite] += mulscale16(a6, t2) * boostz * invertVal;
7596                      }
7597                      if (pVectorData->burnTime)
7598                      {
7599                          XSPRITE* pXSprite = &xsprite[nXSprite];
7600                          if (!pXSprite->burnTime)
7601                              evPost(nSprite, 3, 0, kCallbackFXFlameLick);
7602                          actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->burnTime);
7603                      }
7604                  }
7605              }
7606              if (pSprite->statnum == kStatDude)
7607              {
7608                  #ifdef NOONE_EXTENSIONS
7609                  int t = (IsCustomDude(pSprite)) ? cdudeGet(pSprite->index)->mass : getDudeInfo(pSprite->type)->mass;
7610                  #else
7611                  int t = getDudeInfo(pSprite->type)->mass;
7612                  #endif
7613  
7614                  if (t > 0 && pVectorData->impulse)
7615                  {
7616                      int t2 = divscale8(pVectorData->impulse, t);
7617                      int t3 = mulscale16(a6, t2);
7618                      if (EnemiesNotBlood() && !VanillaMode()) // clamp downward impulse damage to stop players from cheesing bosses
7619                          t3 = ClipHigh(t3, 32767);
7620                      xvel[nSprite] += mulscale16(a4, t2) * boost * invertVal;
7621                      yvel[nSprite] += mulscale16(a5, t2) * boost * invertVal;
7622                      zvel[nSprite] += t3 * boostz * invertVal;
7623                  }
7624                  if (pVectorData->burnTime)
7625                  {
7626                      XSPRITE *pXSprite = &xsprite[nXSprite];
7627                      if (!pXSprite->burnTime)
7628                          evPost(nSprite, 3, 0, kCallbackFXFlameLick);
7629                      actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->burnTime);
7630                  }
7631                  if (Chance(pVectorData->fxChance))
7632                  {
7633                      int t = gVectorData[19].maxDist;
7634                      a4 += Random3(4000);
7635                      a5 += Random3(4000);
7636                      a6 += Random3(4000);
7637                      if (HitScan(pSprite, gHitInfo.hitz, a4, a5, a6, CLIPMASK1, t) == 0)
7638                      {
7639                          if (approxDist(gHitInfo.hitx-pSprite->x, gHitInfo.hity-pSprite->y) <= t)
7640                          {
7641                              int nWall = gHitInfo.hitwall;
7642                              int nSector = gHitInfo.hitsect;
7643                              if (actCanSplatWall(nWall, nSector, gHitInfo.hitx, gHitInfo.hity, gHitInfo.hitz, &nSurf))
7644                              {
7645                                  int x = gHitInfo.hitx - mulscale14(a4, 16);
7646                                  int y = gHitInfo.hity - mulscale14(a5, 16);
7647                                  int z = gHitInfo.hitz - mulscale14(a6, 16<<4);
7648                                  int nSurf = surfType[wall[nWall].picnum];
7649                                  VECTORDATA *pVectorData = &gVectorData[19];
7650                                  FX_ID t2 = pVectorData->surfHit[nSurf].fx2;
7651                                  FX_ID t3 = pVectorData->surfHit[nSurf].fx3;
7652                                  spritetype *pFX = NULL;
7653                                  const char bSpawnBlood = ((gGameOptions.nGoreBehavior > 1) && !VanillaMode()) || (t3 == FX_NONE || Chance(0x4000));
7654                                  if (t2 > FX_NONE && bSpawnBlood)
7655                                      pFX = gFX.fxSpawn(t2, nSector, x, y, z);
7656                                  else if(t3 > FX_NONE)
7657                                      pFX = gFX.fxSpawn(t3, nSector, x, y, z);
7658                                  if (pFX)
7659                                  {
7660                                      zvel[pFX->index] = 0x2222;
7661                                      pFX->ang = (GetWallAngle(nWall)+512)&2047;
7662                                      pFX->cstat |= 16;
7663                                  }
7664                              }
7665                          }
7666                      }
7667                  }
7668                  if ((gGameOptions.nGoreBehavior > 1) && !VanillaMode()) // splatIncrement for default
7669                  {
7670                      switch (pSprite->type)
7671                      {
7672                      case kDudePhantasm:
7673                      case kDudeHand:
7674                      case kDudeSpiderBrown:
7675                      case kDudeSpiderRed:
7676                      case kDudeBoneEel:
7677                      case kDudeBat:
7678                      case kDudeRat:
7679                      case kDudeTentacleGreen:
7680                      case kDudeTentacleFire:
7681                      case kDudeBurningTinyCaleb:
7682                          for (int i = 0; i < pVectorData->bloodSplats; i++)
7683                              if (Chance(pVectorData->splatChance))
7684                                  fxSpawnBlood(pSprite, pVectorData->dmg<<4);
7685                          break;
7686                      default:
7687                      {
7688                          const int kMaxSplatIncrementBase = 5; // base for random splat increment
7689                          const int kShotgunSplatChance = 0x4000; // 25% chance for shotgun-like effects
7690                          const int kTommySplatChance = 0x4000; // 25% chance for tommy-like effects
7691  
7692                          int nSplatIncrement = kMaxSplatIncrementBase - gGameOptions.nDifficulty;
7693                          nSplatIncrement >>= 1;
7694                          const int nBloodSplats = pVectorData->bloodSplats + nSplatIncrement;
7695                          int nType = pShooter->type;
7696                          if (IsPlayerSprite(pShooter))
7697                          {
7698                              PLAYER* pPlayer = &gPlayer[nType - kDudePlayer1];
7699                              if (pPlayer->curWeapon == kWeaponShotgun)
7700                                  nType = kDudeCultistShotgun;
7701                              else if (pPlayer->curWeapon == kWeaponTommy)
7702                                  nType = kDudeCultistTommy;
7703                          }
7704  
7705                          for (int j = 0; j < nBloodSplats; j++)
7706                          {
7707                              switch (nType)
7708                              {
7709                              case kDudeCultistShotgun:
7710                              case kDudeCultistShotgunProne:
7711                                  if (Chance(kShotgunSplatChance)) // apply a chance to limit blood splats when using shotgun
7712                                      fxSpawnBlood(pSprite, pVectorData->dmg<<4);
7713                                  break;
7714                              case kDudeCultistTommy:
7715                              case kDudeCultistTommyProne:
7716                                  if (Chance(kTommySplatChance)) // apply a chance to limit blood splats when using tommy
7717                                      fxSpawnBlood(pSprite, pVectorData->dmg<<4);
7718                                  break;
7719                              default:
7720                                  fxSpawnBlood(pSprite, pVectorData->dmg<<4);
7721                                  break;
7722                              }
7723                          }
7724                          break;
7725                      }
7726                      }
7727                  }
7728                  else
7729                  {
7730                      for (int i = 0; i < pVectorData->bloodSplats; i++)
7731                          if (Chance(pVectorData->splatChance))
7732                              fxSpawnBlood(pSprite, pVectorData->dmg<<4);
7733                  }
7734              }
7735              #ifdef NOONE_EXTENSIONS
7736              // add impulse for sprites from physics list
7737              if (gPhysSpritesList.Length() && pVectorData->impulse && xspriRangeIsFine(pSprite->extra))
7738              {
7739                  XSPRITE* pXSprite = &xsprite[pSprite->extra];
7740                  if (pXSprite->physAttr & kPhysDebrisVector)
7741                  {
7742                      int impulse = divscale6(pVectorData->impulse, ClipLow(gSpriteMass[pSprite->extra].mass, 10));
7743                      xvel[nSprite] += mulscale16(a4, impulse) * boost * invertVal;
7744                      yvel[nSprite] += mulscale16(a5, impulse) * boost * invertVal;
7745                      zvel[nSprite] += mulscale16(a6, impulse) * boostz * invertVal;
7746  
7747                      if (pVectorData->burnTime != 0)
7748                      {
7749                          if (!pXSprite->burnTime)
7750                              evPost(nSprite, 3, 0, kCallbackFXFlameLick);
7751  
7752                          actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->burnTime);
7753                      }
7754  
7755                      if (IsThingSprite(pSprite))
7756                      {
7757                          pSprite->statnum = kStatThing; // temporary change statnum property
7758                          actDamageSprite(nShooter, pSprite, pVectorData->dmgType, pVectorData->dmg << 4);
7759                          pSprite->statnum = kStatDecoration; // return statnum property back
7760                      }
7761                  }
7762              }
7763              #endif
7764              break;
7765          }
7766          }
7767      }
7768      dassert(nSurf < kSurfMax);
7769  #ifdef NOONE_EXTENSIONS
7770      
7771      // let the patrol enemies hear surface hit sounds!
7772      
7773      if (pVectorData->surfHit[nSurf].fx2 >= 0) {
7774          
7775          spritetype* pFX2 = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx2, nSector, x, y, z);
7776          if (pFX2 && gModernMap)
7777              actPropagateSpriteOwner(pFX2, pShooter);
7778      }
7779      
7780      if (pVectorData->surfHit[nSurf].fx3 >= 0) {
7781          
7782          spritetype* pFX3 = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx3, nSector, x, y, z);
7783          if (pFX3 && gModernMap)
7784              actPropagateSpriteOwner(pFX3, pShooter);
7785  
7786      }
7787  
7788  #else
7789      if (pVectorData->surfHit[nSurf].fx2 >= 0)
7790          gFX.fxSpawn(pVectorData->surfHit[nSurf].fx2, nSector, x, y, z);
7791      if (pVectorData->surfHit[nSurf].fx3 >= 0)
7792          gFX.fxSpawn(pVectorData->surfHit[nSurf].fx3, nSector, x, y, z);
7793  #endif
7794  
7795      if (pVectorData->surfHit[nSurf].fxSnd >= 0)
7796          sfxPlay3DSound(x, y, z, pVectorData->surfHit[nSurf].fxSnd, nSector);
7797  }
7798  
7799  void FireballSeqCallback(int, int nXSprite)
7800  {
7801      XSPRITE *pXSprite = &xsprite[nXSprite];
7802      int nSprite = pXSprite->reference;
7803      spritetype *pSprite = &sprite[nSprite];
7804      spritetype *pFX = gFX.fxSpawn(FX_11, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
7805      if (pFX)
7806      {
7807          int nFX = pFX->index;
7808          xvel[nFX] = xvel[nSprite];
7809          yvel[nFX] = yvel[nSprite];
7810          zvel[nFX] = zvel[nSprite];
7811      }
7812  }
7813  
7814  void NapalmSeqCallback(int, int nXSprite)
7815  {
7816      XSPRITE *pXSprite = &xsprite[nXSprite];
7817      int nSprite = pXSprite->reference;
7818      spritetype *pSprite = &sprite[nSprite];
7819      spritetype *pFX = gFX.fxSpawn(FX_12, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
7820      if (pFX)
7821      {
7822          int nFX = pFX->index;
7823          xvel[nFX] = xvel[nSprite];
7824          yvel[nFX] = yvel[nSprite];
7825          zvel[nFX] = zvel[nSprite];
7826      }
7827  }
7828  
7829  void sub_3888C(int, int nXSprite)
7830  {
7831      XSPRITE *pXSprite = &xsprite[nXSprite];
7832      int nSprite = pXSprite->reference;
7833      spritetype *pSprite = &sprite[nSprite];
7834      spritetype *pFX = gFX.fxSpawn(FX_32, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
7835      if (pFX)
7836      {
7837          int nFX = pFX->index;
7838          xvel[nFX] = xvel[nSprite];
7839          yvel[nFX] = yvel[nSprite];
7840          zvel[nFX] = zvel[nSprite];
7841      }
7842  }
7843  
7844  void sub_38938(int, int nXSprite)
7845  {
7846      XSPRITE *pXSprite = &xsprite[nXSprite];
7847      int nSprite = pXSprite->reference;
7848      spritetype *pSprite = &sprite[nSprite];
7849      spritetype *pFX = gFX.fxSpawn(FX_33, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
7850      if (pFX)
7851      {
7852          int nFX = pFX->index;
7853          xvel[nFX] = xvel[nSprite];
7854          yvel[nFX] = yvel[nSprite];
7855          zvel[nFX] = zvel[nSprite];
7856      }
7857  }
7858  
7859  void TreeToGibCallback(int, int nXSprite)
7860  {
7861      XSPRITE *pXSprite = &xsprite[nXSprite];
7862      int nSprite = pXSprite->reference;
7863      spritetype *pSprite = &sprite[nSprite];
7864      pSprite->type = kThingObjectExplode;
7865      pXSprite->state = 1;
7866      pXSprite->data1 = 15;
7867      pXSprite->data2 = 0;
7868      pXSprite->data3 = 0;
7869      pXSprite->health = thingInfo[17].startHealth;
7870      pXSprite->data4 = 312;
7871      pSprite->cstat |= 257;
7872  }
7873  
7874  void DudeToGibCallback1(int, int nXSprite)
7875  {
7876      XSPRITE *pXSprite = &xsprite[nXSprite];
7877      int nSprite = pXSprite->reference;
7878      spritetype *pSprite = &sprite[nSprite];
7879      pSprite->type = kThingBloodChunks;
7880      pXSprite->data1 = 8;
7881      pXSprite->data2 = 0;
7882      pXSprite->data3 = 0;
7883      pXSprite->health = thingInfo[26].startHealth;
7884      pXSprite->data4 = 319;
7885      pXSprite->triggerOnce = 0;
7886      pXSprite->isTriggered = 0;
7887      pXSprite->locked = 0;
7888      pXSprite->targetX = (int)gFrameClock;
7889      pXSprite->state = 1;
7890  }
7891  
7892  void DudeToGibCallback2(int, int nXSprite)
7893  {
7894      XSPRITE *pXSprite = &xsprite[nXSprite];
7895      int nSprite = pXSprite->reference;
7896      spritetype *pSprite = &sprite[nSprite];
7897      pSprite->type = kThingBloodChunks;
7898      pXSprite->data1 = 3;
7899      pXSprite->data2 = 0;
7900      pXSprite->data3 = 0;
7901      pXSprite->health = thingInfo[26].startHealth;
7902      pXSprite->data4 = 319;
7903      pXSprite->triggerOnce = 0;
7904      pXSprite->isTriggered = 0;
7905      pXSprite->locked = 0;
7906      pXSprite->targetX = (int)gFrameClock;
7907      pXSprite->state = 1;
7908  }
7909  
7910  void actPostSprite(int nSprite, int nStatus)
7911  {
7912      int n;
7913      dassert(gPostCount < kMaxSprites);
7914      dassert(nSprite < kMaxSprites && sprite[nSprite].statnum < kMaxStatus);
7915      dassert(nStatus >= 0 && nStatus <= kStatFree);
7916      if (sprite[nSprite].flags&32)
7917      {
7918          for (n = 0; n < gPostCount; n++)
7919              if (gPost[n].nSprite == nSprite)
7920                  break;
7921          dassert(n < gPostCount);
7922      }
7923      else
7924      {
7925          n = gPostCount;
7926          sprite[nSprite].flags |= 32;
7927          gPostCount++;
7928      }
7929      gPost[n].nSprite = nSprite;
7930      gPost[n].nStatus = nStatus;
7931  }
7932  
7933  void actPostProcess(void)
7934  {
7935      for (int i = 0; i < gPostCount; i++)
7936      {
7937          POSTPONE *pPost = &gPost[i];
7938          int nSprite = pPost->nSprite;
7939          spritetype *pSprite = &sprite[nSprite];
7940          pSprite->flags &= ~32;
7941          int nStatus = pPost->nStatus;
7942          if (nStatus == kStatFree)
7943          {
7944              evKill(nSprite, 3);
7945              if (sprite[nSprite].extra > 0)
7946                  seqKill(3, sprite[nSprite].extra);
7947              DeleteSprite(nSprite);
7948          }
7949          else
7950              ChangeSpriteStat(nSprite, nStatus);
7951      }
7952      gPostCount = 0;
7953  }
7954  
7955  void MakeSplash(spritetype *pSprite, XSPRITE *pXSprite)
7956  {
7957      //UNREFERENCED_PARAMETER(pXSprite);
7958      pSprite->flags &= ~2;
7959      int nXSprite = pSprite->extra;
7960      pSprite->z -= 4 << 8;
7961      int nSurface = tileGetSurfType(gSpriteHit[nXSprite].florhit);
7962   
7963  #ifdef NOONE_EXTENSIONS
7964      if (gModernMap)
7965      {
7966          XSPRITE* pXOwner = pXSprite; // so data2 info will be taken from current xsprite
7967          if (spriRangeIsFine(pSprite->owner))
7968          {
7969              spritetype* pOwner = &sprite[pSprite->owner];
7970              if (xspriRangeIsFine(pOwner->extra))
7971                  pXOwner = &xsprite[pOwner->extra]; // or from generator sprite
7972          }
7973  
7974          switch (pSprite->type) {
7975          case kThingDripWater:
7976              switch (nSurface) {
7977                  case kSurfWater:
7978                      seqSpawn(6, 3, nXSprite, -1);
7979                      if (pXOwner->data2 >= 0)
7980                          sfxPlay3DSound(pSprite, (!pXOwner->data2) ? 356 : pXOwner->data2, -1, 0);
7981                      return;
7982                  default:
7983                      seqSpawn(7, 3, nXSprite, -1);
7984                      if (pXOwner->data2 >= 0)
7985                          sfxPlay3DSound(pSprite, (!pXOwner->data2) ? 354 : pXOwner->data2, -1, 0);
7986                      return;
7987              }
7988              return;
7989          case kThingDripBlood:
7990              seqSpawn(8, 3, nXSprite, -1);
7991              if (pXOwner->data2 >= 0)
7992                  sfxPlay3DSound(pSprite, (!pXOwner->data2) ? 354 : pXOwner->data2, -1, 0);
7993              return;
7994          }
7995      }
7996  #endif
7997  
7998      switch (pSprite->type) {
7999          case kThingDripWater:
8000              switch (nSurface) {
8001                  case kSurfWater:
8002                      seqSpawn(6, 3, nXSprite, -1);
8003                      sfxPlay3DSound(pSprite, 356, -1, 0);
8004                      break;
8005                  default:
8006                      seqSpawn(7, 3, nXSprite, -1);
8007                      sfxPlay3DSound(pSprite, 354, -1, 0);
8008                      break;
8009              }
8010              break;
8011          case kThingDripBlood:
8012              seqSpawn(8, 3, nXSprite, -1);
8013              sfxPlay3DSound(pSprite, 354, -1, 0);
8014              break;
8015      }
8016  }
8017  
8018  class ActorLoadSave : public LoadSave
8019  {
8020      virtual void Load(void);
8021      virtual void Save(void);
8022  };
8023  
8024  void ActorLoadSave::Load(void)
8025  {
8026      Read(gSpriteHit, sizeof(gSpriteHit));
8027      Read(gAffectedSectors, sizeof(gAffectedSectors));
8028      Read(gAffectedXWalls, sizeof(gAffectedXWalls));
8029      Read(&gPostCount, sizeof(gPostCount));
8030      Read(gPost, sizeof(gPost));
8031      actInit(true);
8032  }
8033  
8034  void ActorLoadSave::Save(void)
8035  {
8036      Write(gSpriteHit, sizeof(gSpriteHit));
8037      Write(gAffectedSectors, sizeof(gAffectedSectors));
8038      Write(gAffectedXWalls, sizeof(gAffectedXWalls));
8039      Write(&gPostCount, sizeof(gPostCount));
8040      Write(gPost, sizeof(gPost));
8041  }
8042  
8043  static ActorLoadSave *myLoadSave;
8044  
8045  void ActorLoadSaveConstruct(void)
8046  {
8047      myLoadSave = new ActorLoadSave();
8048  }
8049  
8050  #ifdef POLYMER
8051  
8052  // this is the same crap as in game.c's tspr manipulation.  puke.
8053  // XXX: may access tilesizy out-of-bounds by bad user code.
8054  #define LIGHTRAD(spriteNum, s) (s->yrepeat * tilesiz[s->picnum].y)
8055  #define LIGHTRAD2(spriteNum, s) ((s->yrepeat + ((rand() % s->yrepeat)>>2)) * tilesiz[s->picnum].y)
8056  
8057  void G_AddGameLight(int lightRadius, int spriteNum, int zOffset, int lightRange, int lightColor, int lightPrio)
8058  {
8059      auto const s = &sprite[spriteNum];
8060  
8061      if (videoGetRenderMode() != REND_POLYMER || pr_lighting != 1)
8062          return;
8063  
8064      if (gPolymerLight[spriteNum].lightptr == NULL)
8065      {
8066  #pragma pack(push, 1)
8067          _prlight mylight;
8068  #pragma pack(pop)
8069          Bmemset(&mylight, 0, sizeof(mylight));
8070  
8071          mylight.sector = s->sectnum;
8072          mylight.x = s->x;
8073          mylight.y = s->y;
8074          mylight.z = s->z - zOffset;
8075          mylight.color[0] = lightColor & 255;
8076          mylight.color[1] = (lightColor >> 8) & 255;
8077          mylight.color[2] = (lightColor >> 16) & 255;
8078          mylight.radius = lightRadius;
8079          gPolymerLight[spriteNum].lightmaxrange = mylight.range = lightRange;
8080  
8081          mylight.priority = lightPrio;
8082          mylight.tilenum = 0;
8083  
8084          mylight.publicflags.emitshadow = 1;
8085          mylight.publicflags.negative = 0;
8086  
8087          gPolymerLight[spriteNum].lightId = polymer_addlight(&mylight);
8088          if (gPolymerLight[spriteNum].lightId >= 0)
8089              gPolymerLight[spriteNum].lightptr = &prlights[gPolymerLight[spriteNum].lightId];
8090          return;
8091      }
8092  
8093      s->z -= zOffset;
8094  
8095      if (lightRange<gPolymerLight[spriteNum].lightmaxrange>> 1)
8096          gPolymerLight[spriteNum].lightmaxrange = 0;
8097  
8098      if (lightRange > gPolymerLight[spriteNum].lightmaxrange || lightPrio != gPolymerLight[spriteNum].lightptr->priority ||
8099          Bmemcmp(&sprite[spriteNum], gPolymerLight[spriteNum].lightptr, sizeof(int32_t) * 3))
8100      {
8101          if (lightRange > gPolymerLight[spriteNum].lightmaxrange)
8102              gPolymerLight[spriteNum].lightmaxrange = lightRange;
8103  
8104          Bmemcpy(gPolymerLight[spriteNum].lightptr, &sprite[spriteNum], sizeof(int32_t) * 3);
8105          gPolymerLight[spriteNum].lightptr->sector = s->sectnum;
8106          gPolymerLight[spriteNum].lightptr->flags.invalidate = 1;
8107      }
8108  
8109      gPolymerLight[spriteNum].lightptr->priority = lightPrio;
8110      gPolymerLight[spriteNum].lightptr->range = lightRange;
8111      gPolymerLight[spriteNum].lightptr->color[0] = lightColor & 255;
8112      gPolymerLight[spriteNum].lightptr->color[1] = (lightColor >> 8) & 255;
8113      gPolymerLight[spriteNum].lightptr->color[2] = (lightColor >> 16) & 255;
8114  
8115      s->z += zOffset;
8116  }
8117  
8118  void actDoLight(int nSprite)
8119  {
8120      auto const pSprite = &sprite[nSprite];
8121      //int savedFires = 0;
8122      if (((sector[pSprite->sectnum].floorz - sector[pSprite->sectnum].ceilingz) < 16) || pSprite->z > sector[pSprite->sectnum].floorz)
8123      {
8124          if (gPolymerLight[nSprite].lightptr != NULL)
8125              DeleteLight(nSprite);
8126      }
8127      else
8128      {
8129          if (gPolymerLight[nSprite].lightptr != NULL && gPolymerLight[nSprite].lightcount)
8130          {
8131              if (!(--gPolymerLight[nSprite].lightcount))
8132                  DeleteLight(nSprite);
8133          }
8134  
8135          if (pr_lighting != 1)
8136              return;
8137  
8138          switch (pSprite->statnum)
8139          {
8140          case kStatProjectile:
8141              switch (pSprite->type)
8142              {
8143              case kMissileTeslaRegular:
8144                  {
8145                      int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8146                      int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8147  
8148                      pSprite->x -= x;
8149                      pSprite->y -= y;
8150  
8151                      G_AddGameLight(0, nSprite, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 2048, 80+(252<<8)+(120<<16),PR_LIGHT_PRIO_HIGH_GAME);
8152  
8153                      pSprite->x += x;
8154                      pSprite->y += y;
8155                  }
8156                  break;
8157              }
8158              break;
8159          case kStatExplosion:
8160              switch (pSprite->type)
8161              {
8162              default:
8163                  if (!gPolymerLight[nSprite].lightcount)
8164                  {
8165                      // XXX: This block gets CODEDUP'd too much.
8166                      int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8167                      int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8168  
8169                      pSprite->x -= x;
8170                      pSprite->y -= y;
8171  
8172                      G_AddGameLight(0, nSprite, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite), 240+(160<<8)+(80<<16),
8173                          pSprite->yrepeat > 32 ? PR_LIGHT_PRIO_HIGH_GAME : PR_LIGHT_PRIO_LOW_GAME);
8174  
8175                      pSprite->x += x;
8176                      pSprite->y += y;
8177                  }
8178                  break;
8179              }
8180              break;
8181          }
8182      }
8183  #if 0
8184      if (((sector[pSprite->sectnum].floorz - sector[pSprite->sectnum].ceilingz) < 16) || pSprite->z > sector[pSprite->sectnum].floorz || pSprite->z > actor[spriteNum].floorz ||
8185          (pSprite->picnum != SECTOREFFECTOR && ((pSprite->cstat & 32768) || pSprite->yrepeat < 4)) ||
8186          A_CheckSpriteFlags(spriteNum, SFLAG_NOLIGHT) || (A_CheckSpriteFlags(spriteNum, SFLAG_USEACTIVATOR) && sector[pSprite->sectnum].lotag & 16384))
8187      {
8188          if (actor[spriteNum].lightptr != NULL)
8189              A_DeleteLight(spriteNum);
8190      }
8191      else
8192      {
8193          if (actor[spriteNum].lightptr != NULL && actor[spriteNum].lightcount)
8194          {
8195              if (!(--actor[spriteNum].lightcount))
8196                  A_DeleteLight(spriteNum);
8197          }
8198  
8199          if (pr_lighting != 1)
8200              return;
8201  
8202  #ifndef EDUKE32_STANDALONE
8203          for (int ii=0; ii<2; ii++)
8204          {
8205              if (pSprite->picnum <= 0)  // oob safety
8206                  break;
8207  
8208              switch (DYNAMICTILEMAP(pSprite->picnum-1+ii))
8209              {
8210              case DIPSWITCH__STATIC:
8211              case DIPSWITCH2__STATIC:
8212              case DIPSWITCH3__STATIC:
8213              case PULLSWITCH__STATIC:
8214              case SLOTDOOR__STATIC:
8215              case LIGHTSWITCH__STATIC:
8216              case SPACELIGHTSWITCH__STATIC:
8217              case SPACEDOORSWITCH__STATIC:
8218              case FRANKENSTINESWITCH__STATIC:
8219              case POWERSWITCH1__STATIC:
8220              case LOCKSWITCH1__STATIC:
8221              case POWERSWITCH2__STATIC:
8222              case TECHSWITCH__STATIC:
8223              case ACCESSSWITCH__STATIC:
8224              case ACCESSSWITCH2__STATIC:
8225                  {
8226                      if ((pSprite->cstat & 32768) || A_CheckSpriteFlags(spriteNum, SFLAG_NOLIGHT))
8227                      {
8228                          if (actor[spriteNum].lightptr != NULL)
8229                              A_DeleteLight(spriteNum);
8230                          break;
8231                      }
8232  
8233                      vec2_t const d = { sintable[(pSprite->ang+512)&2047]>>7, sintable[(pSprite->ang)&2047]>>7 };
8234  
8235                      pSprite->x += d.x;
8236                      pSprite->y += d.y;
8237  
8238                      int16_t sectnum = pSprite->sectnum;
8239                      updatesector(pSprite->x, pSprite->y, &sectnum);
8240  
8241                      if ((unsigned) sectnum >= MAXSECTORS || pSprite->z > sector[sectnum].floorz || pSprite->z < sector[sectnum].ceilingz)
8242                          goto POOP;
8243  
8244                      G_AddGameLight(0, spriteNum, (pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1, 512-ii*128,
8245                          ii==0 ? (172+(200<<8)+(104<<16)) : 216+(52<<8)+(20<<16), PR_LIGHT_PRIO_LOW);
8246  
8247                  POOP:
8248                      pSprite->x -= d.x;
8249                      pSprite->y -= d.y;
8250                  }
8251                  break;
8252              }
8253          }
8254  
8255          switch (DYNAMICTILEMAP(pSprite->picnum))
8256          {
8257          case ATOMICHEALTH__STATIC:
8258              G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD2(spriteNum, pSprite), 128+(128<<8)+(255<<16),PR_LIGHT_PRIO_HIGH_GAME);
8259              break;
8260  
8261          case FIRE__STATIC:
8262          case FIRE2__STATIC:
8263          case BURNING__STATIC:
8264          case BURNING2__STATIC:
8265              {
8266                  uint32_t color;
8267                  int32_t jj;
8268  
8269                  static int32_t savedfires[32][4];  // sectnum x y z
8270  
8271                  /*
8272                  if (Actor[i].floorz - Actor[i].ceilingz < 128) break;
8273                  if (s->z > Actor[i].floorz+2048) break;
8274                  */
8275  
8276                  switch (pSprite->pal)
8277                  {
8278                  case 1: color = 128+(128<<8)+(255<<16); break;
8279                  case 2: color = 255+(48<<8)+(48<<16); break;
8280                  case 8: color = 48+(255<<8)+(48<<16); break;
8281                  default: color = 240+(160<<8)+(80<<16); break;
8282                  }
8283  
8284                  for (jj=savedFires-1; jj>=0; jj--)
8285                      if (savedfires[jj][0]==pSprite->sectnum && savedfires[jj][1]==(pSprite->x>>3) &&
8286                          savedfires[jj][2]==(pSprite->y>>3) && savedfires[jj][3]==(pSprite->z>>7))
8287                          break;
8288  
8289                  if (jj==-1 && savedFires<32)
8290                  {
8291                      jj = savedFires;
8292                      G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD2(spriteNum, pSprite), color, PR_LIGHT_PRIO_HIGH_GAME);
8293                      savedfires[jj][0] = pSprite->sectnum;
8294                      savedfires[jj][1] = pSprite->x>>3;
8295                      savedfires[jj][2] = pSprite->y>>3;
8296                      savedfires[jj][3] = pSprite->z>>7;
8297                      savedFires++;
8298                  }
8299              }
8300              break;
8301  
8302          case OOZFILTER__STATIC:
8303              if (pSprite->xrepeat > 4)
8304                  G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 4096, 176+(252<<8)+(120<<16),PR_LIGHT_PRIO_HIGH_GAME);
8305              break;
8306          case FLOORFLAME__STATIC:
8307          case FIREBARREL__STATIC:
8308          case FIREVASE__STATIC:
8309              G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<2), LIGHTRAD2(spriteNum, pSprite)>>1, 255+(95<<8),PR_LIGHT_PRIO_HIGH_GAME);
8310              break;
8311  
8312          case EXPLOSION2__STATIC:
8313              if (!actor[spriteNum].lightcount)
8314              {
8315                  // XXX: This block gets CODEDUP'd too much.
8316                  int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8317                  int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8318  
8319                  pSprite->x -= x;
8320                  pSprite->y -= y;
8321  
8322                  G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite), 240+(160<<8)+(80<<16),
8323                      pSprite->yrepeat > 32 ? PR_LIGHT_PRIO_HIGH_GAME : PR_LIGHT_PRIO_LOW_GAME);
8324  
8325                  pSprite->x += x;
8326                  pSprite->y += y;
8327              }
8328              break;
8329          case FORCERIPPLE__STATIC:
8330          case TRANSPORTERBEAM__STATIC:
8331              G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite), 80+(80<<8)+(255<<16),PR_LIGHT_PRIO_LOW_GAME);
8332              break;
8333          case GROWSPARK__STATIC:
8334              {
8335                  int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8336                  int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8337  
8338                  pSprite->x -= x;
8339                  pSprite->y -= y;
8340  
8341                  G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 1024, 216+(52<<8)+(20<<16),PR_LIGHT_PRIO_HIGH_GAME);
8342  
8343                  pSprite->x += x;
8344                  pSprite->y += y;
8345              }
8346              break;
8347          case SHRINKEREXPLOSION__STATIC:
8348              {
8349                  int32_t x = ((sintable[(pSprite->ang+512)&2047])>>6);
8350                  int32_t y = ((sintable[(pSprite->ang)&2047])>>6);
8351  
8352                  pSprite->x -= x;
8353                  pSprite->y -= y;
8354  
8355                  G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 2048, 176+(252<<8)+(120<<16),PR_LIGHT_PRIO_HIGH_GAME);
8356  
8357                  pSprite->x += x;
8358                  pSprite->y += y;
8359              }
8360              break;
8361          case FREEZEBLAST__STATIC:
8362              G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite)<<2, 72+(88<<8)+(140<<16),PR_LIGHT_PRIO_HIGH_GAME);
8363              break;
8364          case COOLEXPLOSION1__STATIC:
8365              G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite)<<2, 128+(0<<8)+(255<<16),PR_LIGHT_PRIO_HIGH_GAME);
8366              break;
8367          case SHRINKSPARK__STATIC:
8368              G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), LIGHTRAD(spriteNum, pSprite), 176+(252<<8)+(120<<16),PR_LIGHT_PRIO_HIGH_GAME);
8369              break;
8370          case FIRELASER__STATIC:
8371              if (pSprite->statnum == STAT_PROJECTILE)
8372                  G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 64 * pSprite->yrepeat, 255+(95<<8),PR_LIGHT_PRIO_LOW_GAME);
8373              break;
8374          case RPG__STATIC:
8375              G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 128 * pSprite->yrepeat, 255+(95<<8),PR_LIGHT_PRIO_LOW_GAME);
8376              break;
8377          case SHOTSPARK1__STATIC:
8378              if (actor[spriteNum].t_data[2] == 0) // check for first frame of action
8379              {
8380                  int32_t x = ((sintable[(pSprite->ang+512)&2047])>>7);
8381                  int32_t y = ((sintable[(pSprite->ang)&2047])>>7);
8382  
8383                  pSprite->x -= x;
8384                  pSprite->y -= y;
8385  
8386                  G_AddGameLight(0, spriteNum, ((pSprite->yrepeat*tilesiz[pSprite->picnum].y)<<1), 8 * pSprite->yrepeat, 240+(160<<8)+(80<<16),PR_LIGHT_PRIO_LOW_GAME);
8387                  actor[spriteNum].lightcount = 1;
8388  
8389                  pSprite->x += x;
8390                  pSprite->y += y;
8391              }
8392              break;
8393          }
8394  #endif
8395      }
8396  #endif
8397  }
8398  #endif // POLYMER