code:
//-------------------------------------------------------------------------
/*
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 "compat.h"
#include "common.h"
#include "blood.h"
#include "dude.h"

DUDEINFO dudeInfo[kDudeMax-kDudeBase] =
{
    {
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0, 0, 0,
        0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4096, //seqStartId
        40, // startHp
        70, // mass
        1200, // ???
        48, // clipdist
        41, // eye height
        20, // aim height
        10240, // hear dist
        51200, // see dist
        512, // periphery
        0, // melee distance
        10, // flee health
        8, // hinder damage
        256, // change target chance
        16, // change target chance to same type
        32768, // alert chance
        1, // lockout
        46603, // front speed
        34952, // side speed
        13981, // back speed
        256, // ang speed
        15, -1, -1, // gib type
        256, 256, 96, 256, 256, 256, 192, // start damage
        0, 0, 0, 0, 0, 0, 0, // real damage
        0, // ???
        0 // ???
    },
    {
        11520,
        40,
        70,
        1200,
        48,
        41,
        20,
        10240,
        51200,
        512,
        0,
        10,
        5,
        256,
        16,
        32768,
        1,
        34952,
        34952,
        13981,
        256,
        15, -1, -1,
        256, 256, 128, 256, 256, 256, 192,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4352,
        60,
        70,
        1200,
        48,
        46,
        20,
        10240,
        51200,
        512,
        0,
        10,
        15,
        256,
        16,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        15, -1, -1,
        256, 256, 112, 256, 256, 256, 160,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4608,
        80,
        200,
        1200,
        48,
        128,
        20,
        10240,
        51200,
        512,
        0,
        10,
        15,
        256,
        16,
        32768,
        1,
        23301,
        23301,
        13981,
        256,
        15, -1, -1,
        256, 256, 32, 128, 256, 64, 128,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4352,
        60,
        70,
        1200,
        48,
        46,
        20,
        5120,
        0,
        341,
        0,
        10,
        15,
        256,
        16,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        15, -1, -1,
        256, 256, 112, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4864,
        110,
        120,
        1200,
        64,
        13,
        5,
        10240,
        51200,
        512,
        0,
        10,
        25,
        256,
        16,
        32768,
        1,
        46603,
        34952,
        23301,
        384,
        30, -1, -1,
        0, 128, 48, 208, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        5120,
        200,
        200,
        1200,
        84,
        13,
        5,
        10240,
        51200,
        512,
        0,
        10,
        20,
        256,
        16,
        32768,
        1,
        46603,
        34952,
        23301,
        256,
        19, -1, -1,
        0, 0, 10, 10, 0, 128, 64,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        11008,
        100,
        200,
        1200,
        64,
        13,
        5,
        2048,
        5120,
        512,
        0,
        10,
        15,
        256,
        16,
        32768,
        0,
        0,
        0,
        0,
        0,
        -1, -1, -1,
        0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        11264,
        100,
        200,
        1200,
        64,
        13,
        5,
        2048,
        5120,
        512,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        0,
        0,
        0,
        0,
        -1, -1, -1,
        0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        5376,
        100,
        70,
        1200,
        64,
        25,
        15,
        10240,
        51200,
        341,
        0,
        10,
        10,
        256,
        0,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        -1, -1, -1,
        0, 0, 48, 0, 0, 16, 0,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        5632,
        70,
        120,
        1200,
        80,
        6,
        0,
        10240,
        51200,
        682,
        0,
        10,
        20,
        256,
        16,
        32768,
        0,
        116508,
        81555,
        69905,
        384,
        29, -1, -1,
        48, 0, 48, 48, 256, 128, 192,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        5888,
        10,
        70,
        1200,
        32,
        0,
        0,
        5120,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        64, 256, 256, 256, 0, 64, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        6144,
        10,
        5,
        1200,
        32,
        -5,
        -5,
        5120,
        51200,
        682,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        64, 256, 256, 96, 256, 64, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        6400,
        25,
        10,
        1200,
        32,
        -5,
        -5,
        5120,
        51200,
        682,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        64, 128, 256, 96, 256, 64, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        6656,
        75,
        20,
        1200,
        32,
        -5,
        -5,
        5120,
        51200,
        682,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        128, 256, 256, 96, 256, 64, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        6912,
        100,
        40,
        1200,
        32,
        -5,
        -5,
        5120,
        51200,
        682,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        32, 16, 16, 16, 32, 32, 32,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        7168,
        50,
        200,
        1200,
        64,
        37,
        20,
        5120,
        51200,
        682,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        48, 80, 64, 128, 0, 128, 48,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        7424,
        25,
        30,
        1200,
        32,
        4,
        0,
        5120,
        51200,
        512,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        34952,
        23301,
        23301,
        128,
        7, -1, -1,
        256, 256, 256, 256, 0, 256, 192,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        7680,
        10,
        5,
        1200,
        32,
        2,
        0,
        10240,
        25600,
        512,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        23301,
        23301,
        13981,
        384,
        7, -1, -1,
        256, 256, 256, 256, 256, 64, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        7936,
        10,
        5,
        1200,
        32,
        3,
        0,
        12800,
        51200,
        512,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        256, 256, 256, 256, 256, 128, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        8192,
        50,
        65535,
        1200,
        64,
        40,
        0,
        2048,
        11264,
        1024,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        0,
        0,
        0,
        384,
        7, -1, -1,
        160, 160, 128, 160, 0, 0, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        8448,
        10,
        65535,
        1200,
        32,
        0,
        0,
        2048,
        5120,
        1024,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        0,
        0,
        0,
        384,
        7, -1, -1,
        256, 256, 256, 80, 0, 0, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        8704,
        100,
        65535,
        1200,
        64,
        40,
        0,
        2048,
        15360,
        1024,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        0,
        0,
        0,
        384,
        7, -1, -1,
        96, 0, 128, 64, 256, 64, 160,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        8960,
        20,
        65535,
        1200,
        32,
        0,
        0,
        2048,
        5120,
        1024,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        0,
        0,
        0,
        384,
        7, -1, -1,
        128, 0, 128, 128, 0, 0, 128,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        9216,
        200,
        65535,
        1200,
        64,
        40,
        0,
        2048,
        51200,
        1024,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        0,
        0,
        0,
        0,
        7, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        9472,
        50,
        65535,
        1200,
        32,
        0,
        0,
        2048,
        51200,
        1024,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        0,
        0,
        0,
        0,
        7, -1, -1,
        256, 256, 128, 256, 128, 128, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        9728,
        200,
        1000,
        1200,
        64,
        29,
        10,
        40960,
        102400,
        682,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        69905,
        58254,
        46603,
        384,
        7, -1, -1,
        16, 0, 16, 16, 0, 96, 48,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        9984,
        100,
        1000,
        1200,
        64,
        29,
        10,
        20480,
        51200,
        682,
        0,
        10,
        10,
        256,
        0,
        32768,
        0,
        58254,
        34952,
        25631,
        384,
        7, -1, -1,
        16, 0, 16, 16, 0, 96, 48,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        10240,
        32, // 800,
        1500,
        1200,
        128,
        0,
        0,
        25600,
        51200,
        512,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        58254,
        58254,
        34952,
        384,
        7, -1, -1,
        3, 1, 4, 4, 0, 4, 3,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4096,
        25,
        20,
        1200,
        32,
        0,
        0,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        15, -1, -1,
        256, 256, 96, 256, 256, 256, 192,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12032,
        100,
        70,
        1200,
        48,
        0,
        16,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12544,
        25,
        70,
        1200,
        48,
        41,
        20,
        10240,
        51200,
        341,
        0,
        100,
        100,
        0,
        0,
        32768,
        0,
        0,
        0,
        0,
        160,
        7, 5, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4096,
        30,
        70,
        1200,
        48,
        41,
        20,
        10240,
        51200,
        341,
        0,
        100,
        100,
        0,
        0,
        32768,
        0,
        46603,
        34952,
        13981,
        160,
        7, 5, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4352,
        12,
        70,
        1200,
        48,
        46,
        20,
        10240,
        51200,
        341,
        0,
        10,
        15,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        160,
        7, 5, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4352,
        25,
        120,
        1200,
        48,
        44,
        20,
        10240,
        51200,
        341,
        0,
        10,
        15,
        256,
        16,
        32768,
        0,
        39612,
        27962,
        13981,
        100,
        7, 5, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4096,
        100,
        70,
        1200,
        64,
        38,
        20,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        0,
        0,
        0,
        64,
        15, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        4352,
        60,
        70,
        1200,
        48,
        46,
        20,
        5120,
        0,
        341,
        0,
        10,
        15,
        256,
        16,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        15, -1, -1,
        256, 256, 112, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12544,
        50,
        70,
        1200,
        48,
        46,
        20,
        2560,
        0,
        341,
        0,
        10,
        8,
        256,
        16,
        32768,
        1,
        58254,
        46603,
        34952,
        384,
        15, -1, -1,
        288, 288, 288, 288, 288, 288, 288,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        11520,
        25,
        70,
        1200,
        32,
        -5,
        0,
        2048,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        0,
        0,
        0,
        64,
        7, 5, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        12800,
        40,
        70,
        1200,
        48,
        41,
        20,
        10240,
        51200,
        512,
        0,
        10,
        8,
        256,
        16,
        32768,
        1,
        46603,
        34952,
        13981,
        256,
        15, -1, -1,
        256, 256, 96, 160, 256, 256, 12,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        13056,
        40,
        70,
        1200,
        48,
        41,
        20,
        10240,
        51200,
        512,
        0,
        10,
        8,
        256,
        16,
        32768,
        1,
        46603,
        34952,
        13981,
        256,
        15, -1, -1,
        256, 160, 96, 64, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        13312,
        40,
        70,
        1200,
        48,
        41,
        20,
        10240,
        51200,
        512,
        0,
        10,
        12,
        256,
        16,
        32768,
        1,
        46603,
        34952,
        13981,
        256,
        15, -1, -1,
        128, 128, 16, 16, 0, 64, 48,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        13568,
        10,
        5,
        1200,
        32,
        3,
        0,
        12800,
        51200,
        512,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        160, 160, 160, 160, 256, 128, 288,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        10752,
        120,
        70,
        1200,
        48,
        41,
        20,
        12800,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        116508,
        81555,
        69905,
        384,
        7, -1, -1,
        5, 5, 15, 8, 0, 15, 15,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        13568,
        10,
        5,
        1200,
        32,
        3,
        0,
        12800,
        51200,
        512,
        0,
        10,
        10,
        256,
        16,
        32768,
        0,
        58254,
        46603,
        34952,
        384,
        7, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    {
        10752,
        25,
        70,
        1200,
        48,
        41,
        20,
        12800,
        51200,
        341,
        0,
        10,
        10,
        256,
        16,
        32768,
        1,
        116508,
        81555,
        69905,
        384,
        7, -1, -1,
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    //254 -  kDudeModernCustom
    {
        11520,		                        // start sequence ID
        85,		                            // start health
        75,			                        // mass
        120,
        48,			                        // clip distance
        48,			                        // eye above z
        20,
        10240,		                        // hear distance
        51200,	                            // seeing distance
        kAng90,	                            // vision periphery
        //			    0,
        618,                                // melee distance
        5,			                        // flee health
        5,			                        // hinder damage
        0x0000,		                        // change target chance
        0x0000,		                        // change target to kin chance
        0x8000,		                        // alertChance
        1,		                            // lockout
        46603,		                        // frontSpeed
        34952,		                        // sideSpeed
        13981,		                        // backSpeed
        256,		                        // angSpeed
        //			    0,
        7,	-1, 18,	                        // nGibType
        64, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },
    //255 -  kDudeModernCustomBurning
    {
        4096,		// start sequence ID
        25,			// start health
        5,			// mass
        120,
        48,			// clip distance
        41,			// eye above z
        20,
        12800,		// hear distance
        51200,		// seeing distance
        kAng60,	// vision periphery
        //						0,
        0,			// melee distance
        10,			// flee health
        10,			// hinder damage
        0x0100,		// change target chance
        0x0010,		// change target to kin chance
        0x8000,		// alertChance
        true,		// lockout
        58254,			// frontSpeed
        46603,			// sideSpeed
        34952,			// backSpeed
        384,			// angSpeed
        //              0,
        7,	-1, -1,		// nGibType
        256, 256, 256, 256, 256, 256, 256,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    }
};

DUDEINFO gPlayerTemplate[4] =
{
    // normal human
    {
        0x2f00,
        100,
        70,
        1200,
        0x30,
        0,
        0x10,
        0x800,
        0xc800,
        0x155,
        0,
        10,
        10,
        0x100,
        0x10,
        0x8000,
        0x1,
        0,
        0,
        0,
        0x40,
        15, -1, -1,
        0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x120,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },

    // normal beast
    {
        0x2900,
        100,
        70,
        1200,
        0x30,
        0,
        0x14,
        0x800,
        0xc800,
        0x155,
        0,
        10,
        10,
        0x100,
        0x10,
        0x8000,
        0x1,
        0,
        0,
        0,
        0x40,
        7, -1, -1,
        0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x120,
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },

    // shrink human
    {
        12032,
        100,
        10, // mass
        1200,
        16, // clipdist
        0,
        0x10,
        0x800,
        0xc800,
        0x155,
        0,
        10,
        10,
        0x100,
        0x10,
        0x8000,
        0x1,
        0,
        0,
        0,
        0x40,
        15, -1, -1, // gib type
        1024, 1024, 1024, 1024, 256, 1024, 1024, //damage shift
        0, 0, 0, 0, 0, 0, 0,
        0,
        0
    },

     // grown human
     {
         12032,
         100,
         1100, // mass
         1200,
         100, // clipdist
         0,
         0x10,
         0x800,
         0xc800,
         0x155,
         0,
         10,
         10,
         0x100,
         0x10,
         0x8000,
         0x1,
         0,
         0,
         0,
         0x40,
         15, 7, 7, // gib type
         64, 64, 64, 64, 256, 64, 64, // damage shift
         0, 0, 0, 0, 0, 0, 0,
         0,
         0
     },
};

DUDEINFO fakeDudeInfo = {};


code:
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
#pragma once
#include "actor.h"
#include "blood.h"
struct DUDEINFO {
    short seqStartID; // seq
    short startHealth; // health
    unsigned short mass; // mass
    int at6; // unused?
    unsigned char clipdist; // clipdist
    int eyeHeight;
    int aimHeight; // used by just Cerberus
    int hearDist; // hear radius
    int seeDist; // sight radius
    int periphery; // periphery
    int meleeDist; // unused?
    int fleeHealth; // at which hp level enemy will turn in burning dude
    int hinderDamage; // recoil damage
    int changeTarget; // chance to change target when attacked someone else
    int changeTargetKin; // chance to change target when attacked by same type
    int alertChance;
    char lockOut; // indicates if this dude can trigger something via trigger flags
    int frontSpeed; // acceleration
    int sideSpeed; // dodge
    int backSpeed; // backward speed (unused)
    int angSpeed; // turn speed
    int nGibType[3]; // which gib used when explode dude
    int startDamage[kDamageMax]; // start damage shift
    int curDamage[kDamageMax]; // current damage stat (post-shift)
    int at8c; // unused ?
    int at90; // unused ?
};

extern DUDEINFO dudeInfo[kDudeMax-kDudeBase];
extern DUDEINFO gPlayerTemplate[4];
extern DUDEINFO fakeDudeInfo;

inline DUDEINFO *getDudeInfo(int const nType)
{
    if (nType >= kDudeBase && nType < kDudeMax)
        return &dudeInfo[nType - kDudeBase];
    return &fakeDudeInfo;
}

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
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 "build.h"
#include "common.h"
#include "mmulti.h"
#include "fx_man.h"
#include "common_game.h"
#include "blood.h"
#include "endgame.h"
#include "globals.h"
#include "levels.h"
#include "loadsave.h"
#include "menu.h"
#include "network.h"
#include "player.h"
#include "sound.h"
#include "view.h"
#include "messages.h"

CEndGameMgr::CEndGameMgr()
{
    at0 = 0;
}

void CEndGameMgr::Draw(void)
{
    viewLoadingScreenWide();
    int nHeight;
    gMenuTextMgr.GetFontInfo(1, NULL, NULL, &nHeight);
    rotatesprite(160<<16, 20<<16, 65536, 0, 2038, -128, 0, 6|8, 0, 0, xdim-1, ydim-1);
    int nY = 20 - nHeight / 2;
    if (gGameOptions.nGameType == kGameTypeSinglePlayer)
    {
        viewDrawText(1, "LEVEL STATS", 160, nY, -128, 0, 1, 0);
        if (CCheatMgr::m_bPlayerCheated)
        {
            viewDrawText(3, ">>> YOU CHEATED! <<<", 160, 32, -128, 0, 1, 1);
        }
        gKillMgr.Draw();
        gSecretMgr.Draw();
    }
    else
    {
        viewDrawText(1, "FRAG STATS", 160, nY, -128, 0, 1, 0);
        gKillMgr.Draw();
    }
    if (/*dword_28E3D4 != 1 && */((int)totalclock&32))
    {
        viewDrawText(3, "PRESS A KEY TO CONTINUE", 160, 134, -128, 0, 1, 1);
    }
}

