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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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, §num); 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