//------------------------------------------------------------------------- /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 Nuke.YKT This file is part of NBlood. NBlood is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include #include #include "build.h" #include "pragmas.h" #include "mmulti.h" #include "common.h" #include "common_game.h" #include "actor.h" #include "ai.h" #include "aibat.h" #include "aibeast.h" #include "aiboneel.h" #include "aiburn.h" #include "aicaleb.h" #include "aicerber.h" #include "aicult.h" #include "aigarg.h" #include "aighost.h" #include "aigilbst.h" #include "aihand.h" #include "aihound.h" #include "aiinnoc.h" #include "aipod.h" #include "airat.h" #include "aispid.h" #include "aitchern.h" #include "aizomba.h" #include "aizombf.h" #include "aiunicult.h" #include "blood.h" #include "callback.h" #include "config.h" #include "db.h" #include "endgame.h" #include "eventq.h" #include "fx.h" #include "gameutil.h" #include "gib.h" #include "globals.h" #include "levels.h" #include "loadsave.h" #include "player.h" #include "seq.h" #include "sfx.h" #include "sound.h" #include "tile.h" #include "trig.h" #include "triggers.h" #include "view.h" #include "warp.h" #include "weapon.h" VECTORDATA gVectorData[] = { // Tine { DAMAGE_TYPE_2, 17, 174762, 1152, 10240, 0, 1, 20480, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_43, FX_6, FX_NONE, 502, FX_43, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_7, 502, FX_43, FX_6, FX_7, 502, FX_NONE, FX_NONE, FX_NONE, 503, FX_43, FX_NONE, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 503, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 1207, 1207, }, // Shell { DAMAGE_TYPE_2, 4, 65536, 0, 8192, 0, 1, 12288, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_5, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 501, FX_43, FX_6, FX_NONE, -1, FX_43, FX_0, FX_NONE, -1, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_43, FX_6, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_NONE, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 1001, 1001, }, // Bullet { DAMAGE_TYPE_2, 7, 21845, 0, 32768, 0, 1, 12288, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_5, FX_7, 510, FX_NONE, FX_5, FX_7, 511, FX_43, FX_6, FX_NONE, 512, FX_43, FX_0, FX_NONE, 513, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_7, 512, FX_43, FX_6, FX_7, 512, FX_NONE, FX_NONE, FX_NONE, 513, FX_43, FX_NONE, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 4001, 4002, }, // Tommy AP { DAMAGE_TYPE_2, 20, 65536, 0, 16384, 0, 1, 20480, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_5, FX_7, 510, FX_NONE, FX_5, FX_7, 511, FX_43, FX_6, FX_NONE, 512, FX_43, FX_0, FX_NONE, 513, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_7, 512, FX_43, FX_6, FX_7, 512, FX_NONE, FX_NONE, FX_NONE, 513, FX_43, FX_NONE, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 431, 431 }, // Shell AP { DAMAGE_TYPE_2, 6, 87381, 0, 12288, 0, 1, 6144, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_5, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 501, FX_43, FX_6, FX_NONE, -1, FX_43, FX_0, FX_NONE, -1, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_43, FX_6, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_NONE, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 1002, 1002, }, // Tommy regular { DAMAGE_TYPE_2, 12, 65536, 0, 16384, 0, 1, 12288, FX_NONE, FX_NONE, FX_NONE, -1, FX_43, FX_5, FX_7, 510, FX_NONE, FX_5, FX_7, 511, FX_43, FX_6, FX_NONE, 512, FX_43, FX_0, FX_NONE, 513, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_7, 512, FX_43, FX_6, FX_7, 512, FX_NONE, FX_NONE, FX_NONE, 513, FX_43, FX_NONE, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_6, FX_NONE, 513, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 359, 359, }, // Bat bite { DAMAGE_TYPE_2, 4, 0, 921, 0, 0, 1, 4096, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 521, 521, }, // Eel bite { DAMAGE_TYPE_2, 12, 0, 1177, 0, 0, 0, 0, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 513, 513 }, // Gill bite { DAMAGE_TYPE_2, 9, 0, 1177, 0, 0, 0, 0, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 499, 499 }, // Beast slash { DAMAGE_TYPE_3, 50, 43690, 1024, 8192, 0, 4, 32768, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 9012, 9014 }, // Axe { DAMAGE_TYPE_2, 18, 436906, 1024, 16384, 0, 2, 20480, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 1101, 1101 }, // Cleaver { DAMAGE_TYPE_2, 9, 218453, 1024, 0, 0, 1, 24576, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 1207, 1207 }, // Phantasm slash { DAMAGE_TYPE_2, 20, 436906, 1024, 16384, 0, 3, 24576, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 499, 495 }, // Gargoyle Slash { DAMAGE_TYPE_2, 16, 218453, 1024, 8192, 0, 4, 20480, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, 495, 496 }, // Cerberus bite { DAMAGE_TYPE_2, 19, 218453, 614, 8192, 0, 2, 24576, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 9013, 499 }, // Hound bite { DAMAGE_TYPE_2, 10, 218453, 614, 8192, 0, 2, 24576, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 1307, 1308 }, // Rat bite { DAMAGE_TYPE_2, 4, 0, 921, 0, 0, 1, 24576, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 499, 499 }, // Spider bite { DAMAGE_TYPE_2, 8, 0, 614, 0, 0, 1, 24576, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 499, 499 }, // Unk { DAMAGE_TYPE_2, 9, 0, 512, 0, 0, 0, 0, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_5, FX_NONE, 500, FX_NONE, FX_5, FX_NONE, 501, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_0, FX_NONE, 503, FX_NONE, FX_4, FX_NONE, -1, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_6, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 502, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 499, 499 }, { (DAMAGE_TYPE)-1, 0, 0, 2560, 0, 0, 0, 0, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_34, FX_35, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 499, 499 }, // Tchernobog burn vector { DAMAGE_TYPE_1, 2, 0, 0, 0, 15, 0, 0, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 351, 351 }, // Vodoo 1.0 vector { DAMAGE_TYPE_5, 25, 0, 0, 0, 0, 0, 0, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, -1, 0,0 }, // 22 kVectorGenDudePunch { DAMAGE_TYPE_0, 37, 874762, 620, 0, 0, 0, 0, FX_NONE, FX_NONE, FX_NONE, -1, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, FX_NONE, FX_NONE, FX_NONE, 357, 357, 499 }, }; ITEMDATA gItemData[] = { { 0, 2552, (char)-8, 0, 32, 32, -1, }, { 0, 2553, (char)-8, 0, 32, 32, -1, }, { 0, 2554, (char)-8, 0, 32, 32, -1, }, { 0, 2555, (char)-8, 0, 32, 32, -1, }, { 0, 2556, (char)-8, 0, 32, 32, -1, }, { 0, 2557, (char)-8, 0, 32, 32, -1, }, { 0, -1, (char)-8, 0, 255, 255, -1, }, { 0, 519, (char)-8, 0, 48, 48, 0, }, { 0, 822, (char)-8, 0, 40, 40, -1, }, { 0, 2169, (char)-8, 0, 40, 40, -1, }, { 0, 2433, (char)-8, 0, 40, 40, -1, }, { 0, 517, (char)-8, 0, 40, 40, -1, }, { 0, 783, (char)-8, 0, 40, 40, -1, }, { 0, 896, (char)-8, 0, 40, 40, -1, }, { 0, 825, (char)-8, 0, 40, 40, -1, }, { 0, 827, (char)-8, 0, 40, 40, 4, }, { 0, 828, (char)-8, 0, 40, 40, -1, }, { 0, 829, (char)-8, 0, 40, 40, -1, }, { 0, 830, (char)-8, 0, 80, 64, 1, }, { 0, 831, (char)-8, 0, 40, 40, -1, }, { 0, 863, (char)-8, 0, 40, 40, -1, }, { 0, 760, (char)-8, 0, 40, 40, 2, }, { 0, 836, (char)-8, 0, 40, 40, -1, }, { 0, 851, (char)-8, 0, 40, 40, -1, }, { 0, 2428, (char)-8, 0, 40, 40, -1, }, { 0, 839, (char)-8, 0, 40, 40, 3, }, { 0, 768, (char)-8, 0, 64, 64, -1, }, { 0, 840, (char)-8, 0, 48, 48, -1, }, { 0, 841, (char)-8, 0, 48, 48, -1, }, { 0, 842, (char)-8, 0, 48, 48, -1, }, { 0, 843, (char)-8, 0, 48, 48, -1, }, { 0, 683, (char)-8, 0, 40, 40, -1, }, { 0, 521, (char)-8, 0, 40, 40, -1, }, { 0, 604, (char)-8, 0, 40, 40, -1, }, { 0, 520, (char)-8, 0, 40, 40, -1, }, { 0, 803, (char)-8, 0, 40, 40, -1, }, { 0, 518, (char)-8, 0, 40, 40, -1, }, { 0, 522, (char)-8, 0, 40, 40, -1, }, { 0, 523, (char)-8, 0, 40, 40, -1, }, { 0, 837, (char)-8, 0, 80, 64, -1, }, { 0, 2628, (char)-8, 0, 64, 64, -1, }, { 0, 2586, (char)-8, 0, 64, 64, -1, }, { 0, 2578, (char)-8, 0, 64, 64, -1, }, { 0, 2602, (char)-8, 0, 64, 64, -1, }, { 0, 2594, (char)-8, 0, 64, 64, -1, }, { 0, 753, (char)-8, 0, 64, 64, -1, }, { 0, 753, (char)-8, 7, 64, 64, -1, }, { 0, 3558, (char)-128, 0, 64, 64, -1, }, { 0, 3558, (char)-128, 7, 64, 64, -1, } }; AMMOITEMDATA gAmmoItemData[] = { { 0, 618, (char)-8, 0, 40, 40, 480, 6, 7 }, { 0, 589, (char)-8, 0, 48, 48, 1, 5, 6 }, { 0, 589, (char)-8, 0, 48, 48, 1, 5, 6 }, { 0, 809, (char)-8, 0, 48, 48, 5, 5, 6 }, { 0, 811, (char)-8, 0, 48, 48, 1, 10, 11 }, { 0, 810, (char)-8, 0, 48, 48, 1, 11, 12 }, { 0, 820, (char)-8, 0, 24, 24, 10, 8, 0 }, { 0, 619, (char)-8, 0, 48, 48, 4, 2, 0 }, { 0, 812, (char)-8, 0, 48, 48, 15, 2, 0 }, { 0, 813, (char)-8, 0, 48, 48, 15, 3, 0 }, { 0, 525, (char)-8, 0, 48, 48, 100, 9, 10 }, { 0, 814, (char)-8, 0, 48, 48, 15, 255, 0 }, { 0, 817, (char)-8, 0, 48, 48, 100, 3, 0 }, { 0, 548, (char)-8, 0, 24, 24, 32, 7, 0 }, { 0, 0, (char)-8, 0, 48, 48, 6, 255, 0 }, { 0, 0, (char)-8, 0, 48, 48, 6, 255, 0 }, { 0, 816, (char)-8, 0, 48, 48, 8, 1, 0 }, { 0, 818, (char)-8, 0, 48, 48, 8, 255, 0 }, { 0, 819, (char)-8, 0, 48, 48, 8, 255, 0 }, { 0, 801, (char)-8, 0, 48, 48, 6, 4, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; WEAPONITEMDATA gWeaponItemData[] = { { 0, -1, (char)0, 0, 0, 0, 0, -1, 0 }, { 0, 559, (char)-8, 0, 48, 48, 3, 2, 8 }, { 0, 558, (char)-8, 0, 48, 48, 4, 3, 50 }, { 0, 524, (char)-8, 0, 48, 48, 2, 1, 9 }, { 0, 525, (char)-8, 0, 48, 48, 10, 9, 100 }, { 0, 539, (char)-8, 0, 48, 48, 8, 7, 64 }, { 0, 526, (char)-8, 0, 48, 48, 5, 4, 6 }, { 0, -1, (char)0, 0, 0, 0, 1, -1, 0 }, { 0, 618, (char)-8, 0, 48, 48, 7, 6, 480 }, { 0, 589, (char)-8, 0, 48, 48, 6, 5, 1 }, { 0, 800, (char)-8, 0, 48, 48, 9, 8, 35 } }; MissileType missileInfo[] = { // Cleaver { 2138, 978670, 512, 40, 40, (char)-16, 16, 1207, 1207 }, // Regular flare { 2424, 3145728, 0, 32, 32, (char)-128, 32, 420, 420 }, // Tesla alt { 3056, 2796202, 0, 32, 32, (char)-128, 32, 471, 471 }, // Flare alt { 2424, 2446677, 0, 32, 32, (char)-128, 4, 421, 421 }, // Spray flame { 0, 1118481, 0, 24, 24, (char)-128, 16, 1309, 351 }, // Fireball { 0, 1118481, 0, 32, 32, (char)-128, 32, 480, 480 }, // Tesla regular { 2130, 2796202, 0, 32, 32, (char)-128, 16, 470, 470 }, // EctoSkull { 870, 699050, 0, 32, 32, (char)-24, 32, 489, 490 }, // Hellhound flame { 0, 1118481, 0, 24, 24, (char)-128, 16, 462, 351 }, // Puke { 0, 838860, 0, 16, 16, (char)-16, 16, 1203, 172 }, // Reserved { 0, 838860, 0, 8, 8, (char)0, 16, 0,0 }, // Stone gargoyle projectile { 3056, 2097152, 0, 32, 32, (char)-128, 16, 1457, 249 }, // Napalm launcher { 0, 2446677, 0, 30, 30, (char)-128, 24, 480, 489 }, // Cerberus fireball { 0, 2446677, 0, 30, 30, (char)-128, 24, 480, 489 }, // Tchernobog fireball { 0, 1398101, 0, 24, 24, (char)-128, 16, 480, 489 }, // Regular life leech { 2446, 2796202, 0, 32, 32, (char)-128, 16, 491, 491 }, // Dropped life leech (enough ammo) { 3056, 2446677, 0, 16, 16, (char)-128, 16, 520, 520 }, // Dropped life leech (no ammo) { 3056, 1747626, 0, 32, 32, (char)-128, 16, 520, 520 } }; THINGINFO thingInfo[] = { //TNT Barrel { 25, 250, 32, 11, 4096, 80, 384, 907, (char)0, 0, 0, 0, 256, 256, 128, 64, 0, 0, 128, 1 }, // Armed Proxy Dynamite { 5, 5, 16, 3, 24576, 1600, 256, 3444, (char)-16, 0, 32, 32, 256, 256, 256, 64, 0, 0, 512, 1 }, // Armed Remote Dynamite { 5, 5, 16, 3, 24576, 1600, 256, 3457, (char)-16, 0, 32, 32, 256, 256, 256, 64, 0, 0, 512, 1 }, // Vase1 { 1, 20, 32, 3, 32768, 80, 0, 739, (char)0, 0, 0, 0, 256, 0, 256, 128, 0, 0, 0, 0 }, // Vase2 { 1, 150, 32, 3, 32768, 80, 0, 642, (char)0, 0, 0, 0, 256, 256, 256, 128, 0, 0, 0, 0 }, // Crate face { 10, 0, 0, 0, 0, 0, 0, 462, (char)0, 0, 0, 0, 0, 0, 0, 256, 0, 0, 0, 0 }, // Glass window { 1, 0, 0, 0, 0, 0, 0, 266, (char)0, 0, 0, 0, 256, 0, 256, 256, 0, 0, 0, 0, }, // Flourescent Light { 1, 0, 0, 0, 0, 0, 0, 796, (char)0, 0, 0, 0, 256, 0, 256, 256, 0, 0, 512, 0, }, // Wall Crack { 50, 0, 0, 0, 0, 0, 0, 1127, (char)0, 0, 0, 0, 0, 0, 0, 256, 0, 0, 0, 0, }, // Wood Beam { 8, 0, 0, 0, 0, 0, 0, 1142, (char)0, 0, 0, 0, 256, 0, 256, 128, 0, 0, 0, 0, }, // Spider's Web { 4, 0, 0, 0, 0, 0, 0, 1069, (char)0, 0, 0, 0, 256, 256, 64, 256, 0, 0, 128, 0, }, // Metal Grate { 40, 0, 0, 0, 0, 0, 0, 483, (char)0, 0, 0, 0, 64, 0, 128, 256, 0, 0, 0, 0, }, // Flammable Tree { 1, 0, 0, 0, 0, 0, 0, -1, (char)0, 0, 0, 0, 0, 256, 0, 256, 0, 0, 128, 0, }, // MachineGun Trap { 1000, 0, 0, 8, 0, 0, 0, -1, (char)0, 0, 0, 0, 0, 0, 128, 256, 0, 0, 512, 0, }, // Falling Rock { 0, 15, 8, 3, 32768, 0, 0, -1, (char)0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, // Kickable Pail { 0, 8, 48, 3, 49152, 0, 0, -1, (char)0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, }, // Gib Object { 10, 2, 0, 0, 32768, 0, 0, -1, (char)0, 0, 0, 0, 256, 0, 256, 256, 0, 0, 128, 0, }, // Explode Object { 20, 2, 0, 0, 32768, 0, 0, -1, (char)0, 0, 0, 0, 0, 0, 0, 256, 0, 0, 128, 0, }, // Armed stick Of TNT { 5, 14, 16, 3, 24576, 1600, 256, 3422, (char)-32, 0, 32, 32, 64, 256, 128, 64, 0, 0, 256, 1 }, // Armed bundle Of TNT { 5, 14, 16, 3, 24576, 1600, 256, 3433, (char)-32, 0, 32, 32, 64, 256, 128, 64, 0, 0, 256, 1 }, // Armed aerosol { 5, 14, 16, 3, 32768, 1600, 256, 3467, (char)-128, 0, 32, 32, 64, 256, 128, 64, 0, 0, 256, 1 }, // Bone (Flesh Garg.) { 5, 6, 16, 3, 32768, 1600, 256, 1462, (char)0, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 1 }, // Some alpha stuff { 8, 3, 16, 11, 32768, 1600, 256, -1, (char)0, 0, 0, 0, 256, 0, 256, 256, 0, 0, 0, 0, }, // WaterDrip { 0, 1, 1, 2, 0, 0, 0, 1147, (char)0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BloodDrip { 0, 1, 1, 2, 0, 0, 0, 1160, (char)0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, // Blood chucks1 { 15, 4, 4, 3, 24576, 0, 257, -1, (char)0, 0, 0, 0, 128, 64, 256, 256, 0, 0, 256, 0, }, // Blood chucks2 { 30, 30, 8, 3, 8192, 0, 257, -1, (char)0, 0, 0, 0, 128, 64, 256, 256, 0, 0, 64, 0, }, // Axe Zombie Head { 60, 5, 32, 3, 40960, 1280, 257, 3405, (char)0, 0, 40, 40, 128, 64, 256, 256, 0, 0, 64, 1, }, // Napalm's Alt Fire explosion { 80, 30, 32, 3, 57344, 1600, 256, 3281, (char)-128, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 1, }, // Fire Pod Explosion { 80, 30, 32, 3, 57344, 1600, 256, 2020, (char)-128, 0, 32, 32, 256, 0, 256, 256, 0, 0, 0, 1, }, // Green Pod Explosion { 80, 30, 32, 3, 57344, 1600, 256, 1860, (char)-128, 0, 32, 32, 256, 0, 256, 256, 0, 0, 0, 1, }, // Life Leech { 150, 30, 48, 3, 32768, 1600, 257, 800, (char)-128, 0, 48, 48, 64, 64, 112, 64, 0, 96, 96, 1, }, // Voodoo Head { 1, 30, 48, 3, 32768, 1600, 0, 2443, (char)-128, 0, 16, 16, 0, 0, 0, 0, 0, 0, 0, 1, }, // 433 - kGDXThingTNTProx { 5, 5, 16, 3, 24576, 1600, 256, 3444, (char)-16, 7, 32, 32, 256, 256, 256, 64, 0, 0, 512, 1 }, // 434 - kGDXThingThrowableRock { 5, 6, 16, 3, 32768, 1600, 256, 1462, (char)0, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 1 }, // 435 - kGDXThingCustomDudeLifeLeech { 150, 30, 48, 3, 32768, 1600, 257, 800, (char)-128, 0, 48, 48, 64, 64, 112, 64, 0, 96, 96, 1, }, }; EXPLOSION explodeInfo[] = { { 40, 10, 10, 75, 450, 0, 60, 80, 40 }, { 80, 20, 10, 150, 900, 0, 60, 160, 60 }, { 120, 40, 15, 225, 1350, 0, 60, 240, 80 }, { 80, 5, 10, 120, 20, 10, 60, 0, 40 }, { 120, 10, 10, 180, 40, 10, 60, 0, 80 }, { 160, 15, 10, 240, 60, 10, 60, 0, 120 }, { 40, 20, 10, 120, 0, 10, 30, 60, 40 }, { 80, 20, 10, 150, 800, 5, 60, 160, 60 }, }; int gDudeDrag = 0x2a00; short gAffectedSectors[kMaxSectors]; short gAffectedXWalls[kMaxXWalls]; short gPlayerGibThingComments[] = { 734, 735, 736, 737, 738, 739, 740, 741, 3038, 3049 }; void FireballSeqCallback(int, int); void sub_38938(int, int); void NapalmSeqCallback(int, int); void sub_3888C(int, int); void TreeToGibCallback(int, int); void DudeToGibCallback1(int, int); void DudeToGibCallback2(int, int); int nFireballClient = seqRegisterClient(FireballSeqCallback); int dword_2192D8 = seqRegisterClient(sub_38938); int nNapalmClient = seqRegisterClient(NapalmSeqCallback); int dword_2192E0 = seqRegisterClient(sub_3888C); int nTreeToGibClient = seqRegisterClient(TreeToGibCallback); int nDudeToGibClient1 = seqRegisterClient(DudeToGibCallback1); int nDudeToGibClient2 = seqRegisterClient(DudeToGibCallback2); int gPostCount = 0; struct POSTPONE { short at0; short at2; }; POSTPONE gPost[kMaxSprites]; static char buffer[120]; bool IsItemSprite(spritetype *pSprite) { return pSprite->type >= 100 && pSprite->type < 149; } bool IsWeaponSprite(spritetype *pSprite) { return pSprite->type >= 40 && pSprite->type < 51; } bool IsAmmoSprite(spritetype *pSprite) { return pSprite->type >= 60 && pSprite->type < 81; } bool IsUnderwaterSector(int nSector) { int nXSector = sector[nSector].extra; if (nXSector > 0 && xsector[nXSector].Underwater) return 1; return 0; } int actSpriteOwnerToSpriteId(spritetype *pSprite) { dassert(pSprite != NULL); if (pSprite->owner == -1) return -1; int nSprite = pSprite->owner & (kMaxSprites-1); if (pSprite->owner & kMaxSprites) nSprite = gPlayer[nSprite].pSprite->index; return nSprite; } void actPropagateSpriteOwner(spritetype *pTarget, spritetype *pSource) { dassert(pTarget != NULL && pSource != NULL); if (IsPlayerSprite(pSource)) pTarget->owner = (pSource->type-kDudePlayer1) | kMaxSprites; else pTarget->owner = pSource->index; } int actSpriteIdToOwnerId(int nSprite) { if (nSprite == -1) return -1; dassert(nSprite >= 0 && nSprite < kMaxSprites); spritetype *pSprite = &sprite[nSprite]; if (IsPlayerSprite(pSprite)) nSprite = (pSprite->type-kDudePlayer1) | kMaxSprites; return nSprite; } int actOwnerIdToSpriteId(int nSprite) { if (nSprite == -1) return -1; if (nSprite & kMaxSprites) nSprite = gPlayer[nSprite&(kMaxSprites-1)].pSprite->index; return nSprite; } bool actTypeInSector(int nSector, int nType) { for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { if (sprite[nSprite].index == nType) return 1; } return 0; } void actAllocateSpares(void) { } int DudeDifficulty[5] = { 512, 384, 256, 208, 160 }; SPRITEMASS gSpriteMass[]; // by NoOne: cache for getSpriteMassBySize(); short gProxySpritesList[]; // by NoOne: list of additional sprites which can be triggered by Proximity short gProxySpritesCount; // current count short gSightSpritesList[]; // by NoOne: list of additional sprites which can be triggered by Sight short gSightSpritesCount; // current count short gPhysSpritesList[]; // by NoOne: list of additional sprites which can be affected by physics short gPhysSpritesCount; // current count void actInit(bool bSaveLoad) { // by NoOne: init code for all my stuff if (!VanillaMode()) { // reset counters gProxySpritesCount = gSightSpritesCount = gPhysSpritesCount = 0; // fill arrays with negative values to avoid xvel 0 situation memset(gSightSpritesList, -1, sizeof(gSightSpritesList)); memset(gProxySpritesList, -1, sizeof(gProxySpritesList)); memset(gPhysSpritesList, -1, sizeof(gPhysSpritesList)); for (int i = 0; i < kMaxXSprites; i++) { if (xsprite[i].reference < 0) continue; XSPRITE* pXSprite = &xsprite[i]; spritetype* pSprite = &sprite[pXSprite->reference]; switch (pSprite->lotag) { // add statnum for faster dude searching case kGDXDudeTargetChanger: changespritestat(i, kStatGDXDudeTargetChanger); break; // remove kStatItem status from random item generators case 40: // Random weapon case 80: // Random ammo changespritestat(i, 0); break; case kCustomDude: case kCustomDudeBurning: getSpriteMassBySize(pSprite); // create mass cache break; } // init after loading save file if (bSaveLoad) { // add in list of physics affected sprites if (pXSprite->physAttr != 0) { gPhysSpritesList[gPhysSpritesCount++] = pSprite->index; // add sprite index getSpriteMassBySize(pSprite); // create mass cache } if (pXSprite->data3 != pXSprite->sysData1) { switch (pSprite->statnum) { case 6: switch (pSprite->lotag) { case kCustomDude: case kCustomDudeBurning: pXSprite->data3 = pXSprite->sysData1; // move sndStartId back from sysData1 to data3 break; } break; } } } // make Proximity flag work not just for dudes and things... if (pXSprite->Proximity && gProxySpritesCount < kMaxSuperXSprites) { switch (pSprite->statnum) { // exceptions case 4: // things already treated in their functions case 6: // enemies already treated in their functions // senseless to have sight and proximity together if (pXSprite->Sight && pXSprite->DudeLockout) pXSprite->Proximity = false; break; case 1: // effects case 2: // explosions case 3: // items case 9: // purgeable sprites case 13: // ??? case 14: // burning flares stuck case 7: // inactive enemies case kStatFree: // removed sprites case kStatMarker: // markers case 16: // path markers break; default: // senseless to have sight and proximity together if (pXSprite->Sight && pXSprite->DudeLockout) pXSprite->Proximity = false; else { gProxySpritesList[gProxySpritesCount++] = pSprite->xvel; if (gProxySpritesCount == kMaxSuperXSprites) ThrowError("Max (%d) *additional* Proximity sprites reached!",kMaxSuperXSprites); } break; } } // make Sight flag work not just for dudes and things... if (pXSprite->Sight && gSightSpritesCount < kMaxSuperXSprites) { switch (pSprite->statnum) { // exceptions case 1: // effects case 2: // explosions case 3: // items case 9: // purgeable sprites case 13: // ??? case 14: // burning flares stuck case 7: // inactive enemies case kStatFree: // removed sprites case kStatMarker: // markers case 16: // path markers break; default: gSightSpritesList[gSightSpritesCount++] = pSprite->xvel; if (gSightSpritesCount == kMaxSuperXSprites) ThrowError("Max (%d) Sight sprites reached!", kMaxSuperXSprites); break; } } } } for (int nSprite = headspritestat[3]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->type == 44) // Voodoo doll (ammo) pSprite->type = 70; // Voodoo doll (weapon) } for (int nSprite = headspritestat[11]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; int nXSprite = pSprite->extra; XSPRITE *pXSprite = NULL; if (nXSprite > 0 && nXSprite < kMaxXSprites) pXSprite = &xsprite[nXSprite]; if (pSprite->type == 459) { pXSprite->state = 0; pXSprite->waitTime = ClipLow(pXSprite->waitTime, 1); pSprite->cstat &= ~1; pSprite->cstat |= 32768; } } for (int nSprite = headspritestat[4]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; int nXSprite = pSprite->extra; if (nXSprite <= 0 || nXSprite >= kMaxXSprites) ThrowError("WARNING: Sprite %d is on the wrong status list!\n", nSprite); XSPRITE *pXSprite = &xsprite[nXSprite]; int nType = pSprite->type - 400; pSprite->clipdist = thingInfo[nType].at4; pSprite->hitag = thingInfo[nType].at5; if (pSprite->hitag&2) pSprite->hitag |= 4; xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0; pXSprite->health = thingInfo[nType].at0<<4; switch (pSprite->type) { case 401: case 413: case kGDXThingTNTProx: pXSprite->state = 0; break; case 426: { SEQINST *pInst = GetInstance(3, nXSprite); if (pInst && pInst->at13) { DICTNODE *hSeq = gSysRes.Lookup(pInst->at8, "SEQ"); if (!hSeq) break; seqSpawn(pInst->at8, 3, nXSprite); } break; } default: pXSprite->state = 1; break; } } if (gGameOptions.nMonsterSettings == 0) { gKillMgr.SetCount(0); while (headspritestat[6] >= 0) { spritetype *pSprite = &sprite[headspritestat[6]]; int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE *pXSprite = &xsprite[nXSprite]; // Drop Key if (pXSprite->key > 0) actDropObject(pSprite, 99 + pXSprite->key); DeleteSprite(headspritestat[6]); } } else { // by NoOne: WTF is this? /////////////// char unk[kDudeMax-kDudeBase]; memset(unk, 0, sizeof(unk)); for (int nSprite = headspritestat[6]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->type < kDudeBase || pSprite->type >= kDudeMax) ThrowError("Non-enemy sprite (%d) in the enemy sprite list.\n", nSprite); unk[pSprite->type-kDudeBase] = 1; } gKillMgr.sub_2641C(); /////////////// for (int i = 0; i < kDudeMax-kDudeBase; i++) for (int j = 0; j < 7; j++) dudeInfo[i].at70[j] = mulscale8(DudeDifficulty[gGameOptions.nDifficulty], dudeInfo[i].startDamage[j]); for (int nSprite = headspritestat[6]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE *pXSprite = &xsprite[nXSprite]; int nType = pSprite->type-kDudeBase; int seqStartId = dudeInfo[nType].seqStartID; if (!IsPlayerSprite(pSprite)) { switch (pSprite->type) { case 225: // by NoOne: FakeDude type (no seq, custom hitag, clipdist and cstat) break; case kCustomDude: case kCustomDudeBurning: pSprite->cstat |= 4096 + 256 + 1; seqStartId = getSeqStartId(pXSprite); // by NoOne: Custom Dude stores it's SEQ in data2 pXSprite->sysData1 = pXSprite->data3; // by NoOne move sndStartId to sysData1, because data3 used by the game; pXSprite->data3 = 0; break; default: pSprite->clipdist = dudeInfo[nType].clipdist; pSprite->cstat |= 4096 + 256 + 1; break; } xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0; // By NoOne: add a way to set custom hp for every enemy - should work only if map just started and not loaded. if (pXSprite->data4 <= 0) pXSprite->health = dudeInfo[nType].startHealth << 4; else pXSprite->health = ClipRange(pXSprite->data4 << 4, 1, 65535); } if (gSysRes.Lookup(seqStartId, "SEQ")) seqSpawn(seqStartId, 3, nXSprite); } aiInit(); } } void ConcussSprite(int a1, spritetype *pSprite, int x, int y, int z, int a6) { dassert(pSprite != NULL); int dx = pSprite->x-x; int dy = pSprite->y-y; int dz = (pSprite->z-z)>>4; int dist2 = 0x40000+dx*dx+dy*dy+dz*dz; dassert(dist2 > 0); a6 = scale(0x40000, a6, dist2); if (pSprite->hitag & 1) { int mass = 0; if (IsDudeSprite(pSprite)) { mass = dudeInfo[pSprite->lotag - kDudeBase].mass; switch (pSprite->lotag) { case kCustomDude: case kCustomDudeBurning: mass = getSpriteMassBySize(pSprite); break; } } else if (pSprite->type >= kThingBase && pSprite->type < kThingMax) mass = thingInfo[pSprite->type-400].at2; else ThrowError("Unexpected type in ConcussSprite(): Sprite: %d Type: %d Stat: %d", (int)pSprite->index, (int)pSprite->type, (int)pSprite->statnum); int size = (tilesiz[pSprite->picnum].x*pSprite->xrepeat*tilesiz[pSprite->picnum].y*pSprite->yrepeat)>>1; dassert(mass > 0); int t = scale(a6, size, mass); dx = mulscale16(t, dx); dy = mulscale16(t, dy); dz = mulscale16(t, dz); int nSprite = pSprite->index; dassert(nSprite >= 0 && nSprite < kMaxSprites); xvel[nSprite] += dx; yvel[nSprite] += dy; zvel[nSprite] += dz; } actDamageSprite(a1, pSprite, DAMAGE_TYPE_3, a6); } int actWallBounceVector(int *x, int *y, int nWall, int a4) { int wx, wy; GetWallNormal(nWall, &wx, &wy); int t = dmulscale16(*x, wx, *y, wy); int t2 = mulscale16r(t, a4+0x10000); *x -= mulscale16(wx, t2); *y -= mulscale16(wy, t2); return mulscale16r(t, 0x10000-a4); } int actFloorBounceVector(int *x, int *y, int *z, int nSector, int a5) { int t = 0x10000-a5; if (sector[nSector].floorheinum == 0) { int t2 = mulscale16(*z, t); *z = -(*z-t2); return t2; } walltype *pWall = &wall[sector[nSector].wallptr]; walltype *pWall2 = &wall[pWall->point2]; int angle = getangle(pWall2->x-pWall->x, pWall2->y-pWall->y)+512; int t2 = sector[nSector].floorheinum<<4; int t3 = approxDist(-0x10000, t2); int t4 = divscale16(-0x10000, t3); int t5 = divscale16(t2, t3); int t6 = mulscale30(t5, Cos(angle)); int t7 = mulscale30(t5, Sin(angle)); int t8 = tmulscale16(*x, t6, *y, t7, *z, t4); int t9 = mulscale16(t8, 0x10000+a5); *x -= mulscale16(t6, t9); *y -= mulscale16(t7, t9); *z -= mulscale16(t4, t9); return mulscale16r(t8, t); } void sub_2A620(int nSprite, int x, int y, int z, int nSector, int nDist, int a7, int a8, DAMAGE_TYPE a9, int a10, int a11, int a12, int a13) { UNREFERENCED_PARAMETER(a12); UNREFERENCED_PARAMETER(a13); char va0[(kMaxSectors+7)>>3]; int nOwner = actSpriteIdToOwnerId(nSprite); gAffectedSectors[0] = 0; gAffectedXWalls[0] = 0; GetClosestSpriteSectors(nSector, x, y, nDist, gAffectedSectors, va0, gAffectedXWalls); nDist <<= 4; if (a10 & 2) { for (int i = headspritestat[6]; i >= 0; i = nextspritestat[i]) { if (i != nSprite || (a10 & 1)) { spritetype *pSprite2 = &sprite[i]; if (pSprite2->extra > 0 && pSprite2->extra < kMaxXSprites) { if (pSprite2->hitag & 0x20) continue; if (!TestBitString(va0, pSprite2->sectnum)) continue; if (!CheckProximity(pSprite2, x, y, z, nSector, nDist)) continue; int dx = klabs(x-pSprite2->x); int dy = klabs(y-pSprite2->y); int dz = klabs(z-pSprite2->z)>>4; int dist = ksqrt(dx*dx+dy*dy+dz*dz); if (dist > nDist) continue; int vcx; if (dist != 0) vcx = a7+((nDist-dist)*a8)/nDist; else vcx = a7+a8; actDamageSprite(nSprite, pSprite2, a9, vcx<<4); if (a11) actBurnSprite(nOwner, &xsprite[pSprite2->extra], a11); } } } } if (a10 & 4) { for (int i = headspritestat[4]; i >= 0; i = nextspritestat[i]) { spritetype *pSprite2 = &sprite[i]; if (pSprite2->hitag&0x20) continue; if (!TestBitString(va0, pSprite2->sectnum)) continue; if (!CheckProximity(pSprite2, x, y, z, nSector, nDist)) continue; XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; if (pXSprite2->locked) continue; int dx = klabs(x-pSprite2->x); int dy = klabs(y-pSprite2->y); int dist = ksqrt(dx*dx+dy*dy); if (dist > nDist) continue; int vcx; if (dist != 0) vcx = a7+((nDist-dist)*a8)/nDist; else vcx = a7+a8; actDamageSprite(nSprite, pSprite2, a9, vcx<<4); if (a11) actBurnSprite(nOwner, pXSprite2, a11); } } } void sub_2AA94(spritetype *pSprite, XSPRITE *pXSprite) { int nSprite = actOwnerIdToSpriteId(pSprite->owner); actPostSprite(pSprite->index, 0); seqSpawn(9, 3, pSprite->extra); if (Chance(0x8000)) pSprite->cstat |= 4; sfxPlay3DSound(pSprite, 303, 24+(pSprite->hitag&3), 1); sub_2A620(nSprite, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 128, 0, 60, DAMAGE_TYPE_3, 15, 120, 0, 0); if (pXSprite->data4 > 1) { GibSprite(pSprite, GIBTYPE_5, NULL, NULL); int v14[2]; v14[0] = pXSprite->data4>>1; v14[1] = pXSprite->data4-v14[0]; int v4 = pSprite->ang; xvel[pSprite->index] = 0; yvel[pSprite->index] = 0; zvel[pSprite->index] = 0; for (int i = 0; i < 2; i++) { int t1 = Random(0x33333)+0x33333; int t2 = Random2(0x71); pSprite->ang = (t2+v4+2048)&2047; spritetype *pSprite2 = actFireThing(pSprite, 0, 0, -0x93d0, 428, t1); XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; pSprite2->owner = pSprite->owner; seqSpawn(61, 3, pSprite2->extra, nNapalmClient); pXSprite2->data4 = v14[i]; } } } spritetype *actSpawnFloor(spritetype *pSprite) { short nSector = pSprite->sectnum; int x = pSprite->x; int y = pSprite->y; updatesector(x, y, &nSector); int zFloor = getflorzofslope(nSector, x, y); spritetype *pSprite2 = actSpawnSprite(nSector, x, y, zFloor, 3, 0); if (pSprite2) pSprite2->cstat &= ~257; return pSprite2; } spritetype *actDropAmmo(spritetype *pSprite, int nType) { spritetype *pSprite2 = NULL; if (pSprite && pSprite->statnum < kMaxStatus && nType >= 60 && nType < 81) { pSprite2 = actSpawnFloor(pSprite); AMMOITEMDATA *pAmmo = &gAmmoItemData[nType-60]; pSprite2->type = nType; pSprite2->picnum = pAmmo->picnum; pSprite2->shade = pAmmo->shade; pSprite2->xrepeat = pAmmo->xrepeat; pSprite2->yrepeat = pAmmo->yrepeat; } return pSprite2; } spritetype *actDropWeapon(spritetype *pSprite, int nType) { spritetype *pSprite2 = NULL; if (pSprite && pSprite->statnum < kMaxStatus && nType >= 40 && nType < 51) { pSprite2 = actSpawnFloor(pSprite); WEAPONITEMDATA *pWeapon = &gWeaponItemData[nType-40]; pSprite2->type = nType; pSprite2->picnum = pWeapon->picnum; pSprite2->shade = pWeapon->shade; pSprite2->xrepeat = pWeapon->xrepeat; pSprite2->yrepeat = pWeapon->yrepeat; } return pSprite2; } spritetype *actDropItem(spritetype *pSprite, int nType) { spritetype *pSprite2 = NULL; if (pSprite && pSprite->statnum < kMaxStatus && nType >= 100 && nType < 149) { pSprite2 = actSpawnFloor(pSprite); ITEMDATA *pItem = &gItemData[nType-100]; pSprite2->type = nType; pSprite2->picnum = pItem->picnum; pSprite2->shade = pItem->shade; pSprite2->xrepeat = pItem->xrepeat; pSprite2->yrepeat = pItem->yrepeat; } return pSprite2; } spritetype *actDropKey(spritetype *pSprite, int nType) { spritetype *pSprite2 = NULL; if (pSprite && pSprite->statnum < kMaxStatus && nType >= 100 && nType <= 106) { pSprite2 = actDropItem(pSprite, nType); if (pSprite2 && gGameOptions.nGameType == 1) { if (pSprite2->extra == -1) dbInsertXSprite(pSprite2->index); xsprite[pSprite2->extra].respawn = 3; gSpriteHit[pSprite2->extra].florhit = 0; gSpriteHit[pSprite2->extra].ceilhit = 0; } } return pSprite2; } spritetype *actDropFlag(spritetype *pSprite, int nType) { spritetype *pSprite2 = NULL; if (pSprite && pSprite->statnum < kMaxStatus && (nType == 147 || nType == 148)) { pSprite2 = actDropItem(pSprite, nType); if (pSprite2 && gGameOptions.nGameType == 3) { evPost(pSprite2->index, 3, 1800, CALLBACK_ID_17); } } return pSprite2; } spritetype *actDropObject(spritetype *pSprite, int nType) { spritetype *pSprite2 = NULL; if (nType >= 100 && nType <= 106) pSprite2 = actDropKey(pSprite, nType); else if (nType == 147 || nType == 148) pSprite2 = actDropFlag(pSprite, nType); else if (nType >= 100 && nType < 149) pSprite2 = actDropItem(pSprite, nType); else if (nType >= 60 && nType < 81) pSprite2 = actDropAmmo(pSprite, nType); else if (nType >= 40 && nType < 51) pSprite2 = actDropWeapon(pSprite, nType); if (pSprite2) { int top, bottom; GetSpriteExtents(pSprite2, &top, &bottom); if (bottom >= pSprite2->z) pSprite2->z -= bottom - pSprite2->z; } return pSprite2; } bool actHealDude(XSPRITE *pXDude, int a2, int a3) { dassert(pXDude != NULL); a2 <<= 4; a3 <<= 4; if (pXDude->health < a3) { spritetype *pSprite = &sprite[pXDude->reference]; if (IsPlayerSprite(pSprite)) sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 780, pSprite->sectnum); pXDude->health = ClipHigh(pXDude->health+a2, a3); return 1; } return 0; } void actKillDude(int a1, spritetype *pSprite, DAMAGE_TYPE a3, int a4) { spritetype *pSprite2 = &sprite[a1]; dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); int nType = pSprite->type-kDudeBase; int nXSprite = pSprite->extra; dassert(nXSprite > 0); XSPRITE *pXSprite = &xsprite[pSprite->extra]; switch (pSprite->type) { case kCustomDude: { removeDudeStuff(pSprite); if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) { if (pXSprite->data1 >= 459 && pXSprite->data1 < (459 + kExplodeMax) - 1 && Chance(0x4000) && a3 != 5 && a3 != 4) { doExplosion(pSprite, pXSprite->data1 - 459); if (Chance(0x9000)) a3 = (DAMAGE_TYPE) 3; } if (a3 == DAMAGE_TYPE_1) { if ((gSysRes.Lookup(pXSprite->data2 + 15, "SEQ") || gSysRes.Lookup(pXSprite->data2 + 16, "SEQ")) && pXSprite->medium == 0) { if (gSysRes.Lookup(pXSprite->data2 + 3, "SEQ")) { pSprite->type = kCustomDudeBurning; if (pXSprite->data2 == kDefaultAnimationBase) // don't inherit palette for burning if using default animation pSprite->pal = 0; aiNewState(pSprite, pXSprite, &GDXGenDudeBurnGoto); actHealDude(pXSprite, dudeInfo[55].startHealth, dudeInfo[55].startHealth); if (pXSprite->burnTime <= 0) pXSprite->burnTime = 1200; gDudeExtra[pSprite->extra].at0 = (int)gFrameClock + 360; return; } } else { pXSprite->burnTime = 0; pXSprite->burnSource = -1; a3 = DAMAGE_TYPE_0; } } } else { pXSprite->locked = 1; // lock while transforming aiSetGenIdleState(pSprite, pXSprite); // set idle state if (pXSprite->key > 0) // drop keys actDropObject(pSprite, 100 + pXSprite->key - 1); if (pXSprite->dropMsg > 0) // drop items actDropObject(pSprite, pXSprite->dropMsg); pSprite->hitag &= ~kPhysMove; xvel[pSprite->index] = yvel[pSprite->index] = 0; int seqId = pXSprite->data2 + 18; if (!gSysRes.Lookup(seqId, "SEQ")) { seqKill(3, nXSprite); sfxPlayGDXGenDudeSound(pSprite, 10); spritetype* pEffect = gFX.fxSpawn((FX_ID)52, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, pSprite->ang); if (pEffect != NULL) { pEffect->cstat = CSTAT_SPRITE_ALIGNMENT_FACING; pEffect->pal = 6; pEffect->xrepeat = pSprite->xrepeat; pEffect->yrepeat = pSprite->yrepeat; } GIBTYPE nGibType; for (int i = 0; i < 3; i++) { if (Chance(0x3000)) nGibType = GIBTYPE_6; else if (Chance(0x2000)) nGibType = GIBTYPE_5; else nGibType = GIBTYPE_17; int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); CGibPosition gibPos(pSprite->x, pSprite->y, top); CGibVelocity gibVel(xvel[pSprite->index] >> 1, yvel[pSprite->index] >> 1, -0xccccc); GibSprite(pSprite, nGibType, &gibPos, &gibVel); } return; } seqSpawn(seqId, 3, nXSprite, -1); sfxPlayGDXGenDudeSound(pSprite, 10); return; } break; } case 227: // Cerberus seqSpawn(dudeInfo[nType].seqStartID+1, 3, nXSprite, -1); return; case 201: case 202: case 247: case 248: if (a3 == DAMAGE_TYPE_1 && pXSprite->medium == 0) { pSprite->type = 240; aiNewState(pSprite, pXSprite, &cultistBurnGoto); actHealDude(pXSprite, dudeInfo[40].startHealth, dudeInfo[40].startHealth); return; } // no break fallthrough__; case 251: if (a3 == DAMAGE_TYPE_1 && pXSprite->medium == 0) { pSprite->type = 253; aiNewState(pSprite, pXSprite, &beastBurnGoto); actHealDude(pXSprite, dudeInfo[53].startHealth, dudeInfo[53].startHealth); return; } // no break fallthrough__; case 245: if (a3 == DAMAGE_TYPE_1 && pXSprite->medium == 0) { pSprite->type = 239; aiNewState(pSprite, pXSprite, &innocentBurnGoto); actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth); return; } break; } for (int p = connecthead; p >= 0; p = connectpoint2[p]) { if (gPlayer[p].at2ee == pSprite->index && gPlayer[p].at1fe > 0) gPlayer[p].at2ee = -1; } if (pSprite->type != 249) trTriggerSprite(pSprite->index, pXSprite, 0); pSprite->hitag |= 7; if (IsPlayerSprite(pSprite2)) { PLAYER *pPlayer = &gPlayer[pSprite2->index-kDudePlayer1]; if (gGameOptions.nGameType == 1) pPlayer->at2c6++; } if (pXSprite->key > 0) actDropObject(pSprite, 100+pXSprite->key-1); if (pXSprite->dropMsg > 0) actDropObject(pSprite, pXSprite->dropMsg); if (pSprite->type == 201) { int nRand = Random(100); if (nRand < 10) actDropObject(pSprite, 42); else if (nRand < 50) actDropObject(pSprite, 69); } else if (pSprite->type == 202) { int nRand = Random(100); if (nRand <= 10) actDropObject(pSprite, 41); else if (nRand <= 50) actDropObject(pSprite, 67); } int nSeq; switch (a3) { case DAMAGE_TYPE_3: nSeq = 2; switch (pSprite->type) { case kCustomDude: case kCustomDudeBurning: sfxPlayGDXGenDudeSound(pSprite, 4); break; case 201: case 202: case 230: case 239: case 240: case 245: case 246: case 247: case 248: case 249: case 250: case 252: sfxPlay3DSound(pSprite, 717,-1,0); break; } break; case DAMAGE_TYPE_1: nSeq = 3; sfxPlay3DSound(pSprite, 351, -1, 0); break; case DAMAGE_TYPE_5: switch (pSprite->type) { case 203: case 205: nSeq = 14; break; case 204: nSeq = 11; break; default: nSeq = 1; break; } break; case DAMAGE_TYPE_0: switch (pSprite->type) { case 201: case 202: nSeq = 1; break; default: nSeq = 1; break; } break; default: nSeq = 1; break; } if (!gSysRes.Lookup(dudeInfo[nType].seqStartID + nSeq, "SEQ")) { seqKill(3, nXSprite); gKillMgr.AddKill(pSprite); actPostSprite(pSprite->index, 1024); return; } switch (pSprite->type) { case 203: sfxPlay3DSound(pSprite, 1107+Random(2), -1, 0); if (nSeq == 2) { seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1); int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); CGibPosition gibPos(pSprite->x, pSprite->y, top); CGibVelocity gibVel(xvel[pSprite->index]>>1, yvel[pSprite->index]>>1, -0xccccc); GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel); } else if (nSeq == 1 && Chance(0x4000)) { seqSpawn(dudeInfo[nType].seqStartID+7, 3, nXSprite, nDudeToGibClient1); evPost(pSprite->index, 3, 0, CALLBACK_ID_5); sfxPlay3DSound(pSprite, 362, -1, 0); pXSprite->data1 = 35; pXSprite->data2 = 5; int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); CGibPosition gibPos(pSprite->x, pSprite->y, top); CGibVelocity gibVel(xvel[pSprite->index] >> 1, yvel[pSprite->index] >> 1, -0x111111); GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel); } else if (nSeq == 14) seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); else if (nSeq == 3) seqSpawn(dudeInfo[nType].seqStartID+13, 3, nXSprite, nDudeToGibClient2); else seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1); break; case 201: case 202: case 247: case 248: sfxPlay3DSound(pSprite, 1018+Random(2), -1, 0); if (nSeq == 3) seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient2); else seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1); break; case 240: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 718, -1, 0); else sfxPlay3DSound(pSprite, 1018+Random(2), -1, 0); a3 = DAMAGE_TYPE_3; if (Chance(0x8000)) { for (int i = 0; i < 3; i++) GibSprite(pSprite, GIBTYPE_7, NULL, NULL); seqSpawn(dudeInfo[nType].seqStartID+16-Random(1), 3, nXSprite, nDudeToGibClient1); } else seqSpawn(dudeInfo[nType].seqStartID+15, 3, nXSprite, nDudeToGibClient2); break; case kCustomDude: sfxPlayGDXGenDudeSound(pSprite, 2); if (nSeq == 3) { bool seq15 = gSysRes.Lookup(pXSprite->data2 + 15, "SEQ"); bool seq16 = gSysRes.Lookup(pXSprite->data2 + 16, "SEQ"); if (seq15 && seq16) seqSpawn((15 + Random(2)) + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); else if (seq16) seqSpawn(16 + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); else if (seq15) seqSpawn(15 + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); else if (gSysRes.Lookup(pXSprite->data2 + nSeq, "SEQ")) seqSpawn(nSeq + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); else seqKill(3, nXSprite); } else { seqSpawn(nSeq + pXSprite->data2, 3, nXSprite, nDudeToGibClient1); } pXSprite->txID = 0; // to avoid second trigger. break; case kCustomDudeBurning: { sfxPlayGDXGenDudeSound(pSprite, 4); a3 = DAMAGE_TYPE_3; if (Chance(0x4000)) { int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); CGibPosition gibPos(pSprite->x, pSprite->y, top); CGibVelocity gibVel(xvel[pSprite->index] >> 1, yvel[pSprite->index] >> 1, -0xccccc); GibSprite(pSprite, GIBTYPE_7, &gibPos, &gibVel); } int seqId = pXSprite->data2; bool seq15 = gSysRes.Lookup(pXSprite->data2 + 15, "SEQ"); bool seq16 = gSysRes.Lookup(pXSprite->data2 + 16, "SEQ"); if (seq15 && seq16) seqId += (15 + Random(2)); else if (seq16) seqId += 16; else seqId += 15; seqSpawn(seqId, 3, nXSprite, nDudeToGibClient1); break; } case 241: if (Chance(0x8000) && nSeq == 3) sfxPlay3DSound(pSprite, 1109, -1, 0); else sfxPlay3DSound(pSprite, 1107+Random(2), -1, 0); a3 = DAMAGE_TYPE_3; if (Chance(0x8000)) { seqSpawn(dudeInfo[nType].seqStartID+13, 3, nXSprite, nDudeToGibClient1); int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); CGibPosition gibPos(pSprite->x, pSprite->y, top); CGibVelocity gibVel(xvel[pSprite->index]>>1, yvel[pSprite->index]>>1, -0xccccc); GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel); } else seqSpawn(dudeInfo[nType].seqStartID+13, 3, nXSprite, nDudeToGibClient2); break; case 242: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1206, -1, 0); else sfxPlay3DSound(pSprite, 1204+Random(2), -1, 0); seqSpawn(dudeInfo[4].seqStartID+10, 3, nXSprite, -1); break; case 239: a3 = DAMAGE_TYPE_3; seqSpawn(dudeInfo[nType].seqStartID+7, 3, nXSprite, nDudeToGibClient1); break; case 204: if (nSeq == 14) { sfxPlay3DSound(pSprite, 1206, -1, 0); seqSpawn(dudeInfo[nType].seqStartID+11, 3, nXSprite, -1); break; } sfxPlay3DSound(pSprite, 1204+Random(2), -1, 0); if (nSeq == 3) seqSpawn(dudeInfo[nType].seqStartID+10, 3, nXSprite, -1); else seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 206: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1405, -1, 0); else sfxPlay3DSound(pSprite, 1403+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 207: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1455, -1, 0); else sfxPlay3DSound(pSprite, 1453+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 210: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1605, -1, 0); else sfxPlay3DSound(pSprite, 1603+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 211: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1305, -1, 0); else sfxPlay3DSound(pSprite, 1303+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 212: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1905, -1, 0); else sfxPlay3DSound(pSprite, 1903+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 213: if (pSprite->owner != -1) { spritetype *pOwner = &sprite[actSpriteOwnerToSpriteId(pSprite)]; gDudeExtra[pOwner->extra].at6.u1.at4--; } if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1805, -1, 0); else sfxPlay3DSound(pSprite, 1803+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 214: if (pSprite->owner != -1) { spritetype *pOwner = &sprite[actSpriteOwnerToSpriteId(pSprite)]; gDudeExtra[pOwner->extra].at6.u1.at4--; } if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1805, -1, 0); else sfxPlay3DSound(pSprite, 1803+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 215: if (pSprite->owner != -1) { spritetype *pOwner = &sprite[actSpriteOwnerToSpriteId(pSprite)]; gDudeExtra[pOwner->extra].at6.u1.at4--; } if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1805, -1, 0); else sfxPlay3DSound(pSprite, 1803+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 216: sfxPlay3DSound(pSprite, 1850, -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 217: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1705, -1, 0); else sfxPlay3DSound(pSprite, 1703+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 218: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 1505, -1, 0); else sfxPlay3DSound(pSprite, 1503+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 219: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 2005, -1, 0); else sfxPlay3DSound(pSprite, 2003+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 220: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 2105, -1, 0); else sfxPlay3DSound(pSprite, 2103+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 221: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 2205, -1, 0); else sfxPlay3DSound(pSprite, 2203+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 222: if (a4 == 5) sfxPlay3DSound(pSprite, 2471, -1, 0); else sfxPlay3DSound(pSprite, 2472, -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 223: if (a4 == 5) sfxPlay3DSound(pSprite, 2451, -1, 0); else sfxPlay3DSound(pSprite, 2452, -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 224: sfxPlay3DSound(pSprite, 2501, -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 225: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 2205, -1, 0); else sfxPlay3DSound(pSprite, 2203+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 226: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 2205, -1, 0); else sfxPlay3DSound(pSprite, 2203+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 227: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 2305, -1, 0); else sfxPlay3DSound(pSprite, 2305+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 228: if (Chance(0x4000) && nSeq == 3) sfxPlay3DSound(pSprite, 2305, -1, 0); else sfxPlay3DSound(pSprite, 2305+Random(2), -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 229: sfxPlay3DSound(pSprite, 2380, -1, 0); seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; case 252: a3 = DAMAGE_TYPE_3; seqSpawn(dudeInfo[nType].seqStartID+11, 3, nXSprite, nDudeToGibClient1); break; case 251: sfxPlay3DSound(pSprite, 9000+Random(2), -1, 0); if (nSeq == 3) seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient2); else seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, nDudeToGibClient1); break; case 253: a3 = DAMAGE_TYPE_3; seqSpawn(dudeInfo[nType].seqStartID+12, 3, nXSprite, nDudeToGibClient1); break; default: seqSpawn(dudeInfo[nType].seqStartID+nSeq, 3, nXSprite, -1); break; } // kMaxSprites - 1 = custom dude had once life leech if (pSprite->owner != -1 && pSprite->owner != (kMaxSprites - 1)) { //int owner = actSpriteIdToOwnerId(pSprite->xvel); int owner = pSprite->owner; switch (sprite[owner].lotag) { case kCustomDude: case kCustomDudeBurning: if (owner != -1) gDudeExtra[sprite[owner].extra].at6.u1.at4--; break; default: break; } } if (a3 == DAMAGE_TYPE_3) { DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type-kDudeBase]; for (int i = 0; i < 3; i++) if (pDudeInfo->nGibType[i] > -1) GibSprite(pSprite, (GIBTYPE)pDudeInfo->nGibType[i], NULL, NULL); for (int i = 0; i < 4; i++) fxSpawnBlood(pSprite, a4); } gKillMgr.AddKill(pSprite); actCheckRespawn(pSprite); pSprite->type = 426; actPostSprite(pSprite->index, 4); } int actDamageSprite(int nSource, spritetype *pSprite, DAMAGE_TYPE a3, int a4) { dassert(nSource < kMaxSprites); if (pSprite->hitag&32) return 0; int nXSprite = pSprite->extra; if (nXSprite <= 0) return 0; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE *pXSprite = &xsprite[nXSprite]; dassert(pXSprite->reference == pSprite->index); if ((pXSprite->health == 0 && pSprite->statnum != 6) || pXSprite->locked) return 0; if (nSource == -1) nSource = pSprite->index; PLAYER *pSourcePlayer = NULL; if (IsPlayerSprite(&sprite[nSource])) pSourcePlayer = &gPlayer[sprite[nSource].type-kDudePlayer1]; if (!gGameOptions.bFriendlyFire && pSourcePlayer != NULL && (gGameOptions.nGameType == 1 || gGameOptions.nGameType == 3) && IsPlayerSprite(pSprite)) { PLAYER *pTargetPlayer = &gPlayer[pSprite->type-kDudePlayer1]; if (pSourcePlayer != pTargetPlayer) { if (gGameOptions.nGameType == 1) return 0; if (gGameOptions.nGameType == 3 && (pSourcePlayer->at2ea&3) == (pTargetPlayer->at2ea&3)) return 0; } } switch (pSprite->statnum) { case 6: { if (pSprite->type < kDudeBase || pSprite->type >= kDudeMax) { sprintf(buffer, "Bad Dude Failed: initial=%d type=%d %s\n", (int)pSprite->zvel, (int)pSprite->type, (int)(pSprite->hitag&16) ? "RESPAWN" : "NORMAL"); ThrowError(buffer); } dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); int nType = pSprite->type-kDudeBase; int nDamageFactor = dudeInfo[nType].at70[a3]; if (!nDamageFactor) return 0; if (nDamageFactor != 256) a4 = mulscale8(a4, nDamageFactor); if (!IsPlayerSprite(pSprite)) { if (!pXSprite->health) return 0; a4 = aiDamageSprite(pSprite, pXSprite, nSource, a3, a4); if (!pXSprite->health) { if (a3 == DAMAGE_TYPE_3 && a4 < 160) a3 = DAMAGE_TYPE_0; actKillDude(nSource, pSprite, a3, a4); } } else { PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; if (pXSprite->health > 0 || playerSeqPlaying(pPlayer, 16)) a4 = playerDamageSprite(nSource, pPlayer, a3, a4); } break; } case 4: dassert(pSprite->type >= kThingBase && pSprite->type < kThingMax); int nType = pSprite->type-kThingBase; int nDamageFactor = thingInfo[nType].at17[a3]; if (!nDamageFactor) return 0; if (nDamageFactor != 256) a4 = mulscale8(a4, nDamageFactor); pXSprite->health = ClipLow(pXSprite->health-a4, 0); if (!pXSprite->health) { if (pSprite->type == 431 || pSprite->type == kGDXThingCustomDudeLifeLeech) { GibSprite(pSprite, GIBTYPE_14, NULL, NULL); pXSprite->data1 = 0; pXSprite->data2 = 0; pXSprite->data3 = 0; pXSprite->stateTimer = 0; pXSprite->data4 = 0; pXSprite->isTriggered = 0; pXSprite->DudeLockout = 0; if (pSprite->owner >= 0 && sprite[pSprite->owner].type == kCustomDude) sprite[pSprite->owner].owner = kMaxSprites -1; // By NoOne: indicates if custom dude had life leech. } else if (!(pSprite->hitag&16)) actPropagateSpriteOwner(pSprite, &sprite[nSource]); trTriggerSprite(pSprite->index, pXSprite, 0); switch (pSprite->type) { case 416: case 417: case 425: case 426: case 427: if (a3 == 3 && pSourcePlayer && gFrameClock > pSourcePlayer->at312 && Chance(0x4000)) { sfxPlay3DSound(pSourcePlayer->pSprite, gPlayerGibThingComments[Random(10)], 0, 2); pSourcePlayer->at312 = (int)gFrameClock+3600; } break; case 413: seqSpawn(28, 3, pSprite->extra, -1); break; case 407: seqSpawn(12, 3, pSprite->extra, -1); GibSprite(pSprite, GIBTYPE_6, NULL, NULL); break; case 410: seqSpawn(15, 3, pSprite->extra, -1); break; case 411: seqSpawn(21, 3, pSprite->extra, -1); GibSprite(pSprite, GIBTYPE_4, NULL, NULL); break; case 412: switch (pXSprite->data1) { case -1: GibSprite(pSprite, GIBTYPE_14, NULL, NULL); sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 312, pSprite->sectnum); actPostSprite(pSprite->index, 1024); break; case 0: seqSpawn(25, 3, pSprite->extra, nTreeToGibClient); sfxPlay3DSound(pSprite, 351, -1, 0); break; case 1: seqSpawn(26, 3, pSprite->extra, nTreeToGibClient); sfxPlay3DSound(pSprite, 351, -1, 0); break; } break; case 422: if (seqGetStatus(3, nXSprite) < 0) seqSpawn(19, 3, pSprite->extra, -1); break; } } break; } return a4>>4; } void actHitcodeToData(int a1, HITINFO *pHitInfo, int *a3, spritetype **a4, XSPRITE **a5, int *a6, walltype **a7, XWALL **a8, int *a9, sectortype **a10, XSECTOR **a11) { dassert(pHitInfo != NULL); int nSprite = -1; spritetype *pSprite = NULL; XSPRITE *pXSprite = NULL; int nWall = -1; walltype *pWall = NULL; XWALL *pXWall = NULL; int nSector = -1; sectortype *pSector = NULL; XSECTOR *pXSector = NULL; switch (a1) { case 3: case 5: nSprite = pHitInfo->hitsprite; dassert(nSprite >= 0 && nSprite < kMaxSprites); pSprite = &sprite[nSprite]; if (pSprite->extra > 0) pXSprite = &xsprite[pSprite->extra]; break; case 0: case 4: nWall = pHitInfo->hitwall; dassert(nWall >= 0 && nWall < kMaxWalls); pWall = &wall[nWall]; if (pWall->extra > 0) pXWall = &xwall[pWall->extra]; break; case 1: case 2: case 6: nSector = pHitInfo->hitsect; dassert(nSector >= 0 && nSector < kMaxSectors); pSector = §or[nSector]; if (pSector->extra > 0) pXSector = &xsector[pSector->extra]; break; } if (a3) *a3 = nSprite; if (a4) *a4 = pSprite; if (a5) *a5 = pXSprite; if (a6) *a6 = nWall; if (a7) *a7 = pWall; if (a8) *a8 = pXWall; if (a9) *a9 = nSector; if (a10) *a10 = pSector; if (a11) *a11 = pXSector; } void actImpactMissile(spritetype *pMissile, int a2) { int nXMissile = pMissile->extra; dassert(nXMissile > 0 && nXMissile < kMaxXSprites); XSPRITE *pXMissile = &xsprite[pMissile->extra]; int nSpriteHit = -1; spritetype *pSpriteHit = NULL; XSPRITE *pXSpriteHit = NULL; int nWallHit = -1; walltype *pWallHit = NULL; XWALL *pXWallHit = NULL; int nSectorHit = -1; sectortype *pSectorHit = NULL; XSECTOR *pXSectorHit = NULL; actHitcodeToData(a2, &gHitInfo, &nSpriteHit, &pSpriteHit, &pXSpriteHit, &nWallHit, &pWallHit, &pXWallHit, &nSectorHit, &pSectorHit, &pXSectorHit); THINGINFO *pThingInfo = NULL; DUDEINFO *pDudeInfo = NULL; if (a2 == 3 && pSpriteHit) { if (pSpriteHit->statnum == 4) { dassert(pXSpriteHit != NULL); pThingInfo = &thingInfo[pSpriteHit->type-kThingBase]; } else if (pSpriteHit->statnum == 6) { dassert(pXSpriteHit != NULL); pDudeInfo = &dudeInfo[pSpriteHit->type-kDudeBase]; } } switch (pMissile->type) { case 315: if (a2 == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) { int nOwner = actSpriteOwnerToSpriteId(pMissile); DAMAGE_TYPE rand1 = (DAMAGE_TYPE)Random(7); int rand2 = (7+Random(7))<<4; int nDamage = actDamageSprite(nOwner, pSpriteHit, rand1, rand2); if ((pThingInfo && pThingInfo->at17[DAMAGE_TYPE_1] != 0) || (pDudeInfo && pDudeInfo->at70[DAMAGE_TYPE_1] != 0)) actBurnSprite(pMissile->owner, pXSpriteHit, 360); // by NoOne: make Life Leech heal user, just like it was in 1.0x versions if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus() && pDudeInfo != NULL) { spritetype* pSource = &sprite[nOwner]; XSPRITE* pXSource = (pSource->extra >= 0) ? &xsprite[pSource->extra] : NULL; if (IsDudeSprite(pSource) && pXSource != NULL && pXSource->health != 0) actHealDude(pXSource, nDamage >> 2, dudeInfo[pSource->lotag - kDudeBase].startHealth); } } if (pMissile->extra > 0) { actPostSprite(pMissile->index, 0); if (pMissile->ang == 1024) sfxPlay3DSound(pMissile, 307, -1, 0); pMissile->type = 0; seqSpawn(9, 3, pMissile->extra, -1); } else { actPostSprite(pMissile->index, 1024); } break; case 302: sub_51340(pMissile, a2); if ((a2 == 0 || a2 == 4) && pWallHit) { spritetype *pFX = gFX.fxSpawn(FX_52, pMissile->sectnum, pMissile->x, pMissile->y, pMissile->z, 0); if (pFX) pFX->ang = (GetWallAngle(nWallHit)+512)&2047; } GibSprite(pMissile, GIBTYPE_24, NULL, NULL); actPostSprite(pMissile->index, 1024); break; case 309: seqKill(3, nXMissile); if (a2 == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) { int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDamage = (15+Random(7))<<4; actDamageSprite(nOwner, pSpriteHit, DAMAGE_TYPE_2, nDamage); } actPostSprite(pMissile->index, 1024); break; case 311: sfxKill3DSound(pMissile, -1, -1); sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 306, pMissile->sectnum); GibSprite(pMissile, GIBTYPE_6, NULL, NULL); if (a2 == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) { int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDamage = (25+Random(20))<<4; actDamageSprite(nOwner, pSpriteHit, DAMAGE_TYPE_5, nDamage); } actPostSprite(pMissile->index, 1024); break; case 316: case 317: sfxKill3DSound(pMissile, -1, -1); sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 306, pMissile->sectnum); if (a2 == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) { if (pDudeInfo) { } int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDmgMul = 3; if (pMissile->type == 317) nDmgMul = 6; int nDamage = (nDmgMul+Random(nDmgMul))<<4; actDamageSprite(nOwner, pSpriteHit, DAMAGE_TYPE_5, nDamage); } actPostSprite(pMissile->index, 1024); break; case 305: case 312: if (a2 == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) { if (pThingInfo && pSpriteHit->type == 400 && pXSpriteHit->burnTime == 0) evPost(nSpriteHit, 3, 0, CALLBACK_ID_0); int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDamage = (50+Random(50))<<4; actDamageSprite(nOwner, pSpriteHit, DAMAGE_TYPE_2, nDamage); } actExplodeSprite(pMissile); break; case 303: sfxKill3DSound(pMissile, -1, -1); actExplodeSprite(pMissile); break; case 301: sfxKill3DSound(pMissile, -1, -1); if (a2 == 3 && pSpriteHit) { if (pThingInfo || pDudeInfo) { int nOwner = actSpriteOwnerToSpriteId(pMissile); if ((pThingInfo && pThingInfo->at17[DAMAGE_TYPE_1] != 0) || (pDudeInfo && pDudeInfo->at70[DAMAGE_TYPE_1] != 0)) { if (pThingInfo && pSpriteHit->type == 400 && pXSpriteHit->burnTime == 0) evPost(nSpriteHit, 3, 0, CALLBACK_ID_0); actBurnSprite(pMissile->owner, pXSpriteHit, 480); sub_2A620(nOwner, pMissile->x, pMissile->y, pMissile->z, pMissile->sectnum, 16, 20, 10, DAMAGE_TYPE_2, 6, 480, 0, 0); // by NoOne: allow additional bullet damage for Flare Gun if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus()) { int nDamage = (20 + Random(10)) << 4; actDamageSprite(nOwner, pSpriteHit, DAMAGE_TYPE_2, nDamage); } } else { int nDamage = (20+Random(10))<<4; actDamageSprite(nOwner, pSpriteHit, DAMAGE_TYPE_2, nDamage); } if (surfType[pSpriteHit->picnum] == kSurfFlesh) { pMissile->picnum = 2123; pXMissile->target = nSpriteHit; pXMissile->targetZ = pMissile->z-pSpriteHit->z; pXMissile->goalAng = getangle(pMissile->x-pSpriteHit->x, pMissile->y-pSpriteHit->y)-pSpriteHit->ang; pXMissile->state = 1; actPostSprite(pMissile->index, 14); pMissile->cstat &= ~257; break; } } } GibSprite(pMissile, GIBTYPE_17, NULL, NULL); actPostSprite(pMissile->index, 1024); break; case 304: case 308: if (a2 == 3) { int nObject = gHitInfo.hitsprite; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; if (pObject->extra > 0) { XSPRITE *pXObject = &xsprite[pObject->extra]; if ((pObject->statnum == 4 || pObject->statnum == 6) && pXObject->burnTime == 0) evPost(nObject, 3, 0, CALLBACK_ID_0); int nOwner = actSpriteOwnerToSpriteId(pMissile); actBurnSprite(pMissile->owner, pXObject, (4+gGameOptions.nDifficulty)<<2); actDamageSprite(nOwner, pObject, DAMAGE_TYPE_1, 8); } } break; case 313: actExplodeSprite(pMissile); if (a2 == 3) { int nObject = gHitInfo.hitsprite; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; if (pObject->extra > 0) { XSPRITE *pXObject = &xsprite[pObject->extra]; if ((pObject->statnum == 4 || pObject->statnum == 6) && pXObject->burnTime == 0) evPost(nObject, 3, 0, CALLBACK_ID_0); int nOwner = actSpriteOwnerToSpriteId(pMissile); actBurnSprite(pMissile->owner, pXObject, (4+gGameOptions.nDifficulty)<<2); actDamageSprite(nOwner, pObject, DAMAGE_TYPE_1, 8); int nDamage = (25+Random(10))<<4; actDamageSprite(nOwner, pObject, DAMAGE_TYPE_2, nDamage); } } actExplodeSprite(pMissile); break; case 314: actExplodeSprite(pMissile); if (a2 == 3) { int nObject = gHitInfo.hitsprite; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; if (pObject->extra > 0) { XSPRITE *pXObject = &xsprite[pObject->extra]; if ((pObject->statnum == 4 || pObject->statnum == 6) && pXObject->burnTime == 0) evPost(nObject, 3, 0, CALLBACK_ID_0); int nOwner = actSpriteOwnerToSpriteId(pMissile); actBurnSprite(pMissile->owner, pXObject, 32); actDamageSprite(nOwner, pObject, DAMAGE_TYPE_5, 12); int nDamage = (25+Random(10))<<4; actDamageSprite(nOwner, pObject, DAMAGE_TYPE_2, nDamage); } } actExplodeSprite(pMissile); break; case 307: sfxKill3DSound(pMissile, -1, -1); sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 522, pMissile->sectnum); actPostSprite(pMissile->index, 15); seqSpawn(20, 3, pMissile->extra, -1); if (a2 == 3) { int nObject = gHitInfo.hitsprite; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; if (pObject->statnum == 6) { int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDamage = (25+Random(10))<<4; actDamageSprite(nOwner, pObject, DAMAGE_TYPE_5, nDamage); } } break; case 300: actPostSprite(pMissile->index, 15); pMissile->cstat &= ~16; pMissile->type = 0; seqSpawn(20, 3, pMissile->extra, -1); if (a2 == 3) { int nObject = gHitInfo.hitsprite; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; if (pObject->statnum == 6) { int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDamage = (10+Random(10))<<4; actDamageSprite(nOwner, pObject, DAMAGE_TYPE_5, nDamage); spritetype *pOwner = &sprite[nOwner]; XSPRITE *pXOwner = &xsprite[pOwner->extra]; int nType = pOwner->type-kDudeBase; if (pXOwner->health > 0) actHealDude(pXOwner, 10, dudeInfo[nType].startHealth); } } break; case 306: sfxKill3DSound(pMissile, -1, -1); sfxPlay3DSound(pMissile->x, pMissile->y, pMissile->z, 518, pMissile->sectnum); GibSprite(pMissile, (a2 == 2) ? GIBTYPE_23 : GIBTYPE_22, NULL, NULL); evKill(pMissile->index, 3); seqKill(3, nXMissile); actPostSprite(pMissile->index, 1024); if (a2 == 3) { int nObject = gHitInfo.hitsprite; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDamage = (15+Random(10))<<4; actDamageSprite(nOwner, pObject, DAMAGE_TYPE_6, nDamage); } break; case 310: default: seqKill(3, nXMissile); actPostSprite(pMissile->index, 1024); if (a2 == 3) { int nObject = gHitInfo.hitsprite; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; int nOwner = actSpriteOwnerToSpriteId(pMissile); int nDamage = (10+Random(10))<<4; actDamageSprite(nOwner, pObject, DAMAGE_TYPE_0, nDamage); } break; } pMissile->cstat &= ~257; } void actKickObject(spritetype *pSprite1, spritetype *pSprite2) { int nSprite1 = pSprite1->index; int nSprite2 = pSprite2->index; int nSpeed = ClipLow(approxDist(xvel[nSprite1], yvel[nSprite1])*2, 0xaaaaa); xvel[nSprite2] = mulscale30(nSpeed, Cos(pSprite1->ang+Random2(85))); yvel[nSprite2] = mulscale30(nSpeed, Sin(pSprite1->ang+Random2(85))); zvel[nSprite2] = mulscale(nSpeed, -0x2000, 14); pSprite2->hitag = 7; } void actTouchFloor(spritetype *pSprite, int nSector) { dassert(pSprite != NULL); dassert(nSector >= 0 && nSector < kMaxSectors); sectortype * pSector = §or[nSector]; XSECTOR * pXSector = NULL; if (pSector->extra > 0) pXSector = &xsector[pSector->extra]; if (pXSector && (pSector->lotag == 618 || pXSector->damageType > 0)) { DAMAGE_TYPE nDamageType; if (pSector->lotag == 618) nDamageType = (DAMAGE_TYPE)ClipRange(pXSector->damageType, DAMAGE_TYPE_0, DAMAGE_TYPE_6); else nDamageType = (DAMAGE_TYPE)ClipRange(pXSector->damageType - 1, DAMAGE_TYPE_0, DAMAGE_TYPE_6); int nDamage; if (pXSector->data) nDamage = ClipRange(pXSector->data, 0, 1000); else nDamage = 1000; actDamageSprite(pSprite->index, pSprite, nDamageType, scale(4, nDamage, 120) << 4); } if (tileGetSurfType(nSector + 0x4000) == kSurfLava) { actDamageSprite(pSprite->index, pSprite, DAMAGE_TYPE_1, 16); sfxPlay3DSound(pSprite, 352, 5, 2); } } void ProcessTouchObjects(spritetype *pSprite, int nXSprite) { int nSprite = pSprite->index; XSPRITE *pXSprite = &xsprite[nXSprite]; SPRITEHIT *pSpriteHit = &gSpriteHit[nXSprite]; PLAYER *pPlayer = NULL; if (IsPlayerSprite(pSprite)) pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; int nHitSprite = pSpriteHit->ceilhit & 0x3fff; switch (pSpriteHit->ceilhit&0xc000) { case 0x8000: break; case 0xc000: if (sprite[nHitSprite].extra > 0) { spritetype *pSprite2 = &sprite[nHitSprite]; XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; if ((pSprite2->statnum == 4 || pSprite2->statnum == 6) && (xvel[nSprite] != 0 || yvel[nSprite] != 0 || zvel[nSprite] != 0)) { if (pSprite2->statnum == 4) { int nType = pSprite2->type-kThingBase; THINGINFO *pThingInfo = &thingInfo[nType]; if (pThingInfo->at5&1) pSprite2->hitag |= 1; if (pThingInfo->at5&2) pSprite2->hitag |= 4; // Inlined ? xvel[pSprite2->index] += mulscale(4, pSprite2->x-sprite[nSprite].x, 2); yvel[pSprite2->index] += mulscale(4, pSprite2->y-sprite[nSprite].y, 2); } else { pSprite2->hitag |= 5; xvel[pSprite2->index] += mulscale(4, pSprite2->x-sprite[nSprite].x, 2); yvel[pSprite2->index] += mulscale(4, pSprite2->y-sprite[nSprite].y, 2); // by NoOne: add size shroom abilities if ((IsPlayerSprite(pSprite) && isShrinked(pSprite)) || (IsPlayerSprite(pSprite2) && isGrown(pSprite2))) { int mass1 = dudeInfo[pSprite2->type - kDudeBase].mass; int mass2 = dudeInfo[pSprite->type - kDudeBase].mass; switch (pSprite->type) { case kCustomDude: case kCustomDudeBurning: mass2 = getSpriteMassBySize(pSprite); break; } if (mass1 > mass2) { int dmg = abs((mass1 - mass2) * (pSprite2->clipdist - pSprite->clipdist)); if (IsDudeSprite(pSprite2)) { if (dmg > 0) actDamageSprite(pSprite2->xvel, pSprite, (Chance(0x2000)) ? DAMAGE_TYPE_0 : (Chance(0x4000)) ? DAMAGE_TYPE_3 : DAMAGE_TYPE_2, dmg); if (Chance(0x0200)) actKickObject(pSprite2, pSprite); } } } if (!IsPlayerSprite(pSprite) || gPlayer[pSprite->type - kDudePlayer1].at31a == 0) { switch (pSprite2->type) { case 229: actDamageSprite(pSprite2->index, pSprite, DAMAGE_TYPE_3, pXSprite->health << 2); break; case kCustomDude: case kCustomDudeBurning: int dmg = (getSpriteMassBySize(pSprite2) - getSpriteMassBySize(pSprite)) + pSprite2->clipdist; if (dmg > 0) { if (IsPlayerSprite(pSprite) && powerupCheck(&gPlayer[pSprite->type - kDudePlayer1],15) > 0) actDamageSprite(pSprite2->xvel, pSprite, DAMAGE_TYPE_3, dmg); else actDamageSprite(pSprite2->xvel, pSprite, DAMAGE_TYPE_0, dmg); } if (!IsPlayerSprite(pSprite) && pSprite2->extra >= 0 && !isActive(pSprite2->xvel)) aiActivateDude(pSprite2, &xsprite[pSprite2->extra]); break; } } } } if (pSprite2->type == 454) { if (pXSprite2->state) { pXSprite2->data1 = 1; pXSprite2->data2 = ClipHigh(pXSprite2->data2+8, 600); actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_2, 16); } else actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_2, 1); } } break; } nHitSprite = pSpriteHit->hit & 0x3fff; switch (pSpriteHit->hit&0xc000) { case 0x8000: break; case 0xc000: if (sprite[nHitSprite].extra > 0) { spritetype *pSprite2 = &sprite[nHitSprite]; //XSPRITE *pXSprite2 = &Xsprite[pSprite2->extra]; // by NoOne: add size shroom abilities if ((IsPlayerSprite(pSprite2) && isShrinked(pSprite2)) || (IsPlayerSprite(pSprite) && isGrown(pSprite))) { if (xvel[pSprite->xvel] != 0 && IsDudeSprite(pSprite2)) { int mass1 = dudeInfo[pSprite->type - kDudeBase].mass; int mass2 = dudeInfo[pSprite2->type - kDudeBase].mass; switch (pSprite2->type) { case kCustomDude: case kCustomDudeBurning: mass2 = getSpriteMassBySize(pSprite2); break; } if (mass1 > mass2) { actKickObject(pSprite, pSprite2); sfxPlay3DSound(pSprite, 357, -1, 1); int dmg = (mass1 - mass2) + abs(xvel[pSprite->xvel] >> 16); if (dmg > 0) actDamageSprite(nSprite, pSprite2, (Chance(0x2000)) ? DAMAGE_TYPE_0 : DAMAGE_TYPE_2, dmg); } } } switch (pSprite->type) { case kCustomDude: case kCustomDudeBurning: { if (IsDudeSprite(pSprite2) && !IsPlayerSprite(pSprite2)) { int mass1 = getSpriteMassBySize(pSprite); int mass2 = getSpriteMassBySize(pSprite2); if ((mass1 - mass2) >= mass2) { if ((pXSprite->target == pSprite2->xvel && !dudeIsMelee(pXSprite) && Chance(0x0500)) || pXSprite->target != pSprite2->xvel) actKickObject(pSprite, pSprite2); if (pSprite2->extra >= 0 && !isActive(pSprite2->xvel)) aiActivateDude(pSprite2, &xsprite[pSprite2->extra]); } } break; } } switch (pSprite2->type) { case 415: actKickObject(pSprite, pSprite2); break; case 427: sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 357, pSprite->sectnum); actKickObject(pSprite, pSprite2); actDamageSprite(-1, pSprite2, DAMAGE_TYPE_0, 80); break; case 239: case 240: case 241: case 242: // This does not make sense pXSprite->burnTime = ClipLow(pXSprite->burnTime-4, 0); actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, DAMAGE_TYPE_1, 8); break; } } break; } nHitSprite = pSpriteHit->florhit & 0x3fff; switch (pSpriteHit->florhit&0xc000) { case 0x8000: break; case 0x4000: actTouchFloor(pSprite, nHitSprite); break; case 0xc000: if (sprite[nHitSprite].extra > 0) { spritetype *pSprite2 = &sprite[nHitSprite]; XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; // by NoOne: add size shroom abilities if ((IsPlayerSprite(pSprite2) && isShrinked(pSprite2)) || (IsPlayerSprite(pSprite) && isGrown(pSprite))) { int mass1 = dudeInfo[pSprite->type - kDudeBase].mass; int mass2 = dudeInfo[pSprite2->type - kDudeBase].mass; switch (pSprite2->type) { case kCustomDude: case kCustomDudeBurning: mass2 = getSpriteMassBySize(pSprite2); break; } if (mass1 > mass2 && IsDudeSprite(pSprite2)) { if ((IsPlayerSprite(pSprite2) && Chance(0x500)) || !IsPlayerSprite(pSprite2)) actKickObject(pSprite, pSprite2); int dmg = (mass1 - mass2) + pSprite->clipdist; if (dmg > 0) actDamageSprite(nSprite, pSprite2, (Chance(0x2000)) ? DAMAGE_TYPE_0 : DAMAGE_TYPE_2, dmg); } } switch (pSprite->type) { case kCustomDude: case kCustomDudeBurning: { if (IsDudeSprite(pSprite2) && !IsPlayerSprite(pSprite2)) { int mass1 = getSpriteMassBySize(pSprite); int mass2 = getSpriteMassBySize(pSprite2); if ((mass1 - mass2) >= mass2) { if (Chance((pXSprite->target == pSprite2->xvel) ? 0x0500 : 0x1000)) actKickObject(pSprite, pSprite2); if (pSprite2->extra >= 0 && !isActive(pSprite2->xvel)) aiActivateDude(pSprite2, &xsprite[pSprite2->extra]); } } break; } } switch (pSprite2->type) { case 415: if (pPlayer) { if (pPlayer->at30e > gFrameClock) return; pPlayer->at30e = (int)gFrameClock+60; } actKickObject(pSprite, pSprite2); sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 357, pSprite->sectnum); sfxPlay3DSound(pSprite, 374, 0, 0); break; case 427: if (pPlayer) { if (pPlayer->at30e > gFrameClock) return; pPlayer->at30e = (int)gFrameClock+60; } actKickObject(pSprite, pSprite2); sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, 357, pSprite->sectnum); actDamageSprite(-1, pSprite2, DAMAGE_TYPE_0, 80); break; case 454: if (pXSprite2->state) { pXSprite2->data1 = 1; pXSprite2->data2 = ClipHigh(pXSprite2->data2+8, 600); actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_2, 16); } else actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_2, 1); break; case 201: case 202: case 203: case 204: case 205: case 206: case 207: case 210: case 211: case 212: case 213: case 214: case 215: case 217: case 219: case 220: case 221: case 222: case 223: case 224: case 225: case 226: case 227: case 228: case 229: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: if (pPlayer && !isShrinked(pSprite)) actDamageSprite(nSprite, pSprite2,DAMAGE_TYPE_2, 8); break; } } break; } // by NoOne: add more trigger statements for Touch flag if (!VanillaMode()) { // Touch sprites int nHSprite = -1; if ((gSpriteHit[nXSprite].hit & 0xc000) == 0xc000) nHSprite = gSpriteHit[nXSprite].hit & 0x3fff; else if ((gSpriteHit[nXSprite].florhit & 0xc000) == 0xc000) nHSprite = gSpriteHit[nXSprite].florhit & 0x3fff; else if ((gSpriteHit[nXSprite].ceilhit & 0xc000) == 0xc000) nHSprite = gSpriteHit[nXSprite].ceilhit & 0x3fff; if (nHSprite >= 0 && sprite[nHSprite].extra >= 0) { XSPRITE* pXHSprite = &xsprite[sprite[nHSprite].extra]; if (pXHSprite->Touch && !pXHSprite->isTriggered && (!pXHSprite->DudeLockout || IsPlayerSprite(pSprite))) trTriggerSprite(nHSprite, pXHSprite, 33); } // Touch walls int nHWall = -1; if ((gSpriteHit[nXSprite].hit & 0xc000) == 0x8000) { if ((nHWall = gSpriteHit[nXSprite].hit & 0x3fff) >= 0 && wall[nHWall].extra >= 0) { XWALL* pXHWall = &xwall[wall[nHWall].extra]; if (pXHWall->triggerTouch && !pXHWall->isTriggered && (!pXHWall->dudeLockout || IsPlayerSprite(pSprite))) trTriggerWall(nHWall, pXHWall, 52); } } } } void actAirDrag(spritetype *pSprite, int a2) { int vbp = 0; int v4 = 0; int nSector = pSprite->sectnum; dassert(nSector >= 0 && nSector < kMaxSectors); sectortype *pSector = §or[nSector]; int nXSector = pSector->extra; if (nXSector > 0) { dassert(nXSector < kMaxXSectors); XSECTOR *pXSector = &xsector[nXSector]; if (pXSector->windVel && (pXSector->windAlways || pXSector->busy)) { int vcx = pXSector->windVel<<12; if (!pXSector->windAlways && pXSector->busy) vcx = mulscale16(vcx, pXSector->busy); vbp = mulscale30(vcx, Cos(pXSector->windAng)); v4 = mulscale30(vcx, Sin(pXSector->windAng)); } } xvel[pSprite->index] += mulscale16(vbp-xvel[pSprite->index], a2); yvel[pSprite->index] += mulscale16(v4-yvel[pSprite->index], a2); zvel[pSprite->index] -= mulscale16(zvel[pSprite->index], a2); } int MoveThing(spritetype *pSprite) { int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pSprite->index; int v8 = 0; dassert(pSprite->type >= kThingBase && pSprite->type < kThingMax); THINGINFO *pThingInfo = &thingInfo[pSprite->type-kThingBase]; int nSector = pSprite->sectnum; dassert(nSector >= 0 && nSector < kMaxSectors); int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); if (xvel[nSprite] || yvel[nSprite]) { short bakCstat = pSprite->cstat; pSprite->cstat &= ~257; v8 = gSpriteHit[nXSprite].hit = ClipMove((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, pSprite->clipdist<<2, (pSprite->z-top)/4, (bottom-pSprite->z)/4, CLIPMASK0); pSprite->cstat = bakCstat; dassert(nSector >= 0); if (pSprite->sectnum != nSector) { dassert(nSector >= 0 && nSector < kMaxSectors); ChangeSpriteSect(nSprite, nSector); } if ((gSpriteHit[nXSprite].hit&0xc000) == 0x8000) { int nHitWall = gSpriteHit[nXSprite].hit&0x3fff; actWallBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], nHitWall, pThingInfo->at7); switch (pSprite->type) { case 427: sfxPlay3DSound(pSprite, 607, 0, 0); actDamageSprite(-1, pSprite, DAMAGE_TYPE_0, 80); break; case 415: sfxPlay3DSound(pSprite, 374, 0, 0); break; } } } else { dassert(nSector >= 0 && nSector < kMaxSectors); FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector); } if (zvel[nSprite]) pSprite->z += zvel[nSprite]>>8; int ceilZ, ceilHit, floorZ, floorHit; GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0); GetSpriteExtents(pSprite, &top, &bottom); if ((pSprite->hitag & 2) && bottom < floorZ) { pSprite->z += 455; zvel[nSprite] += 58254; if (pSprite->type == 427) { spritetype *pFX = gFX.fxSpawn(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); if (pFX) { int v34 = ((int)gFrameClock*3)&2047; int v30 = ((int)gFrameClock*5)&2047; int vbx = ((int)gFrameClock*11)&2047; int v2c = 0x44444; int v28 = 0; int v24 = 0; RotateVector(&v2c,&v28,vbx); RotateVector(&v2c,&v24,v30); RotateVector(&v28,&v24,v34); xvel[pFX->index] = xvel[pSprite->index]+v2c; yvel[pFX->index] = yvel[pSprite->index]+v28; zvel[pFX->index] = zvel[pSprite->index]+v24; } } } if (CheckLink(pSprite)) GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0); GetSpriteExtents(pSprite, &top, &bottom); if (bottom >= floorZ) { actTouchFloor(pSprite, pSprite->sectnum); gSpriteHit[nXSprite].florhit = floorHit; pSprite->z += floorZ-bottom; int v20 = zvel[nSprite]-velFloor[pSprite->sectnum]; if (v20 > 0) { pSprite->hitag |= 4; int vax = actFloorBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], (int*)&v20, pSprite->sectnum, pThingInfo->at7); int nDamage = mulscale(vax, vax, 30)-pThingInfo->atb; if (nDamage > 0) actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_0, nDamage); zvel[nSprite] = v20; if (velFloor[pSprite->sectnum] == 0 && klabs(zvel[nSprite]) < 0x10000) { zvel[nSprite] = 0; pSprite->hitag &= ~4; } switch (pSprite->type) { case 428: if (zvel[nSprite] == 0 || Chance(0xA000)) sub_2AA94(pSprite, pXSprite); break; case 427: if (klabs(zvel[nSprite]) > 0x80000) { sfxPlay3DSound(pSprite, 607, 0, 0); actDamageSprite(-1, pSprite, DAMAGE_TYPE_0, 80); } break; case 415: if (klabs(zvel[nSprite]) > 0x80000) sfxPlay3DSound(pSprite, 374, 0, 0); break; } v8 = 0x4000|nSector; } else if (zvel[nSprite] == 0) pSprite->hitag &= ~4; } else { gSpriteHit[nXSprite].florhit = 0; if (pSprite->hitag&2) pSprite->hitag |= 4; } if (top <= ceilZ) { gSpriteHit[nXSprite].ceilhit = ceilHit; pSprite->z += ClipLow(ceilZ-top, 0); if (zvel[nSprite] < 0) { xvel[nSprite] = mulscale16(xvel[nSprite], 0xc000); yvel[nSprite] = mulscale16(yvel[nSprite], 0xc000); zvel[nSprite] = mulscale16(-zvel[nSprite], 0x4000); switch (pSprite->type) { case 427: if (klabs(zvel[nSprite]) > 0x80000) { sfxPlay3DSound(pSprite, 607, 0, 0); actDamageSprite(-1, pSprite, DAMAGE_TYPE_0, 80); } break; case 415: if (klabs(zvel[nSprite]) > 0x80000) sfxPlay3DSound(pSprite, 374, 0, 0); break; } } } else gSpriteHit[nXSprite].ceilhit = 0; if (bottom >= floorZ) { int nVel = approxDist(xvel[nSprite], yvel[nSprite]); int nVelClipped = ClipHigh(nVel, 0x11111); if ((floorHit & 0xc000) == 0xc000) { int nHitSprite = floorHit & 0x3fff; if ((sprite[nHitSprite].cstat & 0x30) == 0) { xvel[nSprite] += mulscale(4, pSprite->x - sprite[nHitSprite].x, 2); yvel[nSprite] += mulscale(4, pSprite->y - sprite[nHitSprite].y, 2); v8 = gSpriteHit[nXSprite].hit; } } if (nVel > 0) { int t = divscale16(nVelClipped, nVel); xvel[nSprite] -= mulscale16(t, xvel[nSprite]); yvel[nSprite] -= mulscale16(t, yvel[nSprite]); } } if (xvel[nSprite] || yvel[nSprite]) pSprite->ang = getangle(xvel[nSprite], yvel[nSprite]); return v8; } void MoveDude(spritetype *pSprite) { int nXSprite = pSprite->extra; XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pSprite->index; PLAYER *pPlayer = NULL; if (IsPlayerSprite(pSprite)) pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type-kDudeBase]; int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int bz = (bottom-pSprite->z)/4; int tz = (pSprite->z-top)/4; int wd = pSprite->clipdist<<2; int nSector = pSprite->sectnum; dassert(nSector >= 0 && nSector < kMaxSectors); if (xvel[nSprite] || yvel[nSprite]) { if (pPlayer && gNoClip) { pSprite->x += xvel[nSprite]>>12; pSprite->y += yvel[nSprite]>>12; if (!FindSector(pSprite->x, pSprite->y, &nSector)) nSector = pSprite->sectnum; } else { short bakCstat = pSprite->cstat; pSprite->cstat &= ~257; gSpriteHit[nXSprite].hit = ClipMove((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, wd, tz, bz, CLIPMASK0); if (nSector == -1) { nSector = pSprite->sectnum; if (pSprite->statnum == 6 || pSprite->statnum == 4) actDamageSprite(pSprite->index, pSprite, DAMAGE_TYPE_0, 1000<<4); } if (sector[nSector].lotag >= 612 && sector[nSector].lotag <= 617) { short nSector2 = nSector; if (pushmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector2, wd, tz, bz, CLIPMASK0) == -1) actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_0, 1000 << 4); if (nSector2 != -1) nSector = nSector2; } dassert(nSector >= 0); pSprite->cstat = bakCstat; } switch (gSpriteHit[nXSprite].hit&0xc000) { case 0xc000: { int nHitSprite = gSpriteHit[nXSprite].hit&0x3fff; spritetype *pHitSprite = &sprite[nHitSprite]; XSPRITE *pHitXSprite = NULL; // Should be pHitSprite here if (pSprite->extra > 0) pHitXSprite = &xsprite[pHitSprite->extra]; int nOwner = actSpriteOwnerToSpriteId(pHitSprite); if (pHitSprite->statnum == 5 && !(pHitSprite->hitag&32) && pSprite->index != nOwner) { HITINFO hitInfo = gHitInfo; gHitInfo.hitsprite = nSprite; actImpactMissile(pHitSprite, 3); gHitInfo = hitInfo; } // by NoOne: this is why touch for things never worked; they always ON if (pHitXSprite && pHitXSprite->Touch /*&& !pHitXSprite->state*/ && !pHitXSprite->isTriggered) { if (!pHitXSprite->DudeLockout || IsPlayerSprite(pSprite)) // allow dudeLockout for Touch flag trTriggerSprite(nHitSprite, pHitXSprite, 33); } if (pDudeInfo->lockOut && pHitXSprite && pHitXSprite->Push && !pHitXSprite->key && !pHitXSprite->DudeLockout && !pHitXSprite->state && !pHitXSprite->busy && !pPlayer) trTriggerSprite(nHitSprite, pHitXSprite, 30); break; } case 0x8000: { int nHitWall = gSpriteHit[nXSprite].hit&0x3fff; walltype *pHitWall = &wall[nHitWall]; XWALL *pHitXWall = NULL; if (pHitWall->extra > 0) pHitXWall = &xwall[pHitWall->extra]; if (pDudeInfo->lockOut && pHitXWall && pHitXWall->triggerPush && !pHitXWall->key && !pHitXWall->dudeLockout && !pHitXWall->state && !pHitXWall->busy && !pPlayer) trTriggerWall(nHitWall, pHitXWall, 50); if (pHitWall->nextsector != -1) { sectortype *pHitSector = §or[pHitWall->nextsector]; XSECTOR *pHitXSector = NULL; if (pHitSector->extra > 0) pHitXSector = &xsector[pHitSector->extra]; if (pDudeInfo->lockOut && pHitXSector && pHitXSector->Wallpush && !pHitXSector->Key && !pHitXSector->at37_7 && !pHitXSector->state && !pHitXSector->busy && !pPlayer) trTriggerSector(pHitWall->nextsector, pHitXSector, 40); if (top < pHitSector->ceilingz || bottom > pHitSector->floorz) { // ??? } } actWallBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], nHitWall, 0); break; } } } else { dassert(nSector >= 0 && nSector < kMaxSectors); FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector); } if (pSprite->sectnum != nSector) { dassert(nSector >= 0 && nSector < kMaxSectors); XSECTOR *pXSector; int nXSector = sector[pSprite->sectnum].extra; if (nXSector > 0) pXSector = &xsector[nXSector]; else pXSector = NULL; if (pXSector && pXSector->Exit && (pPlayer || !pXSector->at37_7)) trTriggerSector(pSprite->sectnum, pXSector, 43); ChangeSpriteSect(nSprite, nSector); nXSector = sector[nSector].extra; if (nXSector > 0) pXSector = &xsector[nXSector]; else pXSector = NULL; if (pXSector && pXSector->Enter && (pPlayer || !pXSector->at37_7)) { if (sector[nSector].lotag == 604) pXSector->data = pPlayer ? nSprite : -1; trTriggerSector(nSector, pXSector, 42); } nSector = pSprite->sectnum; } char bUnderwater = 0; char bDepth = 0; if (sector[nSector].extra > 0) { XSECTOR *pXSector = &xsector[sector[nSector].extra]; if (pXSector->Underwater) bUnderwater = 1; if (pXSector->Depth) bDepth = 1; } int nUpperLink = gUpperLink[nSector]; int nLowerLink = gLowerLink[nSector]; if (nUpperLink >= 0 && (sprite[nUpperLink].type == 9 || sprite[nUpperLink].type == 13)) bDepth = 1; if (nLowerLink >= 0 && (sprite[nLowerLink].type == 10 || sprite[nLowerLink].type == 14)) bDepth = 1; if (pPlayer) wd += 16; if (zvel[nSprite]) pSprite->z += zvel[nSprite]>>8; int ceilZ, ceilHit, floorZ, floorHit; GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR); GetSpriteExtents(pSprite, &top, &bottom); if (pSprite->hitag & 2) { int vc = 58254; if (bDepth) { if (bUnderwater) { int cz = getceilzofslope(nSector, pSprite->x, pSprite->y); if (cz > top) vc += ((bottom-cz)*-80099) / (bottom-top); else vc = 0; } else { int fz = getflorzofslope(nSector, pSprite->x, pSprite->y); if (fz < bottom) vc += ((bottom-fz)*-80099) / (bottom-top); } } else { if (bUnderwater) vc = 0; else if (bottom >= floorZ) vc = 0; } if (vc) { pSprite->z += ((vc*4)/2)>>8; zvel[nSprite] += vc; } } if (pPlayer && zvel[nSprite] > 0x155555 && !pPlayer->at31b && pXSprite->height > 0) { pPlayer->at31b = 1; sfxPlay3DSound(pSprite, 719, 0, 0); } vec3_t const oldpos = pSprite->pos; int nLink = CheckLink(pSprite); if (nLink) { GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR); if (pPlayer) { if (bVanilla) playerResetInertia(pPlayer); else playerCorrectInertia(pPlayer, &oldpos); } switch (nLink) { case 12: if (pPlayer == gView) SetBitString(gotpic, sector[pSprite->sectnum].floorpicnum); break; case 11: if (pPlayer == gView) SetBitString(gotpic, sector[pSprite->sectnum].ceilingpicnum); break; case 10: case 14: pXSprite->medium = 0; if (pPlayer) { pPlayer->at2f = 0; pPlayer->at302 = 0; if (!pPlayer->at31c && pPlayer->atc.buttonFlags.jump) { zvel[nSprite] = -0x6aaaa; pPlayer->at31c = 1; } sfxPlay3DSound(pSprite, 721, -1, 0); } else { switch (pSprite->type) { case 201: case 202: aiNewState(pSprite, pXSprite, &cultistGoto); break; case 217: aiNewState(pSprite, pXSprite, &gillBeastGoto); pSprite->hitag |= 6; break; case 218: actKillDude(pSprite->index, pSprite, DAMAGE_TYPE_0, 1000<<4); break; } } break; // By NoOne: part of "change of global view palette for stacks" feature case kMarkerUpWater: case kMarkerUpGoo: { pXSprite->medium = nLink == kMarkerUpGoo ? 2 : 1; if (pPlayer) { // look for palette in data2 of marker. If value <= 0, use default ones. pPlayer->nWaterPal = 0; int nXUpper = sprite[gUpperLink[nSector]].extra; if (nXUpper >= 0) pPlayer->nWaterPal = xsprite[nXUpper].data2; pPlayer->at2f = 1; pXSprite->burnTime = 0; pPlayer->at302 = klabs(zvel[nSprite]) >> 12; evPost(nSprite, 3, 0, CALLBACK_ID_10); sfxPlay3DSound(pSprite, 720, -1, 0); } else { switch (pSprite->type) { case 201: case 202: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &cultistSwimGoto); break; case 240: { // There is no difference between water and goo except following chance: if (Chance(nLink == kMarkerUpGoo ? 0x400 : 0xa00)) { pSprite->type = 201; pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &cultistSwimGoto); } else { pSprite->type = 202; pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &cultistSwimGoto); } break; } case 203: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &zombieAGoto); break; case 204: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &zombieFGoto); break; case 217: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &gillBeastSwimGoto); pSprite->hitag &= ~6; break; case 206: case 211: case 213: case 214: case 215: case 219: case 220: case 239: actKillDude(pSprite->index, pSprite, DAMAGE_TYPE_0, 1000 << 4); break; case kCustomDude: evPost(nSprite, 3, 0, CALLBACK_ID_11); if (!canSwim(pSprite)) actKillDude(pSprite->index, pSprite, DAMAGE_TYPE_0, 1000 << 4); break; } } break; } /*case 13: pXSprite->medium = 2; if (pPlayer) { pPlayer->changeTargetKin = 1; pXSprite->burnTime = 0; pPlayer->at302 = klabs(zvel[nSprite])>>12; evPost(nSprite, 3, 0, CALLBACK_ID_10); sfxPlay3DSound(pSprite, 720, -1, 0); } else { switch (pSprite->type) { case 201: case 202: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &cultistSwimGoto); break; case 240: if (Chance(0x400)) { pSprite->type = 201; pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &cultistSwimGoto); } else { pSprite->type = 202; pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &cultistSwimGoto); } break; case 203: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &zombieAGoto); break; case 204: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &zombieFGoto); break; case 217: pXSprite->burnTime = 0; evPost(nSprite, 3, 0, CALLBACK_ID_11); sfxPlay3DSound(pSprite, 720, -1, 0); aiNewState(pSprite, pXSprite, &gillBeastSwimGoto); pSprite->hitag &= ~6; break; case 206: case 211: case 213: case 214: case 215: case 219: case 220: case 239: actKillDude(pSprite->index, pSprite, DAMAGE_TYPE_0, 1000<<4); break; } } break;*/ } } GetSpriteExtents(pSprite, &top, &bottom); if (pPlayer && bottom >= floorZ) { int floorZ2 = floorZ; int floorHit2 = floorHit; GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR); if (bottom <= floorZ && pSprite->z - floorZ2 < bz) { floorZ = floorZ2; floorHit = floorHit2; } } if (floorZ <= bottom) { gSpriteHit[nXSprite].florhit = floorHit; pSprite->z += floorZ-bottom; int v30 = zvel[nSprite]-velFloor[pSprite->sectnum]; if (v30 > 0) { int vax = actFloorBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], (int*)&v30, pSprite->sectnum, 0); int nDamage = mulscale(vax, vax, 30); if (pPlayer) { pPlayer->at31b = 0; if (nDamage > (15<<4) && (pSprite->hitag&4)) playerLandingSound(pPlayer); if (nDamage > (30<<4)) sfxPlay3DSound(pSprite, 701, 0, 0); } nDamage -= 100<<4; if (nDamage > 0) actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_0, nDamage); zvel[nSprite] = v30; if (klabs(zvel[nSprite]) < 0x10000) { zvel[nSprite] = velFloor[pSprite->sectnum]; pSprite->hitag &= ~4; } else pSprite->hitag |= 4; switch (tileGetSurfType(floorHit)) { case kSurfWater: gFX.fxSpawn(FX_9, pSprite->sectnum, pSprite->x, pSprite->y, floorZ, 0); break; case kSurfLava: { spritetype *pFX = gFX.fxSpawn(FX_10, pSprite->sectnum, pSprite->x, pSprite->y, floorZ, 0); if (pFX) { for (int i = 0; i < 7; i++) { spritetype *pFX2 = gFX.fxSpawn(FX_14, pFX->sectnum, pFX->x, pFX->y, pFX->z, 0); if (pFX2) { xvel[pFX2->index] = Random2(0x6aaaa); yvel[pFX2->index] = Random2(0x6aaaa); zvel[pFX2->index] = -Random(0xd5555); } } } break; } } } else if (zvel[nSprite] == 0) pSprite->hitag &= ~4; } else { gSpriteHit[nXSprite].florhit = 0; if (pSprite->hitag&2) pSprite->hitag |= 4; } if (top <= ceilZ) { gSpriteHit[nXSprite].ceilhit = ceilHit; pSprite->z += ClipLow(ceilZ-top, 0); if (zvel[nSprite] <= 0 && (pSprite->hitag&4)) zvel[nSprite] = mulscale16(-zvel[nSprite], 0x2000); } else gSpriteHit[nXSprite].ceilhit = 0; GetSpriteExtents(pSprite,&top,&bottom); pXSprite->height = ClipLow(floorZ-bottom, 0)>>8; if (xvel[nSprite] || yvel[nSprite]) { if ((floorHit & 0xc000) == 0xc000) { int nHitSprite = floorHit & 0x3fff; if ((sprite[nHitSprite].cstat & 0x30) == 0) { xvel[nSprite] += mulscale(4, pSprite->x - sprite[nHitSprite].x, 2); yvel[nSprite] += mulscale(4, pSprite->y - sprite[nHitSprite].y, 2); return; } } int nXSector = sector[pSprite->sectnum].extra; if (nXSector > 0 && xsector[nXSector].Underwater) return; if (pXSprite->height >= 0x100) return; int nDrag = gDudeDrag; if (pXSprite->height > 0) nDrag -= scale(gDudeDrag, pXSprite->height, 0x100); xvel[nSprite] -= mulscale16r(xvel[nSprite], nDrag); yvel[nSprite] -= mulscale16r(yvel[nSprite], nDrag); if (approxDist(xvel[nSprite], yvel[nSprite]) < 0x1000) xvel[nSprite] = yvel[nSprite] = 0; } } int MoveMissile(spritetype *pSprite) { int nXSprite = pSprite->extra; XSPRITE *pXSprite = &xsprite[nXSprite]; int vdi = -1; spritetype *pOwner = NULL; int bakCstat = 0; if (pSprite->owner >= 0) { int nOwner = actSpriteOwnerToSpriteId(pSprite); pOwner = &sprite[nOwner]; if (IsDudeSprite(pOwner)) { bakCstat = pOwner->cstat; pOwner->cstat &= ~257; } else pOwner = NULL; } gHitInfo.hitsect = -1; gHitInfo.hitwall = -1; gHitInfo.hitsprite = -1; if (pSprite->type == 304) actAirDrag(pSprite, 0x1000); int nSprite = pSprite->index; if (pXSprite->target != -1 && (xvel[nSprite] || yvel[nSprite] || zvel[nSprite])) { spritetype *pTarget = &sprite[pXSprite->target]; XSPRITE *pXTarget; if (pTarget->extra > 0) pXTarget = &xsprite[pTarget->extra]; else pXTarget = NULL; if (pTarget->statnum == 6 && pXTarget && pXTarget->health > 0) { int nTargetAngle = getangle(-(pTarget->y-pSprite->y), pTarget->x-pSprite->x); int UNUSED(nAngle) = getangle(xvel[nSprite]>>12,yvel[nSprite]>>12); int vx = missileInfo[pSprite->type-300].at2; int vy = 0; RotatePoint(&vx, &vy, (nTargetAngle+1536)&2047, 0, 0); xvel[nSprite] = vx; yvel[nSprite] = vy; int dx = pTarget->x-pSprite->x; int dy = pTarget->y-pSprite->y; int dz = pTarget->z-pSprite->z; // Inlined int vax = dz/10; if (pTarget->z < pSprite->z) vax = -vax; zvel[nSprite] += vax; ksqrt(dx*dx+dy*dy+dz*dz); } } int vx = xvel[nSprite]>>12; int vy = yvel[nSprite]>>12; int vz = zvel[nSprite]>>8; int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int i = 1; while (1) { int x = pSprite->x; int y = pSprite->y; int z = pSprite->z; int nSector2 = pSprite->sectnum; clipmoveboxtracenum = 1; int vdx = ClipMove(&x, &y, &z, &nSector2, vx, vy, pSprite->clipdist<<2, (z-top)/4, (bottom-z)/4, CLIPMASK0); clipmoveboxtracenum = 3; short nSector = nSector2; if (nSector2 < 0) { vdi = -1; break; } if (vdx) { int nHitSprite = vdx & 0x3fff; if ((vdx&0xc000) == 0xc000) { gHitInfo.hitsprite = nHitSprite; vdi = 3; } else if ((vdx & 0xc000) == 0x8000) { gHitInfo.hitwall = nHitSprite; if (wall[nHitSprite].nextsector == -1) vdi = 0; else { int32_t fz, cz; getzsofslope(wall[nHitSprite].nextsector, x, y, &cz, &fz); if (z <= cz || z >= fz) vdi = 0; else vdi = 4; } } } if (vdi == 4) { walltype *pWall = &wall[gHitInfo.hitwall]; if (pWall->extra > 0) { XWALL *pXWall = &xwall[pWall->extra]; if (pXWall->triggerVector) { trTriggerWall(gHitInfo.hitwall, pXWall, 51); if (!(pWall->cstat&64)) { vdi = -1; if (i-- > 0) continue; vdi = 0; break; } } } } if (vdi >= 0 && vdi != 3) { int nAngle = getangle(xvel[nSprite], yvel[nSprite]); x -= mulscale30(Cos(nAngle), 16); y -= mulscale30(Sin(nAngle), 16); int nVel = approxDist(xvel[nSprite], yvel[nSprite]); vz -= scale(0x100, zvel[nSprite], nVel); updatesector(x, y, &nSector); nSector2 = nSector; } int ceilZ, ceilHit, floorZ, floorHit; GetZRangeAtXYZ(x, y, z, nSector2, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0); GetSpriteExtents(pSprite, &top, &bottom); top += vz; bottom += vz; if (bottom >= floorZ) { gSpriteHit[nXSprite].florhit = floorHit; vz += floorZ-bottom; vdi = 2; } if (top <= ceilZ) { gSpriteHit[nXSprite].ceilhit = ceilHit; vz += ClipLow(ceilZ-top, 0); vdi = 1; } pSprite->x = x; pSprite->y = y; pSprite->z = z+vz; updatesector(x, y, &nSector); if (nSector >= 0 && nSector != pSprite->sectnum) { dassert(nSector >= 0 && nSector < kMaxSectors); ChangeSpriteSect(nSprite, nSector); } CheckLink(pSprite); gHitInfo.hitsect = pSprite->sectnum; gHitInfo.hitx = pSprite->x; gHitInfo.hity = pSprite->y; gHitInfo.hitz = pSprite->z; break; } if (pOwner) pOwner->cstat = bakCstat; return vdi; } void actExplodeSprite(spritetype *pSprite) { int nXSprite = pSprite->extra; if (nXSprite <= 0 || nXSprite >= kMaxXSprites) return; if (pSprite->statnum == 2) return; sfxKill3DSound(pSprite, -1, -1); evKill(pSprite->index, 3); int nType; switch (pSprite->type) { case 312: nType = 7; seqSpawn(4, 3, nXSprite, -1); if (Chance(0x8000)) pSprite->cstat |= 4; sfxPlay3DSound(pSprite, 303, -1, 0); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); break; case 303: nType = 3; seqSpawn(9, 3, nXSprite, -1); if (Chance(0x8000)) pSprite->cstat |= 4; sfxPlay3DSound(pSprite, 306, 24+(pSprite->index&3), 1); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); break; case 313: case 314: nType = 3; seqSpawn(5, 3, nXSprite, -1); sfxPlay3DSound(pSprite, 304, -1, 0); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); break; case 418: nType = 0; if (gSpriteHit[nXSprite].florhit == 0) seqSpawn(4,3,nXSprite,-1); else seqSpawn(3,3,nXSprite,-1); sfxPlay3DSound(pSprite, 303, -1, 0); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); break; case 401: case 402: case 419: case kGDXThingTNTProx: nType = 1; if (gSpriteHit[nXSprite].florhit == 0) seqSpawn(4,3,nXSprite,-1); else seqSpawn(3,3,nXSprite,-1); sfxPlay3DSound(pSprite, 304, -1, 0); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); break; case 420: nType = 4; seqSpawn(5, 3, nXSprite, -1); sfxPlay3DSound(pSprite, 307, -1, 0); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); break; case 400: { spritetype *pSprite2 = actSpawnSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0, 1); pSprite2->owner = pSprite->owner; if (actCheckRespawn(pSprite)) { XSPRITE *pXSprite = &xsprite[nXSprite]; pXSprite->state = 1; pXSprite->health = thingInfo[0].at0<<4; } else actPostSprite(pSprite->index, 1024); nType = 2; nXSprite = pSprite2->extra; seqSpawn(4, 3, nXSprite, -1); sfxPlay3DSound(pSprite2, 305, -1, 0); GibSprite(pSprite2, GIBTYPE_14, NULL, NULL); pSprite = pSprite2; break; } case 459: // By NoOne: allow to customize hidden exploder thing { // Defaults for exploder nType = 1; int nSnd = 304; int nSeq = 4; // Temp variables for override via data fields int tSnd = 0; int tSeq = 0; XSPRITE *pXSPrite = &xsprite[nXSprite]; nType = pXSPrite->data1; // Explosion type tSeq = pXSPrite->data2; // SEQ id tSnd = pXSPrite->data3; // Sound Id if (nType <= 1 || nType > kExplodeMax) { nType = 1; nSeq = 4; nSnd = 304; } else if (nType == 2) { nSeq = 4; nSnd = 305; } else if (nType == 3) { nSeq = 9; nSnd = 307; } else if (nType == 4) { nSeq = 5; nSnd = 307; } else if (nType <= 6) { nSeq = 4; nSnd = 303; } else if (nType == 7) { nSeq = 4; nSnd = 303; } else if (nType == 8) { nType = 0; nSeq = 3; nSnd = 303; } // Override previous sound and seq assigns if (tSeq > 0) nSeq = tSeq; if (tSnd > 0) nSnd = tSnd; if (gSysRes.Lookup(pXSPrite->data2, "SEQ")) seqSpawn(nSeq, 3, nXSprite, -1); sfxPlay3DSound(pSprite, nSnd, -1, 0); } break; case 429: nType = 3; seqSpawn(9, 3, nXSprite, -1); sfxPlay3DSound(pSprite, 307, -1, 0); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); sub_746D4(pSprite, 240); break; default: nType = 1; seqSpawn(4, 3, nXSprite, -1); if (Chance(0x8000)) pSprite->cstat |= 4; sfxPlay3DSound(pSprite, 303, -1, 0); GibSprite(pSprite, GIBTYPE_5, NULL, NULL); break; } int nSprite = pSprite->index; xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0; actPostSprite(nSprite, 2); pSprite->xrepeat = pSprite->yrepeat = explodeInfo[nType].at0; pSprite->hitag &= ~3; pSprite->type = nType; EXPLOSION *pExplodeInfo = &explodeInfo[nType]; xsprite[nXSprite].target = 0; xsprite[nXSprite].data1 = pExplodeInfo->atf; xsprite[nXSprite].data2 = pExplodeInfo->at13; xsprite[nXSprite].data3 = pExplodeInfo->at17; } void actActivateGibObject(spritetype *pSprite, XSPRITE *pXSprite) { int vdx = ClipRange(pXSprite->data1, 0, 31); int vc = ClipRange(pXSprite->data2, 0, 31); int v4 = ClipRange(pXSprite->data3, 0, 31); int vbp = pXSprite->data4; int v8 = pXSprite->dropMsg; if (vdx > 0) GibSprite(pSprite, (GIBTYPE)(vdx-1), NULL, NULL); if (vc > 0) GibSprite(pSprite, (GIBTYPE)(vc-1), NULL, NULL); if (v4 > 0 && pXSprite->burnTime > 0) GibSprite(pSprite, (GIBTYPE)(v4-1), NULL, NULL); if (vbp > 0) sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, vbp, pSprite->sectnum); if (v8 > 0) actDropObject(pSprite, v8); if (!(pSprite->cstat&32768) && !(pSprite->hitag&16)) actPostSprite(pSprite->index, 1024); } bool IsUnderWater(spritetype *pSprite) { int nSector = pSprite->sectnum; int nXSector = sector[nSector].extra; if (nXSector > 0 && nXSector < kMaxXSectors) if (xsector[nXSector].Underwater) return 1; return 0; } void MakeSplash(spritetype *pSprite, XSPRITE *pXSprite); void actProcessSprites(void) { int nSprite; int nNextSprite; if (!VanillaMode()) { // by NoOne: process additional proximity sprites if (gProxySpritesCount > 0) { for (int i = 0; i < gProxySpritesCount; i++) { if (sprite[gProxySpritesList[i]].extra < 0) continue; XSPRITE * pXProxSpr = &xsprite[sprite[gProxySpritesList[i]].extra]; if (!pXProxSpr->Proximity || (!pXProxSpr->Interrutable && pXProxSpr->state != pXProxSpr->restState) || pXProxSpr->locked == 1 || pXProxSpr->isTriggered) continue; // don't process locked or triggered sprites int x = sprite[gProxySpritesList[i]].x; int y = sprite[gProxySpritesList[i]].y; int z = sprite[gProxySpritesList[i]].z; int index = sprite[gProxySpritesList[i]].xvel; int sectnum = sprite[gProxySpritesList[i]].sectnum; if (!pXProxSpr->DudeLockout) { for (int nAffected = headspritestat[6]; nAffected >= 0; nAffected = nextspritestat[nAffected]) { if ((sprite[nAffected].hitag & 32) || xsprite[sprite[nAffected].extra].health <= 0) continue; else if (CheckProximity(&sprite[nAffected], x, y, z, sectnum, 96)) { trTriggerSprite(index, pXProxSpr, 35); break; } } } else { for (int a = connecthead; a >= 0; a = connectpoint2[a]) { if (gPlayer[a].pXSprite->health > 0 && CheckProximity(gPlayer[a].pSprite, x, y, z, sectnum, 96)) { trTriggerSprite(index, pXProxSpr, 35); break; } } } } } // by NoOne: process sight sprites (for players only) if (gSightSpritesCount > 0) { for (int i = 0; i < gSightSpritesCount; i++) { if (sprite[gSightSpritesList[i]].extra < 0) continue; XSPRITE * pXSightSpr = &xsprite[sprite[gSightSpritesList[i]].extra]; if (!pXSightSpr->Sight || (!pXSightSpr->Interrutable && pXSightSpr->state != pXSightSpr->restState) || pXSightSpr->locked == 1 || pXSightSpr->isTriggered) continue; // don't process locked or triggered sprites int x = sprite[gSightSpritesList[i]].x; int y = sprite[gSightSpritesList[i]].y; int z = sprite[gSightSpritesList[i]].z; int index = sprite[gSightSpritesList[i]].xvel; int sectnum = sprite[gSightSpritesList[i]].sectnum; for (int a = connecthead; a >= 0; a = connectpoint2[a]) { spritetype* pPlaySprite = gPlayer[a].pSprite; if (gPlayer[a].pXSprite->health > 0 && cansee(x, y, z, sectnum, pPlaySprite->x, pPlaySprite->y, pPlaySprite->z, pPlaySprite->sectnum)) { trTriggerSprite(index, pXSightSpr, 34); break; } } } } // by NoOne: process Debris sprites for movement if (gPhysSpritesCount > 0) { //System.err.println("PHYS COUNT: "+gPhysSpritesCount); for (int i = 0; i < gPhysSpritesCount; i++) { if (gPhysSpritesList[i] == -1) continue; else if (sprite[gPhysSpritesList[i]].statnum == kStatFree || (sprite[gPhysSpritesList[i]].hitag & kHitagFree) != 0) { gPhysSpritesList[i] = -1; continue; } XSPRITE* pXDebris = &xsprite[sprite[gPhysSpritesList[i]].extra]; if (!(pXDebris->physAttr & kPhysMove) && !(pXDebris->physAttr & kPhysGravity)) { gPhysSpritesList[i] = -1; continue; } spritetype* pDebris = &sprite[gPhysSpritesList[i]]; XSECTOR* pXSector = (sector[pDebris->sectnum].extra >= 0) ? &xsector[sector[pDebris->sectnum].extra] : NULL; viewBackupSpriteLoc(pDebris->xvel, pDebris); int airVel = gSpriteMass[pDebris->extra].airVel; if (pXSector != NULL) { if (pXSector->Underwater) airVel <<= 6; if (pXSector->panVel != 0) { int top, bottom; GetSpriteExtents(pDebris,&top,&bottom); if (getflorzofslope(pDebris->sectnum, pDebris->x, pDebris->y) <= bottom) { int angle = pXSector->panAngle; int speed = 0; if (pXSector->panAlways || pXSector->state || pXSector->busy) { speed = pXSector->panVel << 9; if (!pXSector->panAlways && pXSector->busy) speed = mulscale16(speed, pXSector->busy); } if (sector[pDebris->sectnum].floorstat & 64) angle = (angle + GetWallAngle(sector[pDebris->sectnum].wallptr) + 512) & 2047; int dx = mulscale30(speed, Cos(angle)); int dy = mulscale30(speed, Sin(angle)); xvel[pDebris->xvel] += dx; yvel[pDebris->xvel] += dy; } } } actAirDrag(pDebris, airVel); if (((pDebris->index >> 8) & 15) == (gFrame & 15) && (pXDebris->physAttr & kPhysGravity)) pXDebris->physAttr |= kPhysFalling; if ((pXDebris->physAttr & 4) == 0 && xvel[pDebris->xvel] == 0 && yvel[pDebris->xvel] == 0 && zvel[pDebris->xvel] == 0 && velFloor[pDebris->sectnum] == 0 && velCeil[pDebris->sectnum] == 0) continue; debrisMove(i); } } } for (nSprite = headspritestat[4]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag&32) continue; int nXSprite = pSprite->extra; if (nXSprite > 0) { XSPRITE *pXSprite = &xsprite[nXSprite]; if (pSprite->type == 425 || pSprite->type == 426 || pSprite->type == 427) if (pXSprite->locked && gFrameClock >= pXSprite->targetX) pXSprite->locked = 0; if (pXSprite->burnTime > 0) { pXSprite->burnTime = ClipLow(pXSprite->burnTime-4,0); actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, DAMAGE_TYPE_1, 8); } // by NoOne: don't process locked or 1-shot things for proximity if (pXSprite->Proximity && (VanillaMode() || (pXSprite->locked != 1 && pXSprite->isTriggered != true))) { if (pSprite->type == 431) pXSprite->target = -1; for (int nSprite2 = headspritestat[6]; nSprite2 >= 0; nSprite2 = nNextSprite) { nNextSprite = nextspritestat[nSprite2]; spritetype *pSprite2 = &sprite[nSprite2]; if (pSprite2->hitag&32) continue; XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; if ((unsigned int)pXSprite2->health > 0) { // by NoOne: allow dudeLockout for proximity flag if (pSprite->type != 431 && pXSprite->DudeLockout && !IsPlayerSprite(pSprite2)) continue; int proxyDist = 96; if (pSprite->type == kGDXThingCustomDudeLifeLeech) proxyDist = 512; else if (pSprite->type == 431 && pXSprite->target == -1) { int nOwner = actOwnerIdToSpriteId(pSprite->owner); spritetype *pOwner = &sprite[nOwner]; PLAYER *pPlayer = &gPlayer[pOwner->type-kDudePlayer1]; PLAYER *pPlayer2 = NULL; if (IsPlayerSprite(pSprite2)) pPlayer2 = &gPlayer[pSprite2->type-kDudePlayer1]; if (nSprite2 == nOwner || pSprite2->type == 205 || pSprite2->type == 220 || pSprite2->type == 219) continue; if (gGameOptions.nGameType == 3 && pPlayer2 && pPlayer->at2ea == pPlayer2->at2ea) continue; if (gGameOptions.nGameType == 1 && pPlayer2) continue; proxyDist = 512; } if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, proxyDist)) { switch (pSprite->type) { case kGDXThingTNTProx: if (!IsPlayerSprite(pSprite2)) continue; pSprite->pal = 0; break; case 431: if (!Chance(0x4000) && nNextSprite >= 0) continue; if (pSprite2->cstat & CLIPMASK0) pXSprite->target = pSprite2->index; else continue; break; case kGDXThingCustomDudeLifeLeech: if (pXSprite->target != pSprite2->xvel) continue; break; } if (pSprite->owner == -1) actPropagateSpriteOwner(pSprite, pSprite2); trTriggerSprite(nSprite, pXSprite, 35); } } } } } } for (nSprite = headspritestat[4]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag & 32) continue; int nSector = pSprite->sectnum; int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); int nXSector = sector[nSector].extra; XSECTOR *pXSector = NULL; if (nXSector > 0) { dassert(nXSector > 0 && nXSector < kMaxXSectors); dassert(xsector[nXSector].reference == nSector); pXSector = &xsector[nXSector]; } if (pXSector && pXSector->panVel && (pXSector->panAlways || pXSector->state || pXSector->busy)) { int nType = pSprite->type - kThingBase; THINGINFO *pThingInfo = &thingInfo[nType]; if (pThingInfo->at5 & 1) pSprite->hitag |= 1; if (pThingInfo->at5 & 2) pSprite->hitag |= 4; } if (pSprite->hitag&3) { viewBackupSpriteLoc(nSprite, pSprite); if (pXSector && pXSector->panVel) { int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom) { int angle = pXSector->panAngle; int speed = 0; if (pXSector->panAlways || pXSector->state || pXSector->busy) { speed = pXSector->panVel << 9; if (!pXSector->panAlways && pXSector->busy) speed = mulscale16(speed, pXSector->busy); } if (sector[nSector].floorstat&64) angle = (angle+GetWallAngle(sector[nSector].wallptr)+512)&2047; int dx = mulscale30(speed, Cos(angle)); int dy = mulscale30(speed, Sin(angle)); xvel[nSprite] += dx; yvel[nSprite] += dy; } } actAirDrag(pSprite, 128); if (((pSprite->index>>8)&15) == (gFrame&15) && (pSprite->hitag&2)) pSprite->hitag |= 4; if ((pSprite->hitag&4) || xvel[nSprite] || yvel[nSprite] || zvel[nSprite] || velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum]) { int hit = MoveThing(pSprite); if (hit) { int nXSprite = pSprite->extra; if (nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; if (pXSprite->Impact) trTriggerSprite(nSprite, pXSprite, 0); switch (pSprite->type) { case 423: case 424: MakeSplash(pSprite, pXSprite); break; case kGDXThingThrowableRock: seqSpawn(24, 3, nXSprite, -1); if ((hit & 0xc000) == 0xc000) { pSprite->xrepeat = 32; pSprite->yrepeat = 32; int nObject = hit & 0x3fff; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype * pObject = &sprite[nObject]; actDamageSprite(actSpriteOwnerToSpriteId(pSprite), pObject, DAMAGE_TYPE_0, pXSprite->data1); } break; case 421: seqSpawn(24, 3, nXSprite, -1); if ((hit&0xc000) == 0xc000) { int nObject = hit & 0x3fff; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; actDamageSprite(actSpriteOwnerToSpriteId(pSprite), pObject, DAMAGE_TYPE_0, 12); } break; case 430: if ((hit&0xc000) == 0x4000) { sub_2A620(actSpriteOwnerToSpriteId(pSprite), pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 200, 1, 20, DAMAGE_TYPE_3, 6, 0, 0, 0); evPost(pSprite->index, 3, 0, CALLBACK_ID_19); } else { int nObject = hit & 0x3fff; if ((hit&0xc000) != 0xc000 && (nObject < 0 || nObject >= 4096)) break; dassert(nObject >= 0 && nObject < kMaxSprites); spritetype *pObject = &sprite[nObject]; actDamageSprite(actSpriteOwnerToSpriteId(pSprite), pObject, DAMAGE_TYPE_0, 12); evPost(pSprite->index, 3, 0, CALLBACK_ID_19); } break; case 429: { int nObject = hit & 0x3fff; if ((hit&0xc000) != 0xc000 && (nObject < 0 || nObject >= 4096)) break; dassert(nObject >= 0 && nObject < kMaxSprites); int UNUSED(nOwner) = actSpriteOwnerToSpriteId(pSprite); actExplodeSprite(pSprite); break; } } } } } } } for (nSprite = headspritestat[5]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag & 32) continue; viewBackupSpriteLoc(nSprite, pSprite); int hit = MoveMissile(pSprite); if (hit >= 0) actImpactMissile(pSprite, hit); } for (nSprite = headspritestat[2]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { char v24c[(kMaxSectors+7)>>3]; spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag & 32) continue; int nOwner = actSpriteOwnerToSpriteId(pSprite); int nType = pSprite->type; dassert(nType >= 0 && nType < kExplodeMax); EXPLOSION *pExplodeInfo = &explodeInfo[nType]; int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE *pXSprite = &xsprite[nXSprite]; int x = pSprite->x; int y = pSprite->y; int z = pSprite->z; int nSector = pSprite->sectnum; gAffectedSectors[0] = -1; gAffectedXWalls[0] = -1; // By NoOne: Allow to override explosion radius by data4 field of any sprite which have statnum 2 set in editor // or of Hidden Exploder. int radius = pXSprite->data4; if (pXSprite->data4 <= 0) radius = pExplodeInfo->radius; GetClosestSpriteSectors(nSector, x, y, radius, gAffectedSectors, v24c, gAffectedXWalls); for (int i = 0; i < kMaxXWalls; i++) { int nWall = gAffectedXWalls[i]; if (nWall == -1) break; XWALL *pXWall = &xwall[wall[nWall].extra]; trTriggerWall(nWall, pXWall, 51); } for (int nSprite2 = headspritestat[6]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2]) { spritetype *pDude = &sprite[nSprite2]; if (pDude->hitag & 32) continue; if (TestBitString(v24c, pDude->sectnum)) { if (pXSprite->data1 && CheckProximity(pDude, x, y, z, nSector, radius)) { if (pExplodeInfo->at1 && pXSprite->target == 0) { pXSprite->target = 1; actDamageSprite(nOwner, pDude, DAMAGE_TYPE_0, (pExplodeInfo->at1+Random(pExplodeInfo->at2))<<4); } if (pExplodeInfo->dmgType) ConcussSprite(nOwner, pDude, x, y, z, pExplodeInfo->dmgType); if (pExplodeInfo->atb) { dassert(pDude->extra > 0 && pDude->extra < kMaxXSprites); XSPRITE *pXDude = &xsprite[pDude->extra]; if (!pXDude->burnTime) evPost(nSprite2, 3, 0, CALLBACK_ID_0); actBurnSprite(pSprite->owner, pXDude, pExplodeInfo->atb<<2); } } } } for (int nSprite2 = headspritestat[4]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2]) { spritetype *pThing = &sprite[nSprite2]; if (pThing->hitag & 32) continue; if (TestBitString(v24c, pThing->sectnum)) { if (pXSprite->data1 && CheckProximity(pThing, x, y, z, nSector, radius)) { XSPRITE *pXSprite2 = &xsprite[pThing->extra]; if (!pXSprite2->locked) { if (pExplodeInfo->dmgType) ConcussSprite(nOwner, pThing, x, y, z, pExplodeInfo->dmgType); if (pExplodeInfo->atb) { dassert(pThing->extra > 0 && pThing->extra < kMaxXSprites); XSPRITE *pXThing = &xsprite[pThing->extra]; if (pThing->type == 400 && !pXThing->burnTime) evPost(nSprite2, 3, 0, CALLBACK_ID_0); actBurnSprite(pSprite->owner, pXThing, pExplodeInfo->atb<<2); } } } } } // by NoOne: add impulse for sprites from physics list if (gPhysSpritesCount > 0 && pExplodeInfo->dmgType != 0 && pXSprite->data1 != 0) { for (int i = 0; i < gPhysSpritesCount; i++) { if (gPhysSpritesList[i] == -1) continue; else if (sprite[gPhysSpritesList[i]].sectnum < 0 || (sprite[gPhysSpritesList[i]].hitag & kHitagFree) != 0) continue; spritetype* pDebris = &sprite[gPhysSpritesList[i]]; if (!TestBitString(v24c, pDebris->sectnum) || !CheckProximity(pDebris, x, y, z, nSector, radius)) continue; else debrisConcuss(nOwner, i, x, y, z, pExplodeInfo->dmgType); } } for (int p = connecthead; p >= 0; p = connectpoint2[p]) { spritetype *pSprite2 = gPlayer[p].pSprite; int dx = (x - pSprite2->x)>>4; int dy = (y - pSprite2->y)>>4; int dz = (z - pSprite2->z)>>8; int nDist = dx*dx+dy*dy+dz*dz+0x40000; int t = divscale16(pXSprite->data2, nDist); gPlayer[p].at35a += t; } // By NoOne: if data4 > 0, do not remove explosion. This can be useful when designer wants put explosion generator in map manually // via sprite statnum 2. if (!(pSprite->hitag & kModernTypeFlag1)) { pXSprite->data1 = ClipLow(pXSprite->data1 - 4, 0); pXSprite->data2 = ClipLow(pXSprite->data2 - 4, 0); pXSprite->data3 = ClipLow(pXSprite->data3 - 4, 0); } if (pXSprite->data1 == 0 && pXSprite->data2 == 0 && pXSprite->data3 == 0 && seqGetStatus(3, nXSprite) < 0) actPostSprite(nSprite, 1024); } for (nSprite = headspritestat[11]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag & 32) continue; int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE *pXSprite = &xsprite[nXSprite]; switch (pSprite->type) { case 454: pXSprite->data2 = ClipLow(pXSprite->data2-4, 0); break; case 452: if (pXSprite->state && seqGetStatus(3, nXSprite) < 0) { int x = pSprite->x; int y = pSprite->y; int z = pSprite->z; int t = (pXSprite->data1<<23)/120; int dx = mulscale30(t, Cos(pSprite->ang)); int dy = mulscale30(t, Sin(pSprite->ang)); for (int i = 0; i < 2; i++) { spritetype *pFX = gFX.fxSpawn(FX_32, pSprite->sectnum, x, y, z, 0); if (pFX) { xvel[pFX->index] = dx + Random2(0x8888); yvel[pFX->index] = dy + Random2(0x8888); zvel[pFX->index] = Random2(0x8888); } x += (dx/2)>>12; y += (dy/2)>>12; } dy = Sin(pSprite->ang)>>16; dx = Cos(pSprite->ang)>>16; gVectorData[VECTOR_TYPE_20].maxDist = pXSprite->data1<<9; actFireVector(pSprite, 0, 0, dx, dy, Random2(0x8888), VECTOR_TYPE_20); } break; } } for (nSprite = headspritestat[6]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag & 32) continue; int nXSprite = pSprite->extra; if (nXSprite > 0) { XSPRITE *pXSprite = &xsprite[nXSprite]; if (pXSprite->burnTime > 0) { switch (pSprite->type) { case 239: case 240: case 241: case 242: actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, DAMAGE_TYPE_1, 8); break; default: pXSprite->burnTime = ClipLow(pXSprite->burnTime-4, 0); actDamageSprite(actOwnerIdToSpriteId(pXSprite->burnSource), pSprite, DAMAGE_TYPE_1, 8); break; } } // By NoOne: handle incarnations of custom dude if (pSprite->type == kCustomDude && pXSprite->txID > 0 && pXSprite->health <= 0 && seqGetStatus(3, nXSprite) < 0) { XSPRITE* pXIncarnation = getNextIncarnation(pXSprite); if (pXIncarnation != NULL) { spritetype* pIncarnation = &sprite[pXIncarnation->reference]; pXSprite->key = pXSprite->dropMsg = pXSprite->locked = 0; // save incarnation's going on and off options bool triggerOn = pXIncarnation->triggerOn; bool triggerOff = pXIncarnation->triggerOff; // then remove it from incarnation so it will not send the commands pXIncarnation->triggerOn = false; pXIncarnation->triggerOff = false; // trigger dude death before transform trTriggerSprite(nSprite, pXSprite, COMMAND_ID_0); pSprite->lotag = pIncarnation->lotag; pSprite->hitag = pIncarnation->hitag; pSprite->pal = pIncarnation->pal; pSprite->shade = pIncarnation->shade; pSprite->clipdist = pIncarnation->clipdist; pSprite->xrepeat = pIncarnation->xrepeat; pSprite->yrepeat = pIncarnation->yrepeat; pXSprite->txID = pXIncarnation->txID; pXSprite->command = pXIncarnation->command; pXSprite->triggerOn = triggerOn; pXSprite->triggerOff = triggerOff; pXSprite->busyTime = pXIncarnation->busyTime; pXSprite->waitTime = pXIncarnation->waitTime; pXSprite->burnTime = 0; pXSprite->burnSource = -1; pXSprite->data1 = pXIncarnation->data1; short oldData2 = pXSprite->data2; pXSprite->data2 = pXIncarnation->data2; // seq new seqId and save old one. // if incarnation is active dude, it's sndStartId will be stored in sysData1, otherwise it will be data3 if (pIncarnation->statnum == 6 && pIncarnation->type == kCustomDude) pXSprite->sysData1 = pXIncarnation->sysData1; else pXIncarnation->data3; pXSprite->data4 = pXIncarnation->data4; pXSprite->dudeGuard = pXIncarnation->dudeGuard; pXSprite->dudeDeaf = pXIncarnation->dudeDeaf; pXSprite->dudeAmbush = pXIncarnation->dudeAmbush; pXSprite->dudeFlag4 = pXIncarnation->dudeFlag4; pXSprite->dropMsg = pXIncarnation->dropMsg; pXSprite->key = pXIncarnation->key; pXSprite->locked = pXIncarnation->locked; pXSprite->Decoupled = pXIncarnation->Decoupled; // clear drop items of the incarnation pXIncarnation->key = pXIncarnation->dropMsg = 0; // set hp if (pXSprite->data4 <= 0) pXSprite->health = dudeInfo[pSprite->type - kDudeBase].startHealth << 4; else pXSprite->health = ClipRange(pXSprite->data4 << 4, 1, 65535); int seqId = dudeInfo[pSprite->type - kDudeBase].seqStartID; switch (pSprite->type) { case 225: // fake dude case 226: // fake dude break; case kCustomDude: case kCustomDudeBurning: seqId = getSeqStartId(pXSprite); if (seqId != oldData2) getSpriteMassBySize(pSprite); // create or refresh mass cache fallthrough__; // go below default: seqSpawn(seqId, 3, nXSprite, -1); // save target int target = pXSprite->target; // re-init sprite aiInitSprite(pSprite); // try to restore target if (target == -1) aiSetTarget(pXSprite, pSprite->x, pSprite->y, pSprite->z); else aiSetTarget(pXSprite, target); // finally activate it aiActivateDude(pSprite, pXSprite); break; } // remove the incarnation in case if non-locked if (pXIncarnation->locked == 0) { pXIncarnation->txID = pIncarnation->lotag = 0; actPostSprite(pIncarnation->xvel, kStatFree); // or restore triggerOn and off options } else { pXIncarnation->triggerOn = triggerOn; pXIncarnation->triggerOff = triggerOff; } } else { // just trigger dude death trTriggerSprite(nSprite, pXSprite, COMMAND_ID_0); } } if (pSprite->type == 227) { if (pXSprite->health <= 0 && seqGetStatus(3, nXSprite) < 0) { pXSprite->health = dudeInfo[28].startHealth<<4; pSprite->type = 228; if (pXSprite->target != -1) aiSetTarget(pXSprite, pXSprite->target); aiActivateDude(pSprite, pXSprite); } } if (pXSprite->Proximity && !pXSprite->isTriggered) { for (int nSprite2 = headspritestat[6]; nSprite2 >= 0; nSprite2 = nNextSprite) { nNextSprite = nextspritestat[nSprite2]; spritetype *pSprite2 = &sprite[nSprite2]; if (pSprite2->hitag&32) continue; XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; if ((unsigned int)pXSprite2->health > 0 && pSprite2->type >= kDudePlayer1 && pSprite2->type <= kDudePlayer8) { if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 128)) { trTriggerSprite(nSprite, pXSprite, 35); } } } } if (IsPlayerSprite(pSprite)) { PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; if (pPlayer->at34e) sub_41250(pPlayer); if (pPlayer->at376 && Chance(0x8000)) actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_4, 12); if (pPlayer->at87) { char bActive = packItemActive(pPlayer, 1); if (bActive || pPlayer->at31a) pPlayer->at2f2 = 1200; else pPlayer->at2f2 = ClipLow(pPlayer->at2f2-4, 0); if (pPlayer->at2f2 < 1080 && packCheckItem(pPlayer, 1) && !bActive) packUseItem(pPlayer, 1); if (!pPlayer->at2f2) { pPlayer->at36e += 4; if (Chance(pPlayer->at36e)) actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_4, 3<<4); } else pPlayer->at36e = 0; if (xvel[nSprite] || yvel[nSprite]) sfxPlay3DSound(pSprite, 709, 100, 2); pPlayer->at302 = ClipLow(pPlayer->at302-4, 0); } else if (gGameOptions.nGameType == 0) { if (pPlayer->pXSprite->health > 0 && pPlayer->at30a >= 1200 && Chance(0x200)) { pPlayer->at30a = -1; sfxPlay3DSound(pSprite, 3100+Random(11), 0, 2); } } } ProcessTouchObjects(pSprite, nXSprite); } } for (nSprite = headspritestat[6]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag & 32) continue; int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); int nSector = pSprite->sectnum; viewBackupSpriteLoc(nSprite, pSprite); int nXSector = sector[nSector].extra; XSECTOR *pXSector = NULL; if (nXSector > 0) { dassert(nXSector > 0 && nXSector < kMaxXSectors); dassert(xsector[nXSector].reference == nSector); pXSector = &xsector[nXSector]; } if (pXSector) { int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom) { int angle = pXSector->panAngle; int speed = 0; if (pXSector->panAlways || pXSector->state || pXSector->busy) { speed = pXSector->panVel << 9; if (!pXSector->panAlways && pXSector->busy) speed = mulscale16(speed, pXSector->busy); } if (sector[nSector].floorstat&64) angle = (angle+GetWallAngle(sector[nSector].wallptr)+512)&2047; int dx = mulscale30(speed, Cos(angle)); int dy = mulscale30(speed, Sin(angle)); xvel[nSprite] += dx; yvel[nSprite] += dy; } } if (pXSector && pXSector->Underwater) actAirDrag(pSprite, 5376); else actAirDrag(pSprite, 128); if ((pSprite->hitag&4) || xvel[nSprite] || yvel[nSprite] || zvel[nSprite] || velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum]) MoveDude(pSprite); } for (nSprite = headspritestat[14]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; if (pSprite->hitag & 32) continue; int nXSprite = pSprite->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); XSPRITE *pXSprite = &xsprite[nXSprite]; int nTarget = pXSprite->target; dassert(nTarget >= 0); viewBackupSpriteLoc(nSprite, pSprite); dassert(nTarget < kMaxSprites); spritetype *pTarget = &sprite[nTarget]; if (pTarget->statnum == kMaxStatus) { GibSprite(pSprite, GIBTYPE_17, NULL, NULL); actPostSprite(pSprite->index, 1024); } if (pTarget->extra > 0 && xsprite[pTarget->extra].health > 0) { int x = pTarget->x+mulscale30r(Cos(pXSprite->goalAng+pTarget->ang), pTarget->clipdist*2); int y = pTarget->y+mulscale30r(Sin(pXSprite->goalAng+pTarget->ang), pTarget->clipdist*2); int z = pTarget->z+pXSprite->targetZ; vec3_t pos = { x, y, z }; setsprite(nSprite,&pos); xvel[nSprite] = xvel[nTarget]; yvel[nSprite] = yvel[nTarget]; zvel[nSprite] = zvel[nTarget]; } else { GibSprite(pSprite, GIBTYPE_17, NULL, NULL); actPostSprite(pSprite->index, 1024); } } aiProcessDudes(); gFX.fxProcess(); } spritetype * actSpawnSprite(int nSector, int x, int y, int z, int nStat, char a6) { int nSprite = InsertSprite(nSector, nStat); if (nSprite >= 0) sprite[nSprite].extra = -1; else { nSprite = headspritestat[9]; dassert(nSprite >= 0); dassert(nSector >= 0 && nSector < kMaxSectors); ChangeSpriteSect(nSprite, nSector); actPostSprite(nSprite, nStat); } vec3_t pos = { x, y, z }; setsprite(nSprite, &pos); spritetype *pSprite = &sprite[nSprite]; pSprite->type = 0; if (a6 && pSprite->extra == -1) { int nXSprite = dbInsertXSprite(nSprite); gSpriteHit[nXSprite].florhit = 0; gSpriteHit[nXSprite].ceilhit = 0; } return pSprite; } spritetype * actSpawnSprite(spritetype *pSource, int nStat); spritetype *actSpawnDude(spritetype *pSource, short nType, int a3, int a4) { XSPRITE* pXSource = &xsprite[pSource->extra]; spritetype *pSprite2 = actSpawnSprite(pSource, 6); if (!pSprite2) return NULL; XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; int angle = pSource->ang; int nDude = nType-kDudeBase; int x, y, z; z = a4 + pSource->z; if (a3 < 0) { x = pSource->x; y = pSource->y; } else { x = pSource->x+mulscale30r(Cos(angle), a3); y = pSource->y+mulscale30r(Sin(angle), a3); } pSprite2->type = nType; pSprite2->ang = angle; vec3_t pos = { x, y, z }; setsprite(pSprite2->index, &pos); pSprite2->cstat |= 0x1101; pSprite2->clipdist = dudeInfo[nDude].clipdist; pXSprite2->health = dudeInfo[nDude].startHealth<<4; if (gSysRes.Lookup(dudeInfo[nDude].seqStartID, "SEQ")) seqSpawn(dudeInfo[nDude].seqStartID, 3, pSprite2->extra, -1); // By NoOne: add a way to inherit some values of spawner type 18 by dude. // This way designer can count enemies via switches and do many other interesting things. if (pSource->hitag & kModernTypeFlag1) { switch (pSource->type) { // allow inheriting only for selected source types case 18: //inherit pal? if (pSprite2->pal <= 0) pSprite2->pal = pSource->pal; // inherit spawn sprite trigger settings, so designer can count monsters. pXSprite2->txID = pXSource->txID; pXSprite2->command = pXSource->command; pXSprite2->triggerOn = pXSource->triggerOn; pXSprite2->triggerOff = pXSource->triggerOff; // inherit drop items pXSprite2->dropMsg = pXSource->dropMsg; // inherit dude flags pXSprite2->dudeDeaf = pXSource->dudeDeaf; pXSprite2->dudeGuard = pXSource->dudeGuard; pXSprite2->dudeAmbush = pXSource->dudeAmbush; pXSprite2->dudeFlag4 = pXSource->dudeFlag4; break; } } aiInitSprite(pSprite2); return pSprite2; } spritetype * actSpawnSprite(spritetype *pSource, int nStat) { int nSprite = InsertSprite(pSource->sectnum, nStat); if (nSprite < 0) { nSprite = headspritestat[9]; dassert(nSprite >= 0); dassert(pSource->sectnum >= 0 && pSource->sectnum < kMaxSectors); ChangeSpriteSect(nSprite, pSource->sectnum); actPostSprite(nSprite, nStat); } spritetype *pSprite = &sprite[nSprite]; pSprite->x = pSource->x; pSprite->y = pSource->y; pSprite->z = pSource->z; xvel[nSprite] = xvel[pSource->index]; yvel[nSprite] = yvel[pSource->index]; zvel[nSprite] = zvel[pSource->index]; pSprite->hitag = 0; int nXSprite = dbInsertXSprite(nSprite); gSpriteHit[nXSprite].florhit = 0; gSpriteHit[nXSprite].ceilhit = 0; return pSprite; } spritetype * actSpawnThing(int nSector, int x, int y, int z, int nThingType) { dassert(nThingType >= kThingBase && nThingType < kThingMax); spritetype *pSprite = actSpawnSprite(nSector, x, y, z, 4, 1); int nType = nThingType-kThingBase; int nThing = pSprite->index; int nXThing = pSprite->extra; pSprite->type = nThingType; dassert(nXThing > 0 && nXThing < kMaxXSprites); XSPRITE *pXThing = &xsprite[nXThing]; THINGINFO *pThingInfo = &thingInfo[nType]; pXThing->health = pThingInfo->at0<<4; pSprite->clipdist = pThingInfo->at4; pSprite->hitag = pThingInfo->at5; if (pSprite->hitag & 2) pSprite->hitag |= 4; pSprite->cstat |= pThingInfo->atf; pSprite->picnum = pThingInfo->at11; pSprite->shade = pThingInfo->at13; pSprite->pal = pThingInfo->at14; if (pThingInfo->at15) pSprite->xrepeat = pThingInfo->at15; if (pThingInfo->at16) pSprite->yrepeat = pThingInfo->at16; SetBitString(show2dsprite, pSprite->index); switch (nThingType) { case 432: pXThing->data1 = 0; pXThing->data2 = 0; pXThing->data3 = 0; pXThing->data4 = 0; pXThing->state = 1; pXThing->triggerOnce = 1; pXThing->isTriggered = 0; break; case 431: case kGDXThingCustomDudeLifeLeech: pXThing->data1 = 0; pXThing->data2 = 0; pXThing->data3 = 0; pXThing->data4 = 0; pXThing->state = 1; pXThing->triggerOnce = 0; pXThing->isTriggered = 0; break; case 427: pXThing->data1 = 8; pXThing->data2 = 0; pXThing->data3 = 0; pXThing->data4 = 318; pXThing->targetX = (int)gFrameClock+180.0; pXThing->locked = 1; pXThing->state = 1; pXThing->triggerOnce = 0; pXThing->isTriggered = 0; break; case 425: case 426: if (nThingType == 425) pXThing->data1 = 19; else if (nThingType == 426) pXThing->data1 = 8; pXThing->data2 = 0; pXThing->data3 = 0; pXThing->data4 = 318; pXThing->targetX = (int)gFrameClock+180.0; pXThing->locked = 1; pXThing->state = 1; pXThing->triggerOnce = 0; pXThing->isTriggered = 0; break; case 418: evPost(nThing, 3, 0, CALLBACK_ID_8); sfxPlay3DSound(pSprite, 450, 0, 0); break; case 419: sfxPlay3DSound(pSprite, 450, 0, 0); evPost(nThing, 3, 0, CALLBACK_ID_8); break; case 420: evPost(nThing, 3, 0, CALLBACK_ID_8); break; } return pSprite; } spritetype * actFireThing(spritetype *pSprite, int a2, int a3, int a4, int thingType, int a6) { dassert(thingType >= kThingBase && thingType < kThingMax); int x = pSprite->x+mulscale30(a2, Cos(pSprite->ang+512)); int y = pSprite->y+mulscale30(a2, Sin(pSprite->ang+512)); int z = pSprite->z+a3; x += mulscale28(pSprite->clipdist, Cos(pSprite->ang)); y += mulscale28(pSprite->clipdist, Sin(pSprite->ang)); if (HitScan(pSprite, z, x-pSprite->x, y-pSprite->y, 0, CLIPMASK0, pSprite->clipdist) != -1) { x = gHitInfo.hitx-mulscale28(pSprite->clipdist<<1, Cos(pSprite->ang)); y = gHitInfo.hity-mulscale28(pSprite->clipdist<<1, Sin(pSprite->ang)); } spritetype *pThing = actSpawnThing(pSprite->sectnum, x, y, z, thingType); actPropagateSpriteOwner(pThing, pSprite); pThing->ang = pSprite->ang; xvel[pThing->index] = mulscale30(a6, Cos(pThing->ang)); yvel[pThing->index] = mulscale30(a6, Sin(pThing->ang)); zvel[pThing->index] = mulscale(a6, a4, 14); xvel[pThing->index] += xvel[pSprite->index]/2; yvel[pThing->index] += yvel[pSprite->index]/2; zvel[pThing->index] += zvel[pSprite->index]/2; return pThing; } spritetype* actFireMissile(spritetype *pSprite, int a2, int a3, int a4, int a5, int a6, int nType) { dassert(nType >= kMissileBase && nType < kMissileMax); char v4 = 0; int nSprite = pSprite->index; MissileType *pMissileInfo = &missileInfo[nType-kMissileBase]; int x = pSprite->x+mulscale30(a2, Cos(pSprite->ang+512)); int y = pSprite->y+mulscale30(a2, Sin(pSprite->ang+512)); int z = pSprite->z+a3; int clipdist = pMissileInfo->atd+pSprite->clipdist; x += mulscale28(clipdist, Cos(pSprite->ang)); y += mulscale28(clipdist, Sin(pSprite->ang)); int hit = HitScan(pSprite, z, x-pSprite->x, y-pSprite->y, 0, CLIPMASK0, clipdist); if (hit != -1) { if (hit == 3 || hit == 0) { v4 = 1; x = gHitInfo.hitx-mulscale30(Cos(pSprite->ang), 16); y = gHitInfo.hity-mulscale30(Sin(pSprite->ang), 16); } else { x = gHitInfo.hitx-mulscale28(pMissileInfo->atd<<1, Cos(pSprite->ang)); y = gHitInfo.hity-mulscale28(pMissileInfo->atd<<1, Sin(pSprite->ang)); } } spritetype *pMissile = actSpawnSprite(pSprite->sectnum, x, y, z, 5, 1); int nMissile = pMissile->index; SetBitString(show2dsprite, nMissile); pMissile->type = nType; pMissile->shade = pMissileInfo->atc; pMissile->pal = 0; pMissile->clipdist = pMissileInfo->atd; pMissile->hitag = 1; pMissile->xrepeat = pMissileInfo->ata; pMissile->yrepeat = pMissileInfo->atb; pMissile->picnum = pMissileInfo->picnum; pMissile->ang = (pSprite->ang+pMissileInfo->at6)&2047; xvel[nMissile] = mulscale(pMissileInfo->at2, a4, 14); yvel[nMissile] = mulscale(pMissileInfo->at2, a5, 14); zvel[nMissile] = mulscale(pMissileInfo->at2, a6, 14); actPropagateSpriteOwner(pMissile, pSprite); pMissile->cstat |= 1; int nXSprite = pMissile->extra; dassert(nXSprite > 0 && nXSprite < kMaxXSprites); xsprite[nXSprite].target = -1; evPost(nMissile, 3, 600, CALLBACK_ID_1); actBuildMissile(pMissile, nXSprite, nSprite); if (v4) { actImpactMissile(pMissile, hit); pMissile = NULL; } return pMissile; } void actBuildMissile(spritetype* pMissile, int nXSprite, int nSprite) { int nMissile = pMissile->index; switch (pMissile->type) { case 315: evPost(nMissile, 3, 0, CALLBACK_ID_0); break; case 302: evPost(nMissile, 3, 0, CALLBACK_ID_15); break; case 309: seqSpawn(29, 3, nXSprite, -1); break; case 300: pMissile->cstat |= 16; break; case 306: sfxPlay3DSound(pMissile, 251, 0, 0); break; case 307: seqSpawn(2, 3, nXSprite, -1); sfxPlay3DSound(pMissile, 493, 0, 0); break; case 312: seqSpawn(61, 3, nXSprite, nNapalmClient); sfxPlay3DSound(pMissile, 441, 0, 0); break; case 305: seqSpawn(22, 3, nXSprite, nFireballClient); sfxPlay3DSound(pMissile, 441, 0, 0); break; case 308: seqSpawn(27, 3, nXSprite, -1); xvel[nMissile] += xvel[nSprite] / 2 + Random2(0x11111); yvel[nMissile] += yvel[nSprite] / 2 + Random2(0x11111); zvel[nMissile] += zvel[nSprite] / 2 + Random2(0x11111); break; case 313: seqSpawn(61, 3, nXSprite, dword_2192E0); sfxPlay3DSound(pMissile, 441, 0, 0); break; case 314: seqSpawn(23, 3, nXSprite, dword_2192D8); xvel[nMissile] += xvel[nSprite] / 2 + Random2(0x11111); yvel[nMissile] += yvel[nSprite] / 2 + Random2(0x11111); zvel[nMissile] += zvel[nSprite] / 2 + Random2(0x11111); break; case 304: if (Chance(0x8000)) seqSpawn(0, 3, nXSprite, -1); else seqSpawn(1, 3, nXSprite, -1); xvel[nMissile] += xvel[nSprite] + Random2(0x11111); yvel[nMissile] += yvel[nSprite] + Random2(0x11111); zvel[nMissile] += zvel[nSprite] + Random2(0x11111); break; case 303: evPost(nMissile, 3, 30, CALLBACK_ID_2); evPost(nMissile, 3, 0, CALLBACK_ID_3); sfxPlay3DSound(pMissile, 422, 0, 0); break; case 301: evPost(nMissile, 3, 0, CALLBACK_ID_3); sfxPlay3DSound(pMissile, 422, 0, 0); break; case 317: evPost(nMissile, 3, 0, CALLBACK_ID_7); break; case 311: sfxPlay3DSound(pMissile, 252, 0, 0); break; } } int actGetRespawnTime(spritetype *pSprite) { int nXSprite = pSprite->extra; if (nXSprite <= 0) return -1; XSPRITE *pXSprite = &xsprite[nXSprite]; if (IsDudeSprite(pSprite) && !IsPlayerSprite(pSprite)) { if (pXSprite->respawn == 2 || (pXSprite->respawn != 1 && gGameOptions.nMonsterSettings == 2)) return gGameOptions.nMonsterRespawnTime; return -1; } if (IsWeaponSprite(pSprite)) { if (pXSprite->respawn == 3 || gGameOptions.nWeaponSettings == 1) return 0; if (pXSprite->respawn != 1 && gGameOptions.nWeaponSettings != 0) return gGameOptions.nWeaponRespawnTime; return -1; } if (IsAmmoSprite(pSprite)) { if (pXSprite->respawn == 2 || (pXSprite->respawn != 1 && gGameOptions.nWeaponSettings != 0)) return gGameOptions.nWeaponRespawnTime; return -1; } if (IsItemSprite(pSprite)) { if (pXSprite->respawn == 3 && gGameOptions.nGameType == 1) return 0; if (pXSprite->respawn == 2 || (pXSprite->respawn != 1 && gGameOptions.nItemSettings != 0)) { switch (pSprite->type) { case 113: case 117: case 124: return gGameOptions.nSpecialRespawnTime; case 114: return gGameOptions.nSpecialRespawnTime<<1; default: return gGameOptions.nItemRespawnTime; } } return -1; } return -1; } bool actCheckRespawn(spritetype *pSprite) { int nSprite = pSprite->index; int nXSprite = pSprite->extra; if (nXSprite > 0) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nRespawnTime = actGetRespawnTime(pSprite); if (nRespawnTime < 0) return 0; pXSprite->respawnPending = 1; if (pSprite->type >= kThingBase && pSprite->type < kThingMax) { pXSprite->respawnPending = 3; if (pSprite->type == 400) pSprite->cstat |= 32768; } if (nRespawnTime > 0) { if (pXSprite->respawnPending == 1) nRespawnTime = mulscale16(nRespawnTime, 0xa000); pSprite->owner = pSprite->statnum; actPostSprite(pSprite->index, 8); pSprite->hitag |= 16; if (pSprite->type >= kDudeBase && pSprite->type < kDudeMax) { pSprite->cstat &= ~257; pSprite->x = baseSprite[nSprite].x; pSprite->y = baseSprite[nSprite].y; pSprite->z = baseSprite[nSprite].z; } evPost(nSprite, 3, nRespawnTime, CALLBACK_ID_9); } return 1; } return 0; } bool actCanSplatWall(int nWall) { dassert(nWall >= 0 && nWall < kMaxWalls); walltype *pWall = &wall[nWall]; if (pWall->cstat & 16384) return 0; if (pWall->cstat & 32768) return 0; int nType = GetWallType(nWall); if (nType >= 500 && nType < 512) return 0; if (pWall->nextsector != -1) { sectortype *pSector = §or[pWall->nextsector]; if (pSector->lotag >= 600 && pSector->lotag < 620) return 0; } return 1; } void actFireVector(spritetype *pShooter, int a2, int a3, int a4, int a5, int a6, VECTOR_TYPE vectorType) { int nShooter = pShooter->index; dassert(vectorType >= 0 && vectorType < kVectorMax); VECTORDATA *pVectorData = &gVectorData[vectorType]; int nRange = pVectorData->maxDist; int hit = VectorScan(pShooter, a2, a3, a4, a5, a6, nRange, 1); if (hit == 3) { int nSprite = gHitInfo.hitsprite; dassert(nSprite >= 0 && nSprite < kMaxSprites); spritetype *pSprite = &sprite[nSprite]; if (IsPlayerSprite(pSprite)) { PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; if (powerupCheck(pPlayer, 24)) { gHitInfo.hitsprite = nShooter; gHitInfo.hitx = pShooter->x; gHitInfo.hity = pShooter->y; gHitInfo.hitz = pShooter->z; } } } int x = gHitInfo.hitx-mulscale(a4, 16, 14); int y = gHitInfo.hity-mulscale(a5, 16, 14); int z = gHitInfo.hitz-mulscale(a6, 256, 14); short nSector = gHitInfo.hitsect; char nSurf = kSurfNone; if (nRange == 0 || approxDist(gHitInfo.hitx-pShooter->x, gHitInfo.hity-pShooter->y) < nRange) { switch (hit) { case 1: { int nSector = gHitInfo.hitsect; if (sector[nSector].ceilingstat&1) nSurf = kSurfNone; else nSurf = surfType[sector[nSector].ceilingpicnum]; break; } case 2: { int nSector = gHitInfo.hitsect; if (sector[nSector].floorstat&1) nSurf = kSurfNone; else nSurf = surfType[sector[nSector].floorpicnum]; break; } case 0: { int nWall = gHitInfo.hitwall; dassert(nWall >= 0 && nWall < kMaxWalls); nSurf = surfType[wall[nWall].picnum]; if (actCanSplatWall(nWall)) { int x = gHitInfo.hitx-mulscale(a4, 16, 14); int y = gHitInfo.hity-mulscale(a5, 16, 14); int z = gHitInfo.hitz-mulscale(a6, 256, 14); int nSurf = surfType[wall[nWall].picnum]; dassert(nSurf < kSurfMax); if (pVectorData->at1d[nSurf].at0 >= 0) { spritetype *pFX = gFX.fxSpawn(pVectorData->at1d[nSurf].at0, nSector, x, y, z, 0); if (pFX) { pFX->ang = (GetWallAngle(nWall)+512)&2047; pFX->cstat |= 16; } } } break; } case 4: { int nWall = gHitInfo.hitwall; dassert(nWall >= 0 && nWall < kMaxWalls); nSurf = surfType[wall[nWall].overpicnum]; int nXWall = wall[nWall].extra; if (nXWall > 0) { XWALL *pXWall = &xwall[nXWall]; if (pXWall->triggerVector) trTriggerWall(nWall, pXWall, 51); } break; } case 3: { int nSprite = gHitInfo.hitsprite; nSurf = surfType[sprite[nSprite].picnum]; dassert(nSprite >= 0 && nSprite < kMaxSprites); spritetype *pSprite = &sprite[nSprite]; x -= mulscale(a4, 112, 14); y -= mulscale(a5, 112, 14); z -= mulscale(a6, 112<<4, 14); int shift = 4; if (vectorType == VECTOR_TYPE_0 && !IsPlayerSprite(pSprite)) shift = 3; actDamageSprite(nShooter, pSprite, pVectorData->at0, pVectorData->at1<extra; if (nXSprite > 0) { XSPRITE *pXSprite = &xsprite[nXSprite]; if (pXSprite->Vector) trTriggerSprite(nSprite, pXSprite, 31); } if (pSprite->statnum == 4) { int t = thingInfo[pSprite->type-kThingBase].at2; if (t > 0 && pVectorData->at5) { int t2 = divscale(pVectorData->at5, t, 8); xvel[nSprite] += mulscale16(a4, t2); yvel[nSprite] += mulscale16(a5, t2); zvel[nSprite] += mulscale16(a6, t2); } if (pVectorData->at11) { XSPRITE *pXSprite = &xsprite[nXSprite]; if (!pXSprite->burnTime) evPost(nSprite, 3, 0, CALLBACK_ID_0); actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->at11); } } if (pSprite->statnum == 6) { int t = pSprite->type == 426 ? 0 : dudeInfo[pSprite->type-kDudeBase].mass; if (IsDudeSprite(pSprite)) { switch (pSprite->lotag) { case kCustomDude: case kCustomDudeBurning: t = getSpriteMassBySize(pSprite); break; } } if (t > 0 && pVectorData->at5) { int t2 = divscale(pVectorData->at5, t, 8); xvel[nSprite] += mulscale16(a4, t2); yvel[nSprite] += mulscale16(a5, t2); zvel[nSprite] += mulscale16(a6, t2); } if (pVectorData->at11) { XSPRITE *pXSprite = &xsprite[nXSprite]; if (!pXSprite->burnTime) evPost(nSprite, 3, 0, CALLBACK_ID_0); actBurnSprite(actSpriteIdToOwnerId(nShooter), pXSprite, pVectorData->at11); } if (Chance(pVectorData->atd)) { int t = gVectorData[19].maxDist; a4 += Random3(4000); a5 += Random3(4000); a6 += Random3(4000); if (HitScan(pSprite, gHitInfo.hitz, a4, a5, a6, CLIPMASK1, t) == 0) { if (approxDist(gHitInfo.hitx-pSprite->x, gHitInfo.hity-pSprite->y) <= t) { int nWall = gHitInfo.hitwall; int nSector = gHitInfo.hitsect; if (actCanSplatWall(nWall)) { int x = gHitInfo.hitx - mulscale(a4, 16, 14); int y = gHitInfo.hity - mulscale(a5, 16, 14); int z = gHitInfo.hitz - mulscale(a6, 16<<4, 14); int nSurf = surfType[wall[nWall].picnum]; VECTORDATA *pVectorData = &gVectorData[19]; FX_ID t2 = pVectorData->at1d[nSurf].at1; FX_ID t3 = pVectorData->at1d[nSurf].at2; spritetype *pFX = NULL; if (t2 > FX_NONE && (t3 == FX_NONE || Chance(0x4000))) pFX = gFX.fxSpawn(t2, nSector, x, y, z, 0); else if(t3 > FX_NONE) pFX = gFX.fxSpawn(t3, nSector, x, y, z, 0); if (pFX) { zvel[pFX->index] = 0x2222; pFX->ang = (GetWallAngle(nWall)+512)&2047; pFX->cstat |= 16; } } } } } for (int i = 0; i < pVectorData->at15; i++) if (Chance(pVectorData->at19)) fxSpawnBlood(pSprite, pVectorData->at1<<4); } // by NoOne: add impulse for sprites from physics list if (gPhysSpritesCount > 0 && pVectorData->at5) { int nIndex = isDebris(pSprite->index); if (nIndex != -1 && (xsprite[pSprite->extra].physAttr & kPhysDebrisVector)) { int impulse = divscale(pVectorData->at5, ClipLow(gSpriteMass[pSprite->extra].mass, 10), 6); xvel[nSprite] += mulscale16(a4, impulse); yvel[nSprite] += mulscale16(a5, impulse); zvel[nSprite] += mulscale16(a6, impulse); if (pVectorData->at11 != 0) { if (!xsprite[nXSprite].burnTime) evPost(nSprite, 3, 0, CALLBACK_ID_0); actBurnSprite(actSpriteIdToOwnerId(nShooter), &xsprite[nXSprite], pVectorData->at11); } if (pSprite->index >= kThingBase && pSprite->index < kThingMax) actPostSprite(pSprite->index, 4); // if it was a thing, return it's statnum back } } break; } } } dassert(nSurf < kSurfMax); if (pVectorData->at1d[nSurf].at1 >= 0) gFX.fxSpawn(pVectorData->at1d[nSurf].at1, nSector, x, y, z, 0); if (pVectorData->at1d[nSurf].at2 >= 0) gFX.fxSpawn(pVectorData->at1d[nSurf].at2, nSector, x, y, z, 0); if (pVectorData->at1d[nSurf].at3 >= 0) sfxPlay3DSound(x, y, z, pVectorData->at1d[nSurf].at3, nSector); } void FireballSeqCallback(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; spritetype *pFX = gFX.fxSpawn(FX_11, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); if (pFX) { int nFX = pFX->index; xvel[nFX] = xvel[nSprite]; yvel[nFX] = yvel[nSprite]; zvel[nFX] = zvel[nSprite]; } } void NapalmSeqCallback(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; spritetype *pFX = gFX.fxSpawn(FX_12, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); if (pFX) { int nFX = pFX->index; xvel[nFX] = xvel[nSprite]; yvel[nFX] = yvel[nSprite]; zvel[nFX] = zvel[nSprite]; } } void sub_3888C(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; spritetype *pFX = gFX.fxSpawn(FX_32, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); if (pFX) { int nFX = pFX->index; xvel[nFX] = xvel[nSprite]; yvel[nFX] = yvel[nSprite]; zvel[nFX] = zvel[nSprite]; } } void sub_38938(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; spritetype *pFX = gFX.fxSpawn(FX_33, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); if (pFX) { int nFX = pFX->index; xvel[nFX] = xvel[nSprite]; yvel[nFX] = yvel[nSprite]; zvel[nFX] = zvel[nSprite]; } } void TreeToGibCallback(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; pSprite->type = 417; pXSprite->state = 1; pXSprite->data1 = 15; pXSprite->data2 = 0; pXSprite->data3 = 0; pXSprite->health = thingInfo[17].at0; pXSprite->data4 = 312; pSprite->cstat |= 257; } void DudeToGibCallback1(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; pSprite->type = 426; pXSprite->data1 = 8; pXSprite->data2 = 0; pXSprite->data3 = 0; pXSprite->health = thingInfo[26].at0; pXSprite->data4 = 319; pXSprite->triggerOnce = 0; pXSprite->isTriggered = 0; pXSprite->locked = 0; pXSprite->targetX = (int)gFrameClock; pXSprite->state = 1; } void DudeToGibCallback2(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; pSprite->type = 426; pXSprite->data1 = 3; pXSprite->data2 = 0; pXSprite->data3 = 0; pXSprite->health = thingInfo[26].at0; pXSprite->data4 = 319; pXSprite->triggerOnce = 0; pXSprite->isTriggered = 0; pXSprite->locked = 0; pXSprite->targetX = (int)gFrameClock; pXSprite->state = 1; } void actPostSprite(int nSprite, int nStatus) { int n; dassert(gPostCount < kMaxSprites); dassert(nSprite < kMaxSprites && sprite[nSprite].statnum < kMaxStatus); dassert(nStatus >= 0 && nStatus <= kStatFree); if (sprite[nSprite].hitag&32) { for (n = 0; n < gPostCount; n++) if (gPost[n].at0 == nSprite) break; dassert(n < gPostCount); } else { n = gPostCount; sprite[nSprite].hitag |= 32; gPostCount++; } gPost[n].at0 = nSprite; gPost[n].at2 = nStatus; } void actPostProcess(void) { for (int i = 0; i < gPostCount; i++) { POSTPONE *pPost = &gPost[i]; int nSprite = pPost->at0; spritetype *pSprite = &sprite[nSprite]; pSprite->hitag &= ~32; int nStatus = pPost->at2; if (nStatus == kStatFree) { evKill(nSprite, 3); if (sprite[nSprite].extra > 0) seqKill(3, sprite[nSprite].extra); DeleteSprite(nSprite); } else ChangeSpriteStat(nSprite, nStatus); } gPostCount = 0; } void MakeSplash(spritetype *pSprite, XSPRITE *pXSprite) { UNREFERENCED_PARAMETER(pXSprite); pSprite->hitag &= ~2; int nXSprite = pSprite->extra; pSprite->z -= 4<<8; int nSurface = tileGetSurfType(gSpriteHit[nXSprite].florhit); switch (pSprite->type) { case 423: if (nSurface == kSurfWater) { seqSpawn(6, 3, nXSprite, -1); sfxPlay3DSound(pSprite, 356, -1, 0); } else { seqSpawn(7, 3, nXSprite, -1); sfxPlay3DSound(pSprite, 354, -1, 0); } break; case 424: seqSpawn(8, 3, nXSprite, -1); sfxPlay3DSound(pSprite, 354, -1, 0); break; } } class ActorLoadSave : public LoadSave { virtual void Load(void); virtual void Save(void); }; void ActorLoadSave::Load(void) { Read(gSpriteHit, sizeof(gSpriteHit)); Read(gAffectedSectors, sizeof(gAffectedSectors)); Read(gAffectedXWalls, sizeof(gAffectedXWalls)); Read(&gPostCount, sizeof(gPostCount)); Read(gPost, sizeof(gPost)); actInit(true); } void ActorLoadSave::Save(void) { Write(gSpriteHit, sizeof(gSpriteHit)); Write(gAffectedSectors, sizeof(gAffectedSectors)); Write(gAffectedXWalls, sizeof(gAffectedXWalls)); Write(&gPostCount, sizeof(gPostCount)); Write(gPost, sizeof(gPost)); } static ActorLoadSave *myLoadSave; void ActorLoadSaveConstruct(void) { myLoadSave = new ActorLoadSave(); } // By NoOne: The following functions required for random event features //------------------------- int GetDataVal(spritetype* pSprite, int data) { if (pSprite->extra >= 0) { switch (data) { case 0: return xsprite[pSprite->extra].data1; case 1: return xsprite[pSprite->extra].data2; case 2: return xsprite[pSprite->extra].data3; case 3: return xsprite[pSprite->extra].data4; } } return -1; } std::default_random_engine rng; int my_random(int a, int b) { std::uniform_int_distribution dist_a_b(a, b); return dist_a_b(rng); } // tries to get random data field of sprite int GetRandDataVal(int *rData, spritetype* pSprite) { int temp[4]; if (rData != NULL && pSprite != NULL) return -1; else if (pSprite != NULL) { if (pSprite->extra < 0) return -1; if (rData == NULL) rData = temp; XSPRITE* pXSprite = &xsprite[pSprite->extra]; rData[0] = pXSprite->data1; rData[2] = pXSprite->data3; rData[1] = pXSprite->data2; rData[3] = pXSprite->data4; } else if (rData == NULL) { return -1; } int random = 0; // randomize only in case if at least 2 data fields are not empty int a = 1; int b = -1; for (int i = 0; i <= 3; i++) { if (rData[i] == 0) { if (a++ > 2) return -1; } else if (b == -1) { b++; } } // try randomize few times int maxRetries = 10; while (maxRetries > 0) { // use true random only for single player mode // otherwise use Blood's default one. In the future it maybe possible to make // host send info to clients about what was generated. if (gGameOptions.nGameType != 0 || VanillaMode() || DemoRecordStatus()) random = Random(3); else { rng.seed(std::random_device()()); random = my_random(0, 4); } if (rData[random] > 0) return rData[random]; maxRetries--; } // if nothing, get first found data value from top return rData[b]; } // this function drops random item using random pickup generator(s) spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) { spritetype* pSprite2 = NULL; int rData[4]; int selected = -1; rData[0] = xsprite[pSprite->extra].data1; rData[2] = xsprite[pSprite->extra].data3; rData[1] = xsprite[pSprite->extra].data2; rData[3] = xsprite[pSprite->extra].data4; // randomize only in case if at least 2 data fields fits. for (int i = 0; i <= 3; i++) if (rData[i] < kWeaponItemBase || rData[i] >= kItemMax) rData[i] = 0; int maxRetries = 9; while ((selected = GetRandDataVal(rData, NULL)) == prevItem) if (maxRetries-- <= 0) break; if (selected > 0) { spritetype* pSource = pSprite; XSPRITE* pXSource = &xsprite[pSource->extra]; pSprite2 = actDropObject(pSprite, selected); if (pSprite2 != NULL) { pXSource->dropMsg = pSprite2->lotag; // store dropped item lotag in dropMsg pSprite2->x = pSource->x; pSprite2->y = pSource->y; pSprite2->z = pSource->z; if ((pSource->hitag & kModernTypeFlag1) && (pXSource->txID > 0 || (pXSource->txID != 3 && pXSource->lockMsg > 0)) && dbInsertXSprite(pSprite2->xvel) > 0) { XSPRITE * pXSprite2 = &xsprite[pSprite2->extra]; // inherit spawn sprite trigger settings, so designer can send command when item picked up. pXSprite2->txID = pXSource->txID; pXSprite2->command = pXSource->command; pXSprite2->triggerOn = pXSource->triggerOn; pXSprite2->triggerOff = pXSource->triggerOff; pXSprite2->Pickup = true; } } } return pSprite2; } // this function spawns random dude using dudeSpawn spritetype* spawnRandomDude(spritetype* pSprite) { spritetype* pSprite2 = NULL; if (pSprite->extra >= 0) { int rData[4]; int selected = -1; rData[0] = xsprite[pSprite->extra].data1; rData[2] = xsprite[pSprite->extra].data3; rData[1] = xsprite[pSprite->extra].data2; rData[3] = xsprite[pSprite->extra].data4; // randomize only in case if at least 2 data fields fits. for (int i = 0; i <= 3; i++) if (rData[i] < kDudeBase || rData[i] >= kDudeMax) rData[i] = 0; if ((selected = GetRandDataVal(rData,NULL)) > 0) pSprite2 = actSpawnDude(pSprite, selected, -1, 0); } return pSprite2; } //------------------------- // By NoOne: this function plays sound predefined in missile info bool sfxPlayMissileSound(spritetype* pSprite, int missileId) { MissileType* pMissType = &missileInfo[missileId - kMissileBase]; if (Chance(0x4000)) sfxPlay3DSound(pSprite, pMissType->fireSound[0], -1, 0); else sfxPlay3DSound(pSprite, pMissType->fireSound[1], -1, 0); return true; } // By NoOne: this function plays sound predefined in vector info bool sfxPlayVectorSound(spritetype* pSprite, int vectorId) { VECTORDATA* pVectorData = &gVectorData[vectorId]; if (Chance(0x4000)) sfxPlay3DSound(pSprite, pVectorData->fireSound[0], -1, 0); else sfxPlay3DSound(pSprite, pVectorData->fireSound[1], -1, 0); return true; } // By NoOne: this function allows to spawn new custom dude and inherit spawner settings, // so custom dude can have different weapons, hp and so on... spritetype* actSpawnCustomDude(spritetype* pSprite, int nDist) { spritetype* pSource = pSprite; XSPRITE* pXSource = &xsprite[pSource->extra]; spritetype* pDude = actSpawnSprite(pSprite,6); XSPRITE* pXDude = &xsprite[pDude->extra]; int x, y, z = pSprite->z, nAngle = pSprite->ang, nType = kCustomDude; if (nDist > 0) { x = pSprite->x + mulscale30r(Cos(nAngle), nDist); y = pSprite->y + mulscale30r(Sin(nAngle), nDist); } else { x = pSprite->x; y = pSprite->y; } pDude->lotag = nType; pDude->ang = nAngle; vec3_t pos = { x, y, z }; setsprite(pDude->index, &pos); pDude->cstat |= 0x1101; pDude->clipdist = dudeInfo[nType - kDudeBase].clipdist; // inherit weapon, seq and sound settings. pXDude->data1 = pXSource->data1; pXDude->data2 = pXSource->data2; pXDude->sysData1 = pXSource->data3; // move sndStartId from data3 to sysData1 pXDude->data3 = 0; // spawn seq seqSpawn(getSeqStartId(pXDude), 3, pDude->extra, -1); // inherit movement speed. pXDude->busyTime = pXSource->busyTime; // inherit clipdist? if (pSource->clipdist > 0) pDude->clipdist = pSource->clipdist; // inherit custom hp settings if (pXSource->data4 <= 0) pXDude->health = dudeInfo[nType].startHealth << 4; else pXDude->health = ClipRange(pXSource->data4 << 4, 1, 65535); if (pSource->hitag & kModernTypeFlag1) { switch (pSource->type) { case kGDXCustomDudeSpawn: //inherit pal? if (pDude->pal <= 0) pDude->pal = pSource->pal; // inherit spawn sprite trigger settings, so designer can count monsters. pXDude->txID = pXSource->txID; pXDude->command = pXSource->command; pXDude->triggerOn = pXSource->triggerOn; pXDude->triggerOff = pXSource->triggerOff; // inherit drop items pXDude->dropMsg = pXSource->dropMsg; // inherit required key so it can be dropped pXDude->key = pXSource->key; // inherit dude flags pXDude->dudeDeaf = pXSource->dudeDeaf; pXDude->dudeGuard = pXSource->dudeGuard; pXDude->dudeAmbush = pXSource->dudeAmbush; pXDude->dudeFlag4 = pXSource->dudeFlag4; break; } } aiInitSprite(pDude); return pDude; } int getSpriteMassBySize(spritetype* pSprite) { int mass = 0; int seqId = -1; Seq* pSeq = NULL; if (IsDudeSprite(pSprite)) { switch (pSprite->lotag) { case 225: // fake dude, no seq break; case kCustomDude: case kCustomDudeBurning: seqId = xsprite[pSprite->extra].data2; break; default: seqId = dudeInfo[pSprite->lotag - kDudeBase].seqStartID; break; } } else if (pSprite->extra >= 0) { seqId = seqGetID(3, pSprite->extra); } SPRITEMASS* cached = &gSpriteMass[pSprite->extra]; if (((seqId >= 0 && seqId == cached->seqId) || pSprite->picnum == cached->picnum) && pSprite->xrepeat == cached->xrepeat && pSprite->yrepeat == cached->yrepeat && pSprite->clipdist == cached->clipdist) { return cached->mass; } short picnum = pSprite->picnum; short massDiv = 30; short addMul = 2; short subMul = 2; if (seqId >= 0) { DICTNODE* hSeq = gSysRes.Lookup(seqId, "SEQ"); if (hSeq) { pSeq = (Seq*)gSysRes.Load(hSeq); picnum = seqGetTile(&pSeq->frames[0]); } else picnum = pSprite->picnum; } int clipDist = ClipRange(pSprite->clipdist, 1, 255); short x = tilesiz[picnum].x; short y = tilesiz[picnum].y; short xrepeat = pSprite->xrepeat; short yrepeat = pSprite->yrepeat; // take surface type into account switch (tileGetSurfType(pSprite->xvel + 0xc000)) { case 1: massDiv = 16; break; // stone case 2: massDiv = 18; break; // metal case 3: massDiv = 21; break; // wood case 4: massDiv = 25; break; // flesh case 5: massDiv = 28; break; // water case 6: massDiv = 26; break; // dirt case 7: massDiv = 27; break; // clay case 8: massDiv = 35; break; // snow case 9: massDiv = 22; break; // ice case 10: massDiv = 37; break; // leaves case 11: massDiv = 33; break; // cloth case 12: massDiv = 36; break; // plant case 13: massDiv = 24; break; // goo case 14: massDiv = 23; break; // lava } mass = ((x + y) * (clipDist / 2)) / massDiv; if (xrepeat > 64) mass += ((xrepeat - 64) * addMul); else if (xrepeat < 64 && mass > 0) { for (int i = 64 - xrepeat; i > 0; i--) { if ((mass -= subMul) <= 100 && subMul-- <= 1) { mass -= i; break; } } } if (yrepeat > 64) mass += ((yrepeat - 64) * addMul); else if (yrepeat < 64 && mass > 0) { for (int i = 64 - yrepeat; i > 0; i--) { if ((mass -= subMul) <= 100 && subMul-- <= 1) { mass -= i; break; } } } if (mass <= 0) cached->mass = 1 + Random(10); else cached->mass = ClipRange(mass, 1, 65535); cached->airVel = ClipRange(400 - cached->mass, 32, 400); cached->fraction = ClipRange(60000 - (cached->mass << 7), 8192, 60000); cached->xrepeat = pSprite->xrepeat; cached->yrepeat = pSprite->yrepeat; cached->picnum = pSprite->picnum; cached->seqId = seqId; cached->clipdist = pSprite->clipdist; return cached->mass; } int isDebris(int nSprite) { if (sprite[nSprite].extra < 0 || xsprite[sprite[nSprite].extra].physAttr == 0) return -1; for (int i = 0; i < gPhysSpritesCount; i++) { if (gPhysSpritesList[i] != nSprite) continue; return i; } return -1; } int debrisGetFreeIndex(void) { for (int i = 0; i < kMaxSuperXSprites; i++) { if (gPhysSpritesList[i] == -1 || sprite[gPhysSpritesList[i]].statnum == kStatFree) return i; else if ((sprite[gPhysSpritesList[i]].hitag & kHitagFree) || sprite[gPhysSpritesList[i]].extra < 0) return i; else if (xsprite[sprite[gPhysSpritesList[i]].extra].physAttr == 0) return i; } return -1; } void debrisConcuss(int nOwner, int listIndex, int x, int y, int z, int dmg) { spritetype* pSprite = (gPhysSpritesList[listIndex] >= 0) ? &sprite[gPhysSpritesList[listIndex]] : NULL; if (pSprite != NULL && pSprite->extra >= 0 && pSprite->extra < kMaxXSprites) { int dx = pSprite->x - x; int dy = pSprite->y - y; int dz = (pSprite->z - z) >> 4; dmg = scale(0x40000, dmg, 0x40000 + dx * dx + dy * dy + dz * dz); int size = (tilesiz[pSprite->picnum].x * pSprite->xrepeat * tilesiz[pSprite->picnum].y * pSprite->yrepeat) >> 1; if (xsprite[pSprite->extra].physAttr & kPhysDebrisExplode) { if (gSpriteMass[pSprite->extra].mass > 0) { int t = scale(dmg, size, gSpriteMass[pSprite->extra].mass); xvel[pSprite->xvel] += mulscale16(t, dx); yvel[pSprite->xvel] += mulscale16(t, dy); zvel[pSprite->xvel] += mulscale16(t, dz); } } if (pSprite->index >= kThingBase && pSprite->index < kThingMax) actPostSprite(pSprite->index, 4); // if it was a thing, return it's statnum back actDamageSprite(nOwner, pSprite, DAMAGE_TYPE_3, dmg); return; } } void debrisMove(int listIndex) { if (!(sprite[gPhysSpritesList[listIndex]].extra > 0 && sprite[gPhysSpritesList[listIndex]].extra < kMaxXSprites)) { gPhysSpritesList[listIndex] = -1; return; } else if (!(sprite[gPhysSpritesList[listIndex]].sectnum >= 0 && sprite[gPhysSpritesList[listIndex]].sectnum < kMaxSectors)) { gPhysSpritesList[listIndex] = -1; return; } int nSprite = gPhysSpritesList[listIndex]; int nXSprite = sprite[nSprite].extra; XSPRITE* pXDebris = &xsprite[nXSprite]; spritetype* pSprite = &sprite[nSprite]; int nSector = pSprite->sectnum; int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int moveHit = 0; int floorDist = (bottom - pSprite->z) / 4; int ceilDist = (pSprite->z - top) / 4; int clipDist = pSprite->clipdist << 2; int tmpFraction = gSpriteMass[pSprite->extra].fraction; if (sector[nSector].extra >= 0 && xsector[sector[nSector].extra].Underwater) tmpFraction >>= 1; if (xvel[pSprite->xvel] != 0 || yvel[pSprite->xvel] != 0) { short oldcstat = pSprite->cstat; pSprite->cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); moveHit = gSpriteHit[nXSprite].hit = ClipMove((int*)& pSprite->x, (int*)& pSprite->y, (int*)& pSprite->z, &nSector, xvel[nSprite] >> 12, yvel[nSprite] >> 12, pSprite->clipdist << 2, (pSprite->z - top) / 4, (bottom - pSprite->z) / 4, CLIPMASK0); pSprite->cstat = oldcstat; dassert(nSector >= 0); if (pSprite->sectnum != nSector) { dassert(nSector >= 0 && nSector < kMaxSectors); ChangeSpriteSect(nSprite, nSector); } if ((gSpriteHit[nXSprite].hit & 0xc000) == 0x8000) { int nHitWall = gSpriteHit[nXSprite].hit & 0x3fff; actWallBounceVector((int*)& xvel[nSprite], (int*)& yvel[nSprite], nHitWall, tmpFraction); } } else { dassert(nSector >= 0 && nSector < kMaxSectors); FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector); } if (zvel[nSprite]) pSprite->z += zvel[nSprite] >> 8; int ceilZ, ceilHit, floorZ, floorHit; GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist << 2, CLIPMASK0); GetSpriteExtents(pSprite, &top, &bottom); if ((pXDebris->physAttr & kPhysGravity) && bottom < floorZ) { pSprite->z += 455; zvel[nSprite] += 58254; } int warp = CheckLink(pSprite); if (warp != 0) { GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist << 2, CLIPMASK0); if (!(pSprite->cstat & CSTAT_SPRITE_INVISIBLE)) { switch (warp) { case kMarkerUpWater: case kMarkerUpGoo: long pitch = (150000 - (gSpriteMass[pSprite->extra].mass << 9)) + Random3(8192); sfxPlay3DSoundCP(pSprite, 720, -1, 0, pitch, 75 - Random(40)); if (sector[pSprite->sectnum].extra < 0 || !xsector[sector[pSprite->sectnum].extra].Underwater) evKill(pSprite->xvel, 3, CALLBACK_ID_11); else { if (Chance(0x8000)) evPost(pSprite->xvel, 3, 0, CALLBACK_ID_11); for (int i = 2; i <= 5; i++) { if (Chance(0x3000 * i)) evPost(pSprite->xvel, 3, 0, CALLBACK_ID_11); } } break; } } } GetSpriteExtents(pSprite, &top, &bottom); if ((floorHit & 0xe000) == 0xc000) { if ((sprite[floorHit & 0x1fff].cstat & 0x30) == 0x20) if (klabs(bottom - floorZ) < 1024) floorZ -= 1024; } if (bottom >= floorZ) { gSpriteHit[nXSprite].florhit = floorHit; pSprite->z += floorZ - bottom; int v20 = zvel[nSprite] - velFloor[pSprite->sectnum]; if (v20 > 0) { pXDebris->physAttr |= kPhysFalling; int vax = actFloorBounceVector((int*)& xvel[nSprite], (int*)& yvel[nSprite], (int*)& v20, pSprite->sectnum, tmpFraction); zvel[nSprite] = v20; if (velFloor[pSprite->sectnum] == 0 && klabs(zvel[nSprite]) < 0x10000) { zvel[nSprite] = 0; pXDebris->physAttr &= ~kPhysFalling; } moveHit = 0x4000 | nSector; } else if (zvel[nSprite] == 0) pXDebris->physAttr &= ~kPhysFalling; } else { gSpriteHit[nXSprite].florhit = 0; if (pXDebris->physAttr & kPhysGravity) pXDebris->physAttr |= kPhysFalling; } if (top <= ceilZ) { gSpriteHit[nXSprite].ceilhit = ceilHit; pSprite->z += ClipLow(ceilZ - top, 0); if (zvel[nSprite] < 0) { xvel[nSprite] = mulscale16(xvel[nSprite], 0xc000); yvel[nSprite] = mulscale16(yvel[nSprite], 0xc000); zvel[nSprite] = mulscale16(-zvel[nSprite], 0x4000); } } else { gSpriteHit[nXSprite].ceilhit = 0; } if (bottom >= floorZ) { int nVel = approxDist(xvel[nSprite], yvel[nSprite]); int nVelClipped = ClipHigh(nVel, 0x11111); if ((floorHit & 0xc000) == 0xc000) { int nHitSprite = floorHit & 0x3fff; if ((sprite[nHitSprite].cstat & 0x30) == 0) { xvel[nSprite] += mulscale(4, pSprite->x - sprite[nHitSprite].x, 2); yvel[nSprite] += mulscale(4, pSprite->y - sprite[nHitSprite].y, 2); moveHit = gSpriteHit[nXSprite].hit; } } if (nVel > 0) { int t = divscale16(nVelClipped, nVel); xvel[nSprite] -= mulscale16(t, xvel[nSprite]); yvel[nSprite] -= mulscale16(t, yvel[nSprite]); } } if (xvel[nSprite] || yvel[nSprite]) pSprite->ang = getangle(xvel[nSprite], yvel[nSprite]); if (moveHit != 0 && pXDebris->Impact && pXDebris->locked != 1 && !pXDebris->isTriggered) { if (!pXDebris->Interrutable && pXDebris->state != pXDebris->restState) return; if (pSprite->lotag >= kThingBase && pSprite->lotag < kThingMax) // if thing was turned in debris, change it's stat back so it will do on impact what it supposed to do... //actPostSprite(nSprite, 4); // !!!! not working here for some reason changespritestat(nSprite, 4); if (pXDebris->state == 1) trTriggerSprite(pSprite->xvel, pXDebris, COMMAND_ID_0); else trTriggerSprite(pSprite->xvel, pXDebris, COMMAND_ID_1); } } bool ceilIsTooLow(spritetype* pSprite) { if (pSprite != NULL) { sectortype* pSector = §or[pSprite->sectnum]; int a = pSector->ceilingz - pSector->floorz; int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int b = top - bottom; if (a > b) return true; } return false; }