void CEndGameMgr::ProcessKeys(void)
{
    //if (dword_28E3D4 == 1)
    //{
    //    if (gGameOptions.gameType >= 0 || numplayers > 1)
    //        netWaitForEveryone(0);
    //    Finish();
    //}
    //else
    {
        char ch = keyGetScan();
        if (CONTROL_JoystickEnabled && !ch)
        {
            int32_t joy = JOYSTICK_GetControllerButtons();
            JOYSTICK_ClearAllButtons();
            ch = joy == (1 << CONTROLLER_BUTTON_START);
        }
        if (!ch)
            return;
        if (gGameOptions.nGameType != kGameTypeSinglePlayer || numplayers > 1)
            netWaitForEveryone(0);
        Finish();
    }
}

extern void EndLevel(void);

void CEndGameMgr::Setup(void)
{
    at1 = gInputMode;
    gInputMode = INPUT_MODE_3;
    at0 = 1;
    EndLevel();
    sndStartSample(268, 128, -1, 1);
    keyFlushScans();
}

//int gNextLevel;

extern int gInitialNetPlayers;
extern bool gStartNewGame;

void CEndGameMgr::Finish(void)
{
    levelSetupOptions(gGameOptions.nEpisode, gNextLevel);
    gInitialNetPlayers = numplayers;
    //if (FXDevice != -1)
        FX_StopAllSounds();
    sndKillAllSounds();
    gStartNewGame = 1;
    gInputMode = (INPUT_MODE)at1;
    at0 = 0;
}

CKillMgr::CKillMgr()
{
    Clear();
}

void CKillMgr::SetCount(int nCount)
{
    at0 = nCount;
}

void CKillMgr::AddCount(int nCount)
{
    at0 += nCount;
}

void CKillMgr::AddCount(spritetype* pSprite)
{
    dassert(pSprite != NULL);
    if (pSprite->statnum == kStatDude && pSprite->type != kDudeBat && pSprite->type != kDudeRat && pSprite->type != kDudeInnocent && pSprite->type != kDudeBurningInnocent)
        at0++;
}

void CKillMgr::AddKill(spritetype* pSprite)
{
    dassert(pSprite != NULL);
    if (pSprite->statnum == kStatDude && pSprite->type != kDudeBat && pSprite->type != kDudeRat && pSprite->type != kDudeInnocent && pSprite->type != kDudeBurningInnocent)
        at4++;
}

void CKillMgr::RemoveKill(spritetype* pSprite)
{
    if (gKillMgr.at4 <= 0)
        return;
    dassert(pSprite != NULL);
    if (pSprite->statnum == kStatDude && pSprite->type != kDudeBat && pSprite->type != kDudeRat && pSprite->type != kDudeInnocent && pSprite->type != kDudeBurningInnocent)
        at4--;
}

void CKillMgr::CountTotalKills(void)
{
    at0 = 0;
    for (int nSprite = headspritestat[kStatDude]; 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.", nSprite);
        AddCount(pSprite);
    }
}

void CKillMgr::Draw(void)
{
    char pBuffer[40];
    if (gGameOptions.nGameType == kGameTypeSinglePlayer)
    {
        viewDrawText(1, "KILLS:", 75, 50, -128, 0, 0, 1);
        sprintf(pBuffer, "%2d", at4);
        viewDrawText(1, pBuffer, 160, 50, -128, 0, 0, 1);
        viewDrawText(1, "OF", 190, 50, -128, 0, 0, 1);
        sprintf(pBuffer, "%2d", at0);
        viewDrawText(1, pBuffer, 220, 50, -128, 0, 0, 1);
    }
    else
    {
        viewDrawText(3, "#", 85, 35, -128, 0, 0, 1);
        viewDrawText(3, "NAME", 100, 35, -128, 0, 0, 1);
        viewDrawText(3, "FRAGS", 210, 35, -128, 0, 0, 1);
        int nStart = 0;
        int nEnd = gInitialNetPlayers;
        //if (dword_28E3D4 == 1)
        //{
        //    nStart++;
        //    nEnd++;
        //}
        for (int i = nStart; i < nEnd; i++)
        {
            sprintf(pBuffer, "%-2d", i);
            viewDrawText(3, pBuffer, 85, 50+8*i, -128, 0, 0, 1);
            sprintf(pBuffer, "%s", gProfile[i].name);
            viewDrawText(3, pBuffer, 100, 50+8*i, -128, 0, 0, 1);
            sprintf(pBuffer, "%d", gPlayer[i].fragCount);
            viewDrawText(3, pBuffer, 210, 50+8*i, -128, 0, 0, 1);
        }
    }
}

void CKillMgr::Clear(void)
{
    at0 = at4 = 0;
}

CSecretMgr::CSecretMgr(void)
{
    Clear();
}

void CSecretMgr::SetCount(int nCount)
{
    nAllSecrets = nCount;
}

void CSecretMgr::Found(int nType)
{
    if (nType == 0) nNormalSecretsFound++;
    else if (nType < 0) {
        viewSetSystemMessage("Invalid secret type %d triggered.", nType);
        return;
    } else nSuperSecretsFound++;

    if (gGameOptions.nGameType == kGameTypeSinglePlayer) {
        switch (Random(2)) {
            case 0:
                viewSetMessage("A secret is revealed.", 0, MESSAGE_PRIORITY_SECRET);
                break;
            case 1:
                viewSetMessage("You found a secret.", 0, MESSAGE_PRIORITY_SECRET);
                break;
        }
    }
}

void CSecretMgr::Draw(void)
{
    char pBuffer[40];
    viewDrawText(1, "SECRETS:", 75, 70, -128, 0, 0, 1);
    sprintf(pBuffer, "%2d", nNormalSecretsFound);
    viewDrawText(1, pBuffer, 160, 70, -128, 0, 0, 1);
    viewDrawText(1, "OF", 190, 70, -128, 0, 0, 1);
    sprintf(pBuffer, "%2d", max(gSecretMgr.nNormalSecretsFound, gSecretMgr.nAllSecrets));
    viewDrawText(1, pBuffer, 220, 70, -128, 0, 0, 1);
    if (nSuperSecretsFound > 0)
        viewDrawText(1, "YOU FOUND A SUPER SECRET!", 160, 100, -128, 2, 1, 1);
}

void CSecretMgr::Clear(void)
{
    nAllSecrets = nNormalSecretsFound = nSuperSecretsFound = 0;
}

class EndGameLoadSave : public LoadSave {
public:
    virtual void Load(void);
    virtual void Save(void);
};

void EndGameLoadSave::Load(void)
{
    Read(&gSecretMgr.nAllSecrets, 4);
    Read(&gSecretMgr.nNormalSecretsFound, 4);
    Read(&gSecretMgr.nSuperSecretsFound, 4);
    Read(&gKillMgr.at0, 4);
    Read(&gKillMgr.at4, 4);
}

void EndGameLoadSave::Save(void)
{
    Write(&gSecretMgr.nAllSecrets, 4);
    Write(&gSecretMgr.nNormalSecretsFound, 4);
    Write(&gSecretMgr.nSuperSecretsFound, 4);
    Write(&gKillMgr.at0, 4);
    Write(&gKillMgr.at4, 4);
}

CEndGameMgr gEndGameMgr;
CSecretMgr gSecretMgr;
CKillMgr gKillMgr;
static EndGameLoadSave *myLoadSave;

void EndGameLoadSaveConstruct(void)
{
    myLoadSave = new EndGameLoadSave();
}


code:
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
#pragma once
#include "build.h"
#include "common_game.h"

class CEndGameMgr {
public:
    char at0;
    char at1;
    CEndGameMgr();
    void Setup(void);
    void ProcessKeys(void);
    void Draw(void);
    void Finish(void);
};

class CKillMgr {
public:
    int at0, at4;
    CKillMgr();
    void SetCount(int);
    void AddCount(int);
    void AddCount(spritetype *pSprite);
    void AddKill(spritetype *pSprite);
    void RemoveKill(spritetype *pSprite);
    void CountTotalKills(void);
    void Clear(void);
    void Draw(void);
};

class CSecretMgr {
public:
    int nAllSecrets, nNormalSecretsFound, nSuperSecretsFound;
    CSecretMgr();
    void SetCount(int);
    void Found(int);
    void Clear(void);
    void Draw(void);
};

extern CEndGameMgr gEndGameMgr;
extern CSecretMgr gSecretMgr;
extern CKillMgr gKillMgr;


code:
//-------------------------------------------------------------------------
/*
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "build.h"
#include "common_game.h"

#include "callback.h"
#include "db.h"
#include "eventq.h"
#include "globals.h"
#include "loadsave.h"
#include "pqueue.h"
#include "triggers.h"
#include "view.h"
#ifdef NOONE_EXTENSIONS
#include "nnexts.h"
#endif

class EventQueue
{
public:
    PriorityQueue<EVENT>* PQueue;
    EventQueue()
    {
        PQueue = NULL;
    }
    bool IsNotEmpty(unsigned int nTime)
    {
        return PQueue->Size() > 0 && nTime >= PQueue->LowestPriority();
    }
    EVENT ERemove(void)
    {
        return PQueue->Remove();
    }
    void Kill(int, int);
    void Kill(int idx, int type, int causer);
    void Kill(int, int, CALLBACK_ID);
};

EventQueue eventQ;
void EventQueue::Kill(int a1, int a2)
{
    PQueue->Kill([=](EVENT nItem)->bool {return (nItem.index == a1 && nItem.type == a2); });
}

void EventQueue::Kill(int idx, int type, int causer)
{
    PQueue->Kill([=](EVENT nItem)->bool { return (nItem.index == idx && nItem.type == type && nItem.causer == causer); });
}

void EventQueue::Kill(int a1, int a2, CALLBACK_ID a3)
{
    PQueue->Kill([=](EVENT nItem)->bool {return (nItem.index == a1 && nItem.type == a2 && nItem.cmd == kCmdCallback && nItem.funcID == (unsigned int)a3); });
}

RXBUCKET rxBucket[kChannelMax+1];

int GetBucketChannel(const RXBUCKET *pRX)
{
    switch (pRX->type) {
        case 6: {
            int nIndex = pRX->index;
            int nXIndex = sector[nIndex].extra;
            dassert(nXIndex > 0);
            return xsector[nXIndex].rxID;
        }
        case 0: {
            int nIndex = pRX->index;
            int nXIndex = wall[nIndex].extra;
            dassert(nXIndex > 0);
            return xwall[nXIndex].rxID;
        }
        case 3: {
            int nIndex = pRX->index;
            int nXIndex = sprite[nIndex].extra;
            dassert(nXIndex > 0);
            return xsprite[nXIndex].rxID;
        }
    }
    return 0;
}

#if 0
int CompareChannels(const void *a, const void *b)
{
    return GetBucketChannel((const RXBUCKET*)a)-GetBucketChannel((const RXBUCKET*)b);
}
#else
static int CompareChannels(RXBUCKET *a, RXBUCKET *b)
{
    return GetBucketChannel(a) - GetBucketChannel(b);
}
#endif

static RXBUCKET *SortGetMiddle(RXBUCKET *a1, RXBUCKET *a2, RXBUCKET *a3)
{
    if (CompareChannels(a1, a2) > 0)
    {
        if (CompareChannels(a1, a3) > 0)
        {
            if (CompareChannels(a2, a3) > 0)
                return a2;
            return a3;
        }
        return a1;
    }
    else
    {
        if (CompareChannels(a1, a3) < 0)
        {
            if (CompareChannels(a2, a3) > 0)
                return a3;
            return a2;
        }
        return a1;
    }
}

static void SortSwap(RXBUCKET *a, RXBUCKET *b)
{
    RXBUCKET t = *a;
    *a = *b;
    *b = t;
}

static void SortRXBucket(int nCount)
{
    RXBUCKET *v144[32];
    int vc4[32];
    int v14 = 0;
    RXBUCKET *pArray = rxBucket;
    while (true)
    {
        while (nCount > 1)
        {
            if (nCount < 16)
            {
                for (int nDist = 3; nDist > 0; nDist -= 2)
                {
                    for (RXBUCKET *pI = pArray+nDist; pI < pArray+nCount; pI += nDist)
                    {
                        for (RXBUCKET *pJ = pI; pJ > pArray && CompareChannels(pJ-nDist, pJ) > 0; pJ -= nDist)
                        {
                            SortSwap(pJ, pJ-nDist);
                        }
                    }
                }
                break;
            }
            RXBUCKET *v30, *vdi, *vsi;
            vdi = pArray + nCount / 2;
            if (nCount > 29)
            {
                v30 = pArray;
                vsi = pArray + nCount-1;
                if (nCount > 42)
                {
                    int v20 = nCount / 8;
                    v30 = SortGetMiddle(v30, v30+v20, v30+v20*2);
                    vdi = SortGetMiddle(vdi-v20, vdi, vdi+v20);
                    vsi = SortGetMiddle(vsi-v20*2, vsi-v20, vsi);
                }
                vdi = SortGetMiddle(v30, vdi, vsi);
            }
            RXBUCKET v44 = *vdi;
            RXBUCKET *vc = pArray;
            RXBUCKET *v8 = pArray+nCount-1;
            RXBUCKET *vbx = vc;
            RXBUCKET *v4 = v8;
            while (true)
            {
                while (vbx <= v4)
                {
                    int nCmp = CompareChannels(vbx, &v44);
                    if (nCmp > 0)
                        break;
                    if (nCmp == 0)
                    {
                        SortSwap(vbx, vc);
                        vc++;
                    }
                    vbx++;
                }
                while (vbx <= v4)
                {
                    int nCmp = CompareChannels(v4, &v44);
                    if (nCmp < 0)
                        break;
                    if (nCmp == 0)
                    {
                        SortSwap(v4, v8);
                        v8--;
                    }
                    v4--;
                }
                if (vbx > v4)
                    break;
                SortSwap(vbx, v4);
                v4--;
                vbx++;
            }
            RXBUCKET *v2c = pArray+nCount;
            int vt = ClipHigh(vbx-vc, vc-pArray);
            for (int i = 0; i < vt; i++)
            {
                SortSwap(&vbx[i-vt], &pArray[i]);
            }
            vt = ClipHigh(v8-v4, v2c-v8-1);
            for (int i = 0; i < vt; i++)
            {
                SortSwap(&v2c[i-vt], &vbx[i]);
            }
            int vvsi = v8-v4;
            int vvdi = vbx-vc;
            if (vvsi >= vvdi)
            {
                vc4[v14] = vvsi;
                v144[v14] = v2c-vvsi;
                nCount = vvdi;
                v14++;
            }
            else
            {
                vc4[v14] = vvdi;
                v144[v14] = pArray;
                nCount = vvsi;
                pArray = v2c - vvsi;
                v14++;
            }
        }
        if (v14 == 0)
            return;
        v14--;
        pArray = v144[v14];
        nCount = vc4[v14];
    }
}

unsigned short bucketHead[1024+1];

void evInit(void)
{
    if (eventQ.PQueue)
        delete eventQ.PQueue;
    if (VanillaMode())
        eventQ.PQueue = new VanillaPriorityQueue<EVENT>();
    else
        eventQ.PQueue = new StdPriorityQueue<EVENT>();
    eventQ.PQueue->Clear();
    int nCount = 0;
    for (int i = 0; i < numsectors; i++)
    {
        int nXSector = sector[i].extra;
        if (nXSector >= kMaxXSectors)
            ThrowError("Invalid xsector reference in sector %d", i);
        if (nXSector > 0 && xsector[nXSector].rxID > 0)
        {
            dassert(nCount < kChannelMax);
            rxBucket[nCount].type = 6;
            rxBucket[nCount].index = i;
            nCount++;
        }
    }
    for (int i = 0; i < numwalls; i++)
    {
        int nXWall = wall[i].extra;
        if (nXWall >= kMaxXWalls)
            ThrowError("Invalid xwall reference in wall %d", i);
        if (nXWall > 0 && xwall[nXWall].rxID > 0)
        {
            dassert(nCount < kChannelMax);
            rxBucket[nCount].type = 0;
            rxBucket[nCount].index = i;
            nCount++;
        }
    }
    for (int i = 0; i < kMaxSprites; i++)
    {
        if (sprite[i].statnum < kMaxStatus)
        {
            int nXSprite = sprite[i].extra;
            if (nXSprite >= kMaxXSprites)
                ThrowError("Invalid xsprite reference in sprite %d", i);
            if (nXSprite > 0 && xsprite[nXSprite].rxID > 0)
            {
                dassert(nCount < kChannelMax);
                rxBucket[nCount].type = 3;
                rxBucket[nCount].index = i;
                nCount++;
            }
        }
    }
    SortRXBucket(nCount);
    int i, j = 0;
    for (i = 0; i < 1024; i++)
    {
        bucketHead[i] = j;
        while(j < nCount && GetBucketChannel(&rxBucket[j]) == i)
            j++;
    }
    bucketHead[i] = j;
}

char evGetSourceState(int nType, int nIndex)
{
    switch (nType)
    {
    case 6:
    {
        int nXIndex = sector[nIndex].extra;
        dassert(nXIndex > 0 && nXIndex < kMaxXSectors);
        return xsector[nXIndex].state;
    }
    case 0:
    {
        int nXIndex = wall[nIndex].extra;
        dassert(nXIndex > 0 && nXIndex < kMaxXWalls);
        return xwall[nXIndex].state;
    }
    case 3:
    {
        int nXIndex = sprite[nIndex].extra;
        dassert(nXIndex > 0 && nXIndex < kMaxXSprites);
        return xsprite[nXIndex].state;
    }
    }
    return 0;
}

void evSend(int nIndex, int nType, int rxId, COMMAND_ID command, int causerID)
{
    switch (command) {
        case kCmdState:
            command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff;
            break;
        case kCmdNotState:
            command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn;
            break;
        default:
            break;
    }

    EVENT event;
    event.index = nIndex;
    event.type = nType;
    event.cmd = command;
    #ifdef NOONE_EXTENSIONS
        event.causer = (gModernMap) ? causerID : kCauserGame;
    #else
        event.causer = kCauserGame;
    #endif

    switch (rxId) {
    case kChannelTextOver:
        if (command >= kCmdNumberic) trTextOver(command - kCmdNumberic);
        else viewSetSystemMessage("Invalid TextOver command by xobject #%d (object type %d)", nIndex, nType);
        return;
    case kChannelLevelExitNormal:
        levelEndLevel(kLevelExitNormal);
        return;
    case kChannelLevelExitSecret:
        levelEndLevel(kLevelExitSecret);
        return;
    #ifdef NOONE_EXTENSIONS
    // finished level and load custom level � via numbered command.
    case kChannelModernEndLevelCustom:
        if (command >= kCmdNumberic) levelEndLevelCustom(command - kCmdNumberic);
        else viewSetSystemMessage("Invalid Level-Exit# command by xobject #%d (object type %d)", nIndex, nType);
        return;
    #endif
    case kChannelSetTotalSecrets:
        if (command >= kCmdNumberic) levelSetupSecret(command - kCmdNumberic);
        else viewSetSystemMessage("Invalid Total-Secrets command by xobject #%d (object type %d)", nIndex, nType);
        break;
    case kChannelSecretFound:
        if (command >= kCmdNumberic) levelTriggerSecret(command - kCmdNumberic);
        else viewSetSystemMessage("Invalid Trigger-Secret command by xobject #%d (object type %d)", nIndex, nType);
        break;
    case kChannelRemoteBomb0:
    case kChannelRemoteBomb1:
    case kChannelRemoteBomb2:
    case kChannelRemoteBomb3:
    case kChannelRemoteBomb4:
    case kChannelRemoteBomb5:
    case kChannelRemoteBomb6:
    case kChannelRemoteBomb7:
        for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
        {
            spritetype* pSprite = &sprite[nSprite];
            if (pSprite->flags & 32)
                continue;
            int nXSprite = pSprite->extra;
            if (nXSprite > 0)
            {
                XSPRITE* pXSprite = &xsprite[nXSprite];
                if (pXSprite->rxID == rxId)
                    trMessageSprite(nSprite, event);
            }
        }
        return;
    case kChannelTeamAFlagCaptured:
    case kChannelTeamBFlagCaptured:
        for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nextspritestat[nSprite])
        {
            spritetype* pSprite = &sprite[nSprite];
            if (pSprite->flags & 32)
                continue;
            int nXSprite = pSprite->extra;
            if (nXSprite > 0)
            {
                XSPRITE* pXSprite = &xsprite[nXSprite];
                if (pXSprite->rxID == rxId)
                    trMessageSprite(nSprite, event);
            }
        }
        return;
    default:
        break;
    }

    #ifdef NOONE_EXTENSIONS
    if (gModernMap)
    {
        // allow to send commands on player sprites
        PLAYER* pPlayer = NULL;
        if (playerRXRngIsFine(rxId))
        {
            if ((pPlayer = getPlayerById((rxId - kChannelPlayer7) + kMaxPlayers)) != NULL)
            {
                if (command == kCmdEventKillFull)
                    evKill(pPlayer->nSprite, OBJ_SPRITE);
                else
                    trMessageSprite(pPlayer->nSprite, event);
            }

            return;

        }
        else if (rxId == kChannelAllPlayers)
        {
            for (int i = 0; i < kMaxPlayers; i++)
            {
                if ((pPlayer = getPlayerById(i)) != NULL)
                {
                    if (command == kCmdEventKillFull)
                        evKill(pPlayer->nSprite, OBJ_SPRITE);
                    else
                        trMessageSprite(pPlayer->nSprite, event);
                }
            }

            return;
        }
        // send command on sprite which created the event sequence
        else if (rxId == kChannelEventCauser && event.causer < kCauserGame)
        {
            spritetype* pSpr = &sprite[event.causer];
            if (!(pSpr->flags & kHitagFree) && !(pSpr->flags & kHitagRespawn))
            {
                if (command == kCmdEventKillFull)
                    evKill(causerID, OBJ_SPRITE);
                else
                    trMessageSprite(event.causer, event);
            }

            return;
        }
        else if (command == kCmdEventKillFull)
        {
            killEvents(rxId, command);
            return;
        }
    }
    #endif

    for (int i = bucketHead[rxId]; i < bucketHead[rxId+1]; i++) {
        if (event.type != rxBucket[i].type || event.index != rxBucket[i].index) {
            switch (rxBucket[i].type) {
                case 6:
                    trMessageSector(rxBucket[i].index, event);
                    break;
                case 0:
                    trMessageWall(rxBucket[i].index, event);
                    break;
                case 3:
                {
                    int nSprite = rxBucket[i].index;
                    spritetype *pSprite = &sprite[nSprite];
                    if (pSprite->flags&32)
                        continue;
                    int nXSprite = pSprite->extra;
                    if (nXSprite > 0)
                    {
                        XSPRITE *pXSprite = &xsprite[nXSprite];
                        if (pXSprite->rxID > 0)
                            trMessageSprite(nSprite, event);
                    }
                    break;
                }
            }
        }
    }
}

void evPost(int nIndex, int nType, unsigned int nDelta, COMMAND_ID command, int causerID) {
    dassert(command != kCmdCallback);
    if (command == kCmdState) command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff;
    else if (command == kCmdNotState) command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn;
    EVENT evn = {};
    evn.index = nIndex;
    evn.type = nType;
    evn.cmd = command;
    #ifdef NOONE_EXTENSIONS
        evn.causer = (gModernMap) ? causerID : kCauserGame;
    #else
        evn.causer = kCauserGame;
    #endif

    eventQ.PQueue->Insert((int)gFrameClock+nDelta, evn);
}

void evPost(int nIndex, int nType, unsigned int nDelta, CALLBACK_ID callback) {
    EVENT evn = {};
    evn.index = nIndex;
    evn.type = nType;
    evn.cmd = kCmdCallback;
    evn.funcID = callback;
    evn.causer = kCauserGame;
    eventQ.PQueue->Insert((int)gFrameClock+nDelta, evn);
}

void evProcess(unsigned int nTime)
{
#if 0
    while (1)
    {
        // Inlined?
        char bDone;
        if (eventQ.fNodeCount > 0 && nTime >= eventQ.queueItems[1])
            bDone = 1;
        else
            bDone = 0;
        if (!bDone)
            break;
#endif
    while(eventQ.IsNotEmpty(nTime))
    {
        EVENT event = eventQ.ERemove();
        if (event.cmd == kCmdCallback)
        {
            dassert(event.funcID < kCallbackMax);
            dassert(gCallback[event.funcID] != NULL);
            gCallback[event.funcID](event.index);
        }
        else
        {
            switch (event.type)
            {
            case 6:
                trMessageSector(event.index, event);
                break;
            case 0:
                trMessageWall(event.index, event);
                break;
            case 3:
                trMessageSprite(event.index, event);
                break;
            }
        }
    }
}

void evKill(int a1, int a2)
{
    eventQ.Kill(a1, a2);
}

void evKill(int idx, int type, int causer)
{
    #ifdef NOONE_EXTENSIONS
    if (gModernMap)
        eventQ.Kill(idx, type, causer);
    else
    #endif
        eventQ.Kill(idx, type);
}

void evKill(int a1, int a2, CALLBACK_ID a3)
{
    eventQ.Kill(a1, a2, a3);
}

class EventQLoadSave : public LoadSave
{
public:
    virtual void Load();
    virtual void Save();
};

void EventQLoadSave::Load()
{
    if (eventQ.PQueue)
        delete eventQ.PQueue;
    Read(&eventQ, sizeof(eventQ));
    if (VanillaMode())
        eventQ.PQueue = new VanillaPriorityQueue<EVENT>();
    else
        eventQ.PQueue = new StdPriorityQueue<EVENT>();
    int nEvents;
    Read(&nEvents, sizeof(nEvents));
    for (int i = 0; i < nEvents; i++)
    {
        EVENT event = {};
        unsigned int eventtime;
        Read(&eventtime, sizeof(eventtime));
        Read(&event, sizeof(event));
        eventQ.PQueue->Insert(eventtime, event);
    }
    Read(rxBucket, sizeof(rxBucket));
    Read(bucketHead, sizeof(bucketHead));
}

void EventQLoadSave::Save()
{
    EVENT events[1024];
    unsigned int eventstime[1024];
    Write(&eventQ, sizeof(eventQ));
    int nEvents = eventQ.PQueue->Size();
    Write(&nEvents, sizeof(nEvents));
    for (int i = 0; i < nEvents; i++)
    {
        eventstime[i] = eventQ.PQueue->LowestPriority();
        events[i] = eventQ.ERemove();
        Write(&eventstime[i], sizeof(eventstime[i]));
        Write(&events[i], sizeof(events[i]));
    }
    dassert(eventQ.PQueue->Size() == 0);
    for (int i = 0; i < nEvents; i++)
    {
        eventQ.PQueue->Insert(eventstime[i], events[i]);
    }
    Write(rxBucket, sizeof(rxBucket));
    Write(bucketHead, sizeof(bucketHead));
}

static EventQLoadSave *myLoadSave;

void EventQLoadSaveConstruct(void)
{
    myLoadSave = new EventQLoadSave();
}


code:
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
#pragma once
#include "callback.h"
enum {
kChannelZero                        = 0,
kChannelSetTotalSecrets             = 1,
kChannelSecretFound                 = 2,
kChannelTextOver                    = 3,
kChannelLevelExitNormal             = 4,
kChannelLevelExitSecret             = 5,
kChannelModernEndLevelCustom        = 6, // custom level number end (gModernMap)
kChannelLevelStart                  = 7,
kChannelLevelStartMatch             = 8, // DM and TEAMS
kChannelLevelStartCoop              = 9,
kChannelLevelStartTeamsOnly         = 10,
kChannelPlayerDeathTeamA            = 15,
kChannelPlayerDeathTeamB            = 16,
/////////////////////////////
// level start channels for specific ports
kChannelLevelStartNBLOOD            = 17, // *NBlood only* must trigger it at level start (gModernMap)
kChannelLevelStartRAZE              = 18, // *Raze only* must trigger it at level start (gModernMap)
// channels of players to send commands on
kChannelAllPlayers                  = 29,
kChannelPlayer0                     = 30,
kChannelPlayer1,
kChannelPlayer2,
kChannelPlayer3,
kChannelPlayer4,
kChannelPlayer5,
kChannelPlayer6,
kChannelPlayer7,
// channel of event causer
kChannelEventCauser                 = 50,
// map requires modern features to work properly
kChannelMapModernize                = 60,
/////////////////////////////
kChannelTeamAFlagCaptured           = 80,
kChannelTeamBFlagCaptured,
kChannelRemoteBomb0                 = 90,
kChannelRemoteBomb1,
kChannelRemoteBomb2,
kChannelRemoteBomb3,
kChannelRemoteBomb4,
kChannelRemoteBomb5,
kChannelRemoteBomb6,
kChannelRemoteBomb7,
kChannelUser                        = 100,
kChannelUserMax                     = 1024,
kChannelMax                         = 4096,
};

struct RXBUCKET
{
    unsigned int index : 14;
    unsigned int type : 3;
};
extern RXBUCKET rxBucket[];
extern unsigned short bucketHead[];

enum COMMAND_ID {
kCmdOff                     = 0,
kCmdOn                      = 1,
kCmdState                   = 2,
kCmdToggle                  = 3,
kCmdNotState                = 4,
kCmdLink                    = 5,
kCmdLock                    = 6,
kCmdUnlock                  = 7,
kCmdToggleLock              = 8,
kCmdStopOff                 = 9,
kCmdStopOn                  = 10,
kCmdStopNext                = 11,
kCmdCounterSector           = 12,
kCmdCallback                = 20,
kCmdRepeat                  = 21,

kCmdSpritePush              = 30,
kCmdSpriteImpact            = 31,
kCmdSpritePickup            = 32,
kCmdSpriteTouch             = 33,
kCmdSpriteSight             = 34,
kCmdSpriteProximity         = 35,
kCmdSpriteExplode           = 36,

kCmdSectorPush              = 40,
kCmdSectorImpact            = 41,
kCmdSectorEnter             = 42,
kCmdSectorExit              = 43,

kCmdWallPush                = 50,
kCmdWallImpact              = 51,
kCmdWallTouch               = 52,
#ifdef NOONE_EXTENSIONS
kCmdSectorMotionPause       = 13,   // stops motion of the sector
kCmdSectorMotionContinue    = 14,   // continues motion of the sector
kCmdDudeFlagsSet            = 15,   // copy dudeFlags from sprite to dude
kCmdEventKillFull           = 16,   // immediately kill the pending object events
kCmdModernUse               = 53,   // used by most of modern types
#endif

kCmdNumberic                = 64, // 64: 0, 65: 1 and so on up to 255
kCmdModernFeaturesEnable    = 100, // must be in object with kChannelMapModernize RX / TX
kCmdNumbericMax             = 255,
};

inline bool playerRXRngIsFine(int rx) {
    return (rx >= kChannelPlayer0 && rx < kChannelPlayer7);
}

inline bool channelRangeIsFine(int channel) {
    return (channel >= kChannelUser && channel < kChannelUserMax);
}

struct EVENT {
    unsigned int index:     14; // index
    unsigned int type:      3; // type
    unsigned int cmd:       8; // cmd
    unsigned int funcID:    8; // callback
    unsigned int causer:    14; // spritenum of object which initiated this event (kCauserGame == initiated by the game)
};

void evInit(void);
char evGetSourceState(int nType, int nIndex);
void evSend(int nIndex, int nType, int rxId, COMMAND_ID command, int causerID);
void evPost(int nIndex, int nType, unsigned int nDelta, COMMAND_ID command, int causerID);
void evPost(int nIndex, int nType, unsigned int nDelta, CALLBACK_ID callback);
void evProcess(unsigned int nTime);
void evKill(int a1, int a2);
void evKill(int idx, int type, int causer);
void evKill(int a1, int a2, CALLBACK_ID a3);


code:
//-------------------------------------------------------------------------
/*
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "build.h"
#include "common_game.h"
#include "blood.h"
#include "fire.h"
#include "globals.h"
#include "misc.h"
#include "tile.h"

int fireSize = 128;
int gDamping = 6;

/*extern "C" */char CoolTable[1024];

void CellularFrame(char *pFrame, int sizeX, int sizeY);

char FrameBuffer[17280];
char SeedBuffer[16][128];
char *gCLU;

void InitSeedBuffers(void)
{
    for (int i = 0; i < 16; i++)
        for (int j = 0; j < fireSize; j += 2)
            SeedBuffer[i][j] = SeedBuffer[i][j+1] = wrand();
}

void BuildCoolTable(void)
{
    for (int i = 0; i < 1024; i++)
        CoolTable[i] = ClipLow((i-gDamping) / 4, 0);
}

void DoFireFrame(void)
{
    int nRand = qrand()&15;
    for (int i = 0; i < 3; i++)
    {
        memcpy(FrameBuffer+16896+i*128, SeedBuffer[nRand], 128);
    }
    CellularFrame(FrameBuffer, 128, 132);
    char *pData = tileLoadTile(2342);
    char *pSource = FrameBuffer;
    int x = fireSize;
    do
    {
        int y = fireSize;
        char *pDataBak = pData;
        do
        {
            *pData = gCLU[*pSource];
            pSource++;
            pData += fireSize;
        } while (--y);
        pData = pDataBak + 1;
    } while (--x);
}

void FireInit(void)
{
    memset(FrameBuffer, 0, sizeof(FrameBuffer));
    BuildCoolTable();
    InitSeedBuffers();
    DICTNODE *pNode = gSysRes.Lookup("RFIRE", "CLU");
    if (!pNode)
        ThrowError("RFIRE.CLU not found");
    gCLU = (char*)gSysRes.Lock(pNode);
    for (int i = 0; i < 100; i++)
        DoFireFrame();
}

void FireProcess(void)
{
    static ClockTicks lastUpdate;
    if (totalclock < lastUpdate || lastUpdate + 2 < totalclock)
    {
        DoFireFrame();
        lastUpdate = totalclock;
        tileInvalidate(2342, -1, -1);
    }
}

void CellularFrame(char *pFrame, int sizeX, int sizeY)
{
    int nSquare = sizeX * sizeY;
    unsigned char *pPtr1 = (unsigned char*)pFrame;
    while (nSquare--)
    {
        unsigned char *pPtr2 = pPtr1+sizeX;
        int sum = *(pPtr2-1) + *pPtr2 + *(pPtr2+1) + *(pPtr2+sizeX);
        if (*(pPtr2+sizeX) > 96)
        {
            pPtr2 += sizeX;
            sum += *(pPtr2-1) + *pPtr2 + *(pPtr2+1) + *(pPtr2+sizeX);
            sum >>= 1;
        }
        *pPtr1 = CoolTable[sum];
        pPtr1++;
    }
}


code:
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
#pragma once

void FireInit(void);
void FireProcess(void);


code:
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------

// function.h

// file created by makehead.exe
// these headers contain default key assignments, as well as
// default button assignments and game function names
// axis defaults are also included

#ifndef function_public_h_
#define function_public_h_
#ifdef __cplusplus
extern "C" {
#endif

#define NUMGAMEFUNCTIONS 55
#define MAXGAMEFUNCLEN 32

extern char gamefunctions[NUMGAMEFUNCTIONS][MAXGAMEFUNCLEN];
extern const char keydefaults[NUMGAMEFUNCTIONS*2][MAXGAMEFUNCLEN];
extern const char oldkeydefaults[NUMGAMEFUNCTIONS*2][MAXGAMEFUNCLEN];

enum GameFunction_t
   {
   gamefunc_Move_Forward,
   gamefunc_Move_Backward,
   gamefunc_Turn_Left,
   gamefunc_Turn_Right,
   gamefunc_Turn_Around,
   gamefunc_Strafe,
   gamefunc_Strafe_Left,
   gamefunc_Strafe_Right,
   gamefunc_Jump,
   gamefunc_Crouch,
   gamefunc_Run,
   gamefunc_AutoRun,
   gamefunc_Open,
   gamefunc_Weapon_Fire,
   gamefunc_Weapon_Special_Fire,
   gamefunc_Aim_Up,
   gamefunc_Aim_Down,
   gamefunc_Aim_Center,
   gamefunc_Look_Up,
   gamefunc_Look_Down,
   gamefunc_Tilt_Left,
   gamefunc_Tilt_Right,
   gamefunc_Weapon_1,
   gamefunc_Weapon_2,
   gamefunc_Weapon_3,
   gamefunc_Weapon_4,
   gamefunc_Weapon_5,
   gamefunc_Weapon_6,
   gamefunc_Weapon_7,
   gamefunc_Weapon_8,
   gamefunc_Weapon_9,
   gamefunc_Weapon_10,
   gamefunc_Inventory_Use,
   gamefunc_Inventory_Left,
   gamefunc_Inventory_Right,
   gamefunc_Map_Toggle,
   gamefunc_Map_Follow_Mode,
   gamefunc_Shrink_Screen,
   gamefunc_Enlarge_Screen,
   gamefunc_Send_Message,
   gamefunc_See_Coop_View,
   gamefunc_See_Chase_View,
   gamefunc_Mouse_Aiming,
   gamefunc_Toggle_Crosshair,
   gamefunc_Next_Weapon,
   gamefunc_Previous_Weapon,
   gamefunc_Holster_Weapon,
   gamefunc_Show_Opponents_Weapon,
   gamefunc_BeastVision,
   gamefunc_CrystalBall,
   gamefunc_JumpBoots,
   gamefunc_MedKit,
   gamefunc_ProximityBombs,
   gamefunc_RemoteBombs,
   gamefunc_Show_Console,
   };
#ifdef __cplusplus
}
#endif
#endif


code:
//-------------------------------------------------------------------------
/*
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 "build.h"
#include "common_game.h"

#include "actor.h"
#include "blood.h"
#include "callback.h"
#include "config.h"
#include "db.h"
#include "eventq.h"
#include "fx.h"
#include "gameutil.h"
#include "levels.h"
#include "seq.h"
#include "tile.h"
#include "trig.h"
#include "view.h"

CFX gFX;

struct FXDATA {
    CALLBACK_ID funcID;
    char detail;
    short seq;
    short flags;
    int gravity;
    int airdrag;
    int duration;
    short picnum;
    unsigned char xrepeat;
    unsigned char yrepeat;
    short cstat;
    signed char shade;
    char pal;
};

FXDATA gFXData[] = {
    { kCallbackNone, 0, 49, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 0, 50, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 0, 51, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 0, 52, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 0, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 0, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 0, 45, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 0, 46, 1, -128, 8192, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 2, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 2, 42, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 2, 43, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 48, 3, -256, 8192, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 60, 3, -256, 8192, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackFXBloodBits, 2, 0, 1, 46603, 2048, 480, 2154, 40, 40, 0, -12, 0 },
    { kCallbackNone, 2, 0, 3, 46603, 5120, 480, 2269, 24, 24, 0, -128, 0 },
    { kCallbackNone, 2, 0, 3, 46603, 5120, 480, 1720, 24, 24, 0, -128, 0 },
    { kCallbackNone, 1, 0, 1, 58254, 3072, 480, 2280, 48, 48, 0, -128, 0 },
    { kCallbackNone, 1, 0, 1, 58254, 3072, 480, 3135, 48, 48, 0, -128, 0 },
    { kCallbackNone, 0, 0, 3, 58254, 1024, 480, 3261, 32, 32, 0, 0, 0 },
    { kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3265, 32, 32, 0, 0, 0 },
    { kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3269, 32, 32, 0, 0, 0 },
    { kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3273, 32, 32, 0, 0, 0 },
    { kCallbackNone, 1, 0, 3, 58254, 1024, 480, 3277, 32, 32, 0, 0, 0 },
    { kCallbackNone, 2, 0, 1, -27962, 8192, 600, 1128, 16, 16, 514, -16, 0 }, // bubble 1
    { kCallbackNone, 2, 0, 1, -18641, 8192, 600, 1128, 12, 12, 514, -16, 0 }, // bubble 2
    { kCallbackNone, 2, 0, 1, -9320, 8192, 600, 1128, 8, 8, 514, -16, 0 }, // bubble 3
    { kCallbackNone, 2, 0, 1, -18641, 8192, 600, 1131, 32, 32, 514, -16, 0 },
    { kCallbackFXBloodBits, 2, 0, 3, 27962, 4096, 480, 733, 32, 32, 0, -16, 0 },
    { kCallbackNone, 1, 0, 3, 18641, 4096, 120, 2261, 12, 12, 0, -128, 0 },
    { kCallbackNone, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 0, 3, 58254, 3328, 480, 2185, 48, 48, 0, 0, 0 },
    { kCallbackNone, 0, 0, 3, 58254, 1024, 480, 2620, 48, 48, 0, 0, 0 },
    { kCallbackNone, 1, 55, 1, -13981, 5120, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 56, 1, -13981, 5120, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 57, 1, 0, 2048, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 58, 1, 0, 2048, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 2, 0, 0, 0, 0, 960, 956, 32, 32, 610, 0, 0 },
    { kCallbackFXBouncingSleeve, 2, 62, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackFXBouncingSleeve, 2, 63, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackFXBouncingSleeve, 2, 64, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackFXBouncingSleeve, 2, 65, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackFXBouncingSleeve, 2, 66, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackFXBouncingSleeve, 2, 67, 0, 46603, 1024, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 0, 3, 0, 0, 0, 838, 16, 16, 80, -8, 0 },
    { kCallbackNone, 0, 0, 3, 34952, 8192, 0, 2078, 64, 64, 0, -8, 0 },
    { kCallbackNone, 0, 0, 3, 34952, 8192, 0, 1106, 64, 64, 0, -8, 0 },
    { kCallbackNone, 0, 0, 3, 58254, 3328, 480, 2406, 48, 48, 0, 0, 0 },
    { kCallbackNone, 1, 0, 3, 46603, 4096, 480, 3511, 64, 64, 0, -128, 0 },
    { kCallbackNone, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 2, 11, 3, -256, 8192, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 2, 11, 3, 0, 8192, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { kCallbackNone, 1, 30, 3, 0, 0, 0, 0, 40, 40, 80, -8, 0 },
    { kCallbackFXPodBloodSplat, 2, 0, 3, 27962, 4096, 480, 4023, 32, 32, 0, -16, 0 },
    { kCallbackFXPodBloodSplat, 2, 0, 3, 27962, 4096, 480, 4028, 32, 32, 0, -16, 0 },
    { kCallbackNone, 2, 0, 0, 0, 0, 480, 926, 32, 32, 610, -12, 0 },
    { kCallbackNone, 1, 70, 1, -13981, 5120, 0, 0, 0, 0, 0, 0, 0 }
};

void CFX::fxKill(int nSprite)
{
    if (nSprite < 0 || nSprite >= kMaxSprites)
        return;
    evKill(nSprite, 3);
    if (sprite[nSprite].extra > 0)
        seqKill(3, sprite[nSprite].extra);
    DeleteSprite(nSprite);
}

void CFX::fxFree(int nSprite)
{
    if (nSprite < 0 || nSprite >= kMaxSprites)
        return;
    spritetype *pSprite = &sprite[nSprite];
    if (pSprite->extra > 0)
        seqKill(3, pSprite->extra);
    if (pSprite->statnum != kStatFree)
        actPostSprite(nSprite, kStatFree);
}

spritetype * CFX::fxSpawn(FX_ID nFx, int nSector, int x, int y, int z, unsigned int duration)
{
    if (nSector < 0 || nSector >= numsectors)
        return NULL;
    int nSector2 = nSector;
    if (!FindSector(x, y, z, &nSector2))
        return NULL;
    if (gbAdultContent && gGameOptions.nGameType == kGameTypeSinglePlayer)
    {
        switch (nFx)
        {
        case FX_0:
        case FX_1:
        case FX_2:
        case FX_3:
        case FX_13:
        case FX_34:
        case FX_35:
        case FX_36:
            return NULL;
        default:
            break;
        }
    }
    if (nFx < 0 || nFx >= kFXMax)
        return NULL;
    FXDATA *pFX = &gFXData[nFx];
    if (gStatCount[kStatFX] == 512) // hit upper limit of available sprites, don't spawn any more
    {
        int nSprite = headspritestat[kStatFX];
        while ((sprite[nSprite].flags & 32) && nSprite != -1) // scan through sprites for free slot
            nSprite = nextspritestat[nSprite];
        if (nSprite == -1)
            return NULL;
        fxKill(nSprite);
    }
    spritetype *pSprite = actSpawnSprite(nSector, x, y, z, 1, 0);
    pSprite->type = nFx;
    pSprite->picnum = pFX->picnum;
    pSprite->cstat |= pFX->cstat;
    pSprite->shade = pFX->shade;
    pSprite->pal = pFX->pal;
    qsprite_filler[pSprite->index] = pFX->detail;
    if (pFX->xrepeat > 0)
        pSprite->xrepeat = pFX->xrepeat;
    if (pFX->yrepeat > 0)
        pSprite->yrepeat = pFX->yrepeat;
    if ((pFX->flags & 1) && Chance(0x8000))
        pSprite->cstat |= 4;
    if ((pFX->flags & 2) && Chance(0x8000))
        pSprite->cstat |= 8;
    if (pFX->seq)
    {
        int nXSprite = dbInsertXSprite(pSprite->index);
        seqSpawn(pFX->seq, 3, nXSprite, -1);
    }
    if (duration == 0) // no override duration set, load from global fx data struct
        duration = pFX->duration;
    if (duration)
        evPost((int)pSprite->index, 3, duration+Random2(duration>>1), kCallbackRemove);
    return pSprite;
}

void CFX::fxProcess(void)
{
    for (int nSprite = headspritestat[kStatFX]; nSprite >= 0; nSprite = nextspritestat[nSprite])
    {
        spritetype *pSprite = &sprite[nSprite];
        if (pSprite->statnum == kStatFree) // skip free'd fx sprite
            continue;
        viewBackupSpriteLoc(nSprite, pSprite);
        short nSector = pSprite->sectnum;
        dassert(nSector >= 0 && nSector < kMaxSectors);
        dassert(pSprite->type < kFXMax);
        FXDATA *pFXData = &gFXData[pSprite->type];
        actAirDrag(pSprite, pFXData->airdrag);
        if (xvel[nSprite])
            pSprite->x += xvel[nSprite]>>12;
        if (yvel[nSprite])
            pSprite->y += yvel[nSprite]>>12;
        if (zvel[nSprite])
            pSprite->z += zvel[nSprite]>>8;
        // Weird...
        if (xvel[nSprite] || (yvel[nSprite] && pSprite->z >= sector[pSprite->sectnum].floorz))
        {
            updatesector(pSprite->x, pSprite->y, &nSector);
            if (nSector == -1)
            {
                fxFree(nSprite);
                continue;
            }
            if (getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y) <= pSprite->z)
            {
                if (pFXData->funcID < 0 || pFXData->funcID >= kCallbackMax)
                {
                    fxFree(nSprite);
                    continue;
                }
                dassert(gCallback[pFXData->funcID] != NULL);
                gCallback[pFXData->funcID](nSprite);
                continue;
            }
            if (nSector != pSprite->sectnum)
            {
                dassert(nSector >= 0 && nSector < kMaxSectors);
                ChangeSpriteSect(nSprite, nSector);
            }
        }
        if (xvel[nSprite] || yvel[nSprite] || zvel[nSprite])
        {
            int32_t floorZ, ceilZ;
            getzsofslope(nSector, pSprite->x, pSprite->y, &ceilZ, &floorZ);
            if (ceilZ > pSprite->z && !(sector[nSector].ceilingstat&1))
            {
                fxFree(nSprite);
                continue;
            }
            if (floorZ < pSprite->z)
            {
                if (pFXData->funcID < 0 || pFXData->funcID >= kCallbackMax)
                {
                    fxFree(nSprite);
                    continue;
                }
                dassert(gCallback[pFXData->funcID] != NULL);
                gCallback[pFXData->funcID](nSprite);
                continue;
            }
        }
        zvel[nSprite] += pFXData->gravity;
    }
}

void fxSpawnBlood(spritetype *pSprite, int a2)
{
    UNREFERENCED_PARAMETER(a2);
    if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors)
        return;
    int nSector = pSprite->sectnum;
    if (!FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector))
        return;
    if (gbAdultContent && gGameOptions.nGameType == kGameTypeSinglePlayer)
        return;
    spritetype *pBlood = gFX.fxSpawn(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
    if (pBlood)
    {
        pBlood->ang = 1024;
        xvel[pBlood->index] = Random2(0x6aaaa);
        yvel[pBlood->index] = Random2(0x6aaaa);
        zvel[pBlood->index] = -((int)Random(0x10aaaa))-100;
        evPost(pBlood->index, 3, 8, kCallbackFXBloodSpurt);
    }
}

void fxSpawnPodBlood(spritetype *pSprite, int a2)
{
    UNREFERENCED_PARAMETER(a2);
    if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors)
        return;
    int nSector = pSprite->sectnum;
    if (!FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector))
        return;
    if (gbAdultContent && gGameOptions.nGameType == kGameTypeSinglePlayer)
        return;
    spritetype *pSpawn;
    if (pSprite->type == kDudePodGreen)
        pSpawn = gFX.fxSpawn(FX_53, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
    else
        pSpawn = gFX.fxSpawn(FX_54, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z);
    if (pSpawn)
    {
        pSpawn->ang = 1024;
        xvel[pSpawn->index] = Random2(0x6aaaa);
        yvel[pSpawn->index] = Random2(0x6aaaa);
        zvel[pSpawn->index] = -((int)Random(0x10aaaa))-100;
        evPost(pSpawn->index, 3, 8, kCallbackFXPodBloodSpray);
    }
}

void fxSpawnEjectingBrass(spritetype *pSprite, int z, int a3, int a4)
{
    int x = pSprite->x+mulscale28(pSprite->clipdist-4, Cos(pSprite->ang));
    int y = pSprite->y+mulscale28(pSprite->clipdist-4, Sin(pSprite->ang));
    x += mulscale30(a3, Cos(pSprite->ang+512));
    y += mulscale30(a3, Sin(pSprite->ang+512));
    spritetype *pBrass = gFX.fxSpawn((FX_ID)(FX_37+Random(3)), pSprite->sectnum, x, y, z);
    if (pBrass)
    {
        if (!VanillaMode())
            pBrass->ang = Random(2047);
        int nDist = (a4<<18)/120+Random2(((a4/4)<<18)/120);
        int nAngle = pSprite->ang+Random2(56)+512;
        xvel[pBrass->index] = mulscale30(nDist, Cos(nAngle));
        yvel[pBrass->index] = mulscale30(nDist, Sin(nAngle));
        zvel[pBrass->index] = zvel[pSprite->index]-(0x20000+(Random2(40)<<18)/120);
    }
}

void fxSpawnEjectingShell(spritetype *pSprite, int z, int a3, int a4)
{
    int x = pSprite->x+mulscale28(pSprite->clipdist-4, Cos(pSprite->ang));
    int y = pSprite->y+mulscale28(pSprite->clipdist-4, Sin(pSprite->ang));
    x += mulscale30(a3, Cos(pSprite->ang+512));
    y += mulscale30(a3, Sin(pSprite->ang+512));
    spritetype *pShell = gFX.fxSpawn((FX_ID)(FX_40+Random(3)), pSprite->sectnum, x, y, z);
    if (pShell)
    {
        if (!VanillaMode())
            pShell->ang = Random(2047);
        int nDist = (a4<<18)/120+Random2(((a4/4)<<18)/120);
        int nAngle = pSprite->ang+Random2(56)+512;
        xvel[pShell->index] = mulscale30(nDist, Cos(nAngle));
        yvel[pShell->index] = mulscale30(nDist, Sin(nAngle));
        zvel[pShell->index] = zvel[pSprite->index]-(0x20000+(Random2(20)<<18)/120);
    }
}

void fxPrecache(void)
{
    for (int i = 0; i < kFXMax; i++)
    {
        tilePrecacheTile(gFXData[i].picnum, 0);
        if (gFXData[i].seq)
            seqPrecacheId(gFXData[i].seq);
    }
}


code:
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------
#pragma once
#include "build.h"
#include "common_game.h"

enum FX_ID {
    FX_NONE = -1,
    FX_0 = 0,
    FX_1,
    FX_2,
    FX_3,
    FX_4,
    FX_5,
    FX_6,
    FX_7,
    FX_8,
    FX_9,
    FX_10,
    FX_11,
    FX_12,
    FX_13,
    FX_14,
    FX_15,
    FX_16,
    FX_17,
    FX_18,
    FX_19,
    FX_20,
    FX_21,
    FX_22,
    FX_23,
    FX_24,
    FX_25,
    FX_26,
    FX_27,
    FX_28,
    FX_29,
    FX_30,
    FX_31,
    FX_32,
    FX_33,
    FX_34,
    FX_35,
    FX_36,
    FX_37,
    FX_38,
    FX_39,
    FX_40,
    FX_41,
    FX_42,
    FX_43,
    FX_44,
    FX_45,
    FX_46,
    FX_47,
    FX_48,
    FX_49,
    FX_50,
    FX_51,
    FX_52,
    FX_53,
    FX_54,
    FX_55,
    FX_56,
    kFXMax
};

class CFX {
public:
    void fxKill(int);
    void fxFree(int);
    spritetype * fxSpawn(FX_ID, int, int, int, int, unsigned int duration = 0);
    void fxProcess(void);
};

void fxSpawnBlood(spritetype *pSprite, int a2);
void fxSpawnPodBlood(spritetype *pSprite, int a2);
void fxSpawnEjectingBrass(spritetype *pSprite, int z, int a3, int a4);
void fxSpawnEjectingShell(spritetype *pSprite, int z, int a3, int a4);

void fxPrecache(void);

extern CFX gFX;


code:
//-------------------------------------------------------------------------
/*
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.
*/
//-------------------------------------------------------------------------

//****************************************************************************
//
// gamedefs.h
//
// common defines between the game and the setup program
//
//****************************************************************************

#ifndef gamedefs_public_h_
#define gamedefs_public_h_
#ifdef __cplusplus
extern "C" {
#endif

// config file name
#define SETUPFILENAME APPBASENAME ".cfg"

// KEEPINSYNC mact/include/_control.h, build/src/sdlayer.cpp
#define MAXJOYBUTTONS 32
#define MAXJOYBUTTONSANDHATS (MAXJOYBUTTONS+4)

// KEEPINSYNC mact/include/_control.h, build/src/sdlayer.cpp
#define MAXMOUSEAXES 2

// KEEPINSYNC mact/include/_control.h, build/src/sdlayer.cpp
#define MAXJOYAXES 9
#define MAXJOYDIGITAL (MAXJOYAXES*2)

// default mouse scale
#define DEFAULTMOUSEANALOGUESCALE           65536

// default joystick settings

#if defined(GEKKO)
#define DEFAULTJOYSTICKANALOGUESCALE        16384
#define DEFAULTJOYSTICKANALOGUEDEAD         1000
#define DEFAULTJOYSTICKANALOGUESATURATE     9500
#else
#define DEFAULTJOYSTICKANALOGUESCALE        65536
#define DEFAULTJOYSTICKANALOGUEDEAD         1000
#define DEFAULTJOYSTICKANALOGUESATURATE     9500
#endif

#ifdef __cplusplus
}
#endif
#endif