code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors

This file is part of EDuke32.

EDuke32 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

#ifndef actors_h_
#define actors_h_

#include "player.h"
#include "dnames.h"

#ifdef __cplusplus
extern "C" {
#endif

#define MAXSLEEPDIST        16384
#define SLEEPTIME           1536
#define AC_FZOFFSET(t) (((int32_t) actor[t].floorzoffset + 1) << 8)

#define ZOFFSET2            (16<<8)
#define ZOFFSET3            (8<<8)
#define ZOFFSET4            (12<<8)
#define ZOFFSET5            (32<<8)
#define ZOFFSET6            (4<<8)
#define ZOFFSET7            (18<<8)

#define ACTOR_MAXFALLINGZVEL 6144
#define ACTOR_ONWATER_ADDZ (24<<8)

#define STAT_DEFAULT        0
#define STAT_ACTOR          1
#define STAT_ZOMBIEACTOR    2
#define STAT_EFFECTOR       3
#define STAT_PROJECTILE     4
#define STAT_MISC           5
#define STAT_STANDABLE      6
#define STAT_LOCATOR        7
#define STAT_ACTIVATOR      8
#define STAT_TRANSPORT      9
#define STAT_PLAYER         10
#define STAT_FX             11
#define STAT_FALLER         12
#define STAT_DUMMYPLAYER    13
#define STAT_LIGHT          14
#define STAT_NETALLOC       (MAXSTATUS-1)

// Defines the motion characteristics of an actor
enum amoveflags_t
{
    face_player       = 1,
    geth              = 2,
    getv              = 4,
    random_angle      = 8,
    face_player_slow  = 16,
    spin              = 32,
    face_player_smart = 64,
    fleeenemy         = 128,
    jumptoplayer_only = 256,
    jumptoplayer_bits = 257,  // NOTE: two bits set!
    seekplayer        = 512,
    furthestdir       = 1024,
    dodgebullet       = 4096
};

// Defines for 'useractor' keyword
enum uactortypes_t
{
    notenemy,
    enemy,
    enemystayput
};

// These macros are there to give names to the t_data[]/T*/vm.g_t[] indices
// when used with actors. Greppability of source code is certainly a virtue.
#define AC_COUNT(t) ((t)[0])  /* the actor's count */
/* The ID of the actor's current move. In C-CON, the bytecode offset to the
 * move composite: */
#define AC_MOVE_ID(t) ((t)[1])
#define AC_ACTION_COUNT(t) ((t)[2])  /* the actor's action count */
#define AC_CURFRAME(t) ((t)[3])  /* the actor's current frame offset */
/* The ID of the actor's current action. In C-CON, the bytecode offset to the
 * action composite: */
#define AC_ACTION_ID(t) ((t)[4])
#define AC_AI_ID(t) ((t)[5])  /* the ID of the actor's current ai */

enum actionparams
{
    ACTION_STARTFRAME = 0,
    ACTION_NUMFRAMES,
    ACTION_VIEWTYPE,
    ACTION_INCVAL,
    ACTION_DELAY,
    ACTION_FLAGS,
    ACTION_PARAM_COUNT,
};

enum actionflags
{
    AF_VIEWPOINT = 1u<<0u,
};

// <spr>: sprite pointer
// <a>: actor_t pointer
#define AC_ACTIONTICS(spr, a) ((spr)->lotag)
#define AC_MOVFLAGS(spr, a) ((spr)->hitag)

// (+ 40 16 16 4 8 6 8 6 4 20)
#pragma pack(push, 1)
typedef struct
{
    int32_t t_data[10];  // 40b sometimes used to hold offsets to con code

    uint32_t flags;                       // 4b
    vec3_t   bpos;                        // 12b
    int32_t  floorz, ceilingz;            // 8b
    vec2_t   lastv;                       // 8b
    int16_t  htpicnum, htang;             // 4b
    int16_t  htextra, htowner;            // 4b
    int16_t  movflag, tempang;            // 4b
    int16_t  timetosleep, stayput;        // 4b
    int8_t   floorzoffset, waterzoffset;  // 2b
    uint8_t  filler[2];                   // 3b
    int16_t  dispicnum;                   // 2b NOTE: updated every frame, not in sync with game tics!
    uint8_t  cgg, lasttransport;          // 2b
} actor_t;

EDUKE32_STATIC_ASSERT(sizeof(actor_t) == 96);

#ifdef POLYMER
typedef struct
{
    _prlight *lightptr;              // 4b/8b  aligned on 96 bytes
    vec3_t lightoffset;
    int16_t lightId, lightmaxrange;  // 4b
    int16_t lightrange, olightrange;
    int16_t lightang, olightang;
    uint8_t lightcount, lightcolidx;   // 4b
} practor_t;

// NOTE: T5 is AC_ACTION_ID
#define LIGHTRAD_PICOFS(i) (T5(i) ? *(apScript + T5(i)) + (*(apScript + T5(i) + 2)) * AC_CURFRAME(actor[i].t_data) : 0)

// this is the same crap as in game.c's tspr manipulation.  puke.
// XXX: may access tilesizy out-of-bounds by bad user code.
#define LIGHTRAD(s) (sprite[s].yrepeat * tilesiz[sprite[s].picnum + LIGHTRAD_PICOFS(s)].y)
#define LIGHTRAD2(s) ((sprite[s].yrepeat + ((rand() % sprite[s].yrepeat)>>2)) * tilesiz[sprite[s].picnum + LIGHTRAD_PICOFS(s)].y)
#define LIGHTRAD3(s) (sprite[s].yrepeat * tilesiz[sprite[s].picnum].y)
#define LIGHTZOFF(s) ((sprite[s].yrepeat*tilesiz[sprite[s].picnum].y)<<1)
#endif

// note: fields in this struct DO NOT have to be in this order,
// however if you add something to this struct, please make sure
// a field gets added to ActorFields[], otherwise the field
// won't get synced over the network!
//
// I don't think the ActorFields array needs to be in the same order,
// need to verify this...
typedef struct netactor_s
{
    // actor fields
    //--------------------------------------------
    int32_t
        t_data_0,
        t_data_1,
        t_data_2,
        t_data_3,
        t_data_4,
        t_data_5,
        t_data_6,
        t_data_7,
        t_data_8,
        t_data_9;

    int32_t
        flags;

    int32_t
        bpos_x,
        bpos_y,
        bpos_z;

    int32_t
        floorz,
        ceilingz,
        lastvx,
        lastvy,

        lasttransport,

        htpicnum,
        htang,
        htextra,
        htowner,

        movflag,
        tempang,
        timetosleep,
        stayput,
        floorzoffset,
        waterzoffset,
        dispicnum;

    // note: lightId, lightcount, lightmaxrange are not synchronized between client and server

    int32_t
        cgg;

    // sprite fields
    //-----------------------------

    int32_t
        spr_x,
        spr_y,
        spr_z,

        spr_cstat,

        spr_picnum,

        spr_shade,

        spr_pal,
        spr_clipdist,
        spr_blend,

        spr_xrepeat,
        spr_yrepeat,

        spr_xoffset,
        spr_yoffset,

        spr_sectnum,
        spr_statnum,

        spr_ang,
        spr_owner,
        spr_xvel,
        spr_yvel,
        spr_zvel,

        spr_lotag,
        spr_hitag,

        spr_extra;

    //---------------------------------------------
    //spriteext fields

    int32_t
        ext_mdanimtims,

        ext_mdanimcur,
        ext_angoff,
        ext_pitch,
        ext_roll,

        ext_pivot_offset_x,
        ext_pivot_offset_y,
        ext_pivot_offset_z,

        ext_position_offset_x,
        ext_position_offset_y,
        ext_position_offset_z,

        ext_flags,
        ext_xpanning,
        ext_ypanning;

    float       ext_alpha;

    // DON'T send tsprites over the internet

    //--------------------------------------------
    //spritesmooth fields

    float       sm_smoothduration;

    int32_t
        sm_mdcurframe,
        sm_mdoldframe,

        sm_mdsmooth;

    //--------------------------------------------
    // SpriteProjectile fields

    // may want to put projectile fields here
    int32_t netIndex;

} netactor_t;
#pragma pack(pop)

typedef struct
{
    intptr_t *execPtr;  // pointer to CON script for this tile, formerly actorscrptr
    intptr_t *loadPtr;  // pointer to load time CON script, formerly actorLoadEventScrPtr or something
    projectile_t *proj;
    projectile_t *defproj;
    uint32_t      flags;       // formerly SpriteFlags, ActorType
    int32_t       cacherange;  // formerly SpriteCache
} tiledata_t;

enum sflags_t : unsigned int
{
    SFLAG_SHADOW        = 0x00000001,
    SFLAG_NVG           = 0x00000002,
    SFLAG_NOSHADE       = 0x00000004,
    SFLAG_PROJECTILE    = 0x00000008,
    SFLAG_DECAL         = 0x00000010,
    SFLAG_BADGUY        = 0x00000020,
    SFLAG_NOPAL         = 0x00000040,
    SFLAG_NOEVENTCODE   = 0x00000080,
    SFLAG_NOLIGHT       = 0x00000100,
    SFLAG_USEACTIVATOR  = 0x00000200,
    SFLAG_NULL          = 0x00000400,  // null sprite in multiplayer
    SFLAG_NOCLIP        = 0x00000800,  // clipmove it with cliptype 0
    SFLAG_NOFLOORSHADOW = 0x00001000,  // for temp. internal use, per-tile flag not checked
    SFLAG_SMOOTHMOVE    = 0x00002000,
    SFLAG_NOTELEPORT    = 0x00004000,
    SFLAG_BADGUYSTAYPUT = 0x00008000,
    SFLAG_CACHE         = 0x00010000,
    // rotation-fixed wrt a pivot point to prevent position diverging due to
    // roundoff error accumulation:
    SFLAG_ROTFIXED         = 0x00020000,
    SFLAG_HARDCODED_BADGUY = 0x00040000,
    SFLAG_DIDNOSE7WATER    = 0x00080000,  // used temporarily
    SFLAG_NODAMAGEPUSH     = 0x00100000,
    SFLAG_NOWATERDIP       = 0x00200000,
    SFLAG_HURTSPAWNBLOOD   = 0x00400000,
    SFLAG_GREENSLIMEFOOD   = 0x00800000,
    SFLAG_REALCLIPDIST     = 0x01000000,
    SFLAG_WAKEUPBADGUYS    = 0x02000000,
    SFLAG_DAMAGEEVENT      = 0x04000000,
    SFLAG_NOWATERSECTOR    = 0x08000000,
    SFLAG_QUEUEDFORDELETE  = 0x10000000,
    SFLAG_RESERVED         = 0x20000000,
    SFLAG_RESERVED2        = 0x40000000,
    SFLAG_RESERVED3        = 0x80000000,
};

// Custom projectiles "workslike" flags.
// XXX: Currently not predefined from CON.
enum pflags_t : unsigned int
{
    PROJECTILE_HITSCAN           = 0x00000001,
    PROJECTILE_RPG               = 0x00000002,
    PROJECTILE_BOUNCESOFFWALLS   = 0x00000004,
    PROJECTILE_BOUNCESOFFMIRRORS = 0x00000008,
    PROJECTILE_KNEE              = 0x00000010,
    PROJECTILE_WATERBUBBLES      = 0x00000020,
    PROJECTILE_TIMED             = 0x00000040,
    PROJECTILE_BOUNCESOFFSPRITES = 0x00000080,
    PROJECTILE_SPIT              = 0x00000100,
    PROJECTILE_COOLEXPLOSION1    = 0x00000200,
    PROJECTILE_BLOOD             = 0x00000400,
    PROJECTILE_LOSESVELOCITY     = 0x00000800,
    PROJECTILE_NOAIM             = 0x00001000,
    PROJECTILE_RANDDECALSIZE     = 0x00002000,
    PROJECTILE_EXPLODEONTIMER    = 0x00004000,
    PROJECTILE_RPG_IMPACT        = 0x00008000,
    PROJECTILE_RADIUS_PICNUM     = 0x00010000,
    PROJECTILE_ACCURATE_AUTOAIM  = 0x00020000,
    PROJECTILE_FORCEIMPACT       = 0x00040000,
    PROJECTILE_REALCLIPDIST      = 0x00080000,
    PROJECTILE_ACCURATE          = 0x00100000,
    PROJECTILE_NOSETOWNERSHADE   = 0x00200000,
    PROJECTILE_RPG_IMPACT_DAMAGE = 0x00400000,
//    PROJECTILE_PENETRATE         = 0x00800000,
    PROJECTILE_HITRADIUS_ADDITIVE  = 0x01000000,
    PROJECTILE_MOVED             = 0x80000000,  // internal flag, do not document
    PROJECTILE_TYPE_MASK         = PROJECTILE_HITSCAN | PROJECTILE_RPG | PROJECTILE_KNEE | PROJECTILE_BLOOD,
};

extern tiledata_t   g_tile[MAXTILES];
extern actor_t      actor[MAXSPRITES];
#ifdef POLYMER
extern practor_t    practor[MAXSPRITES];
#endif
extern int32_t      block_deletesprite;
extern int32_t      g_noEnemies;
extern int32_t      otherp;
extern int32_t      ticrandomseed;
extern projectile_t SpriteProjectile[MAXSPRITES];
extern uint8_t      g_radiusDmgStatnums[(MAXSTATUS+7)>>3];

int dukeValidateSectorEffectorPlaysSound(int);
int dukeValidateSectorPlaysSound(int);

int  A_CheckNoSE7Water(uspriteptr_t pSprite, int sectNum, int sectLotag, int32_t *pOther);
int  A_CheckSwitchTile(int spriteNum);
int A_IncurDamage(int spriteNum);
void A_AddToDeleteQueue(int spriteNum);
void A_DeleteSprite(int spriteNum);
void A_DoGuts(int spriteNum, int tileNum, int spawnCnt);
void A_DoGutsDir(int spriteNum, int tileNum, int spawnCnt);
int A_GetClipdist(int spriteNum);
void A_MoveCyclers(void);
void A_MoveDummyPlayers(void);
void A_MoveSector(int spriteNum);
void A_PlayAlertSound(int spriteNum);
void A_RadiusDamage(int spriteNum, int blastRadius, int dmg1, int dmg2, int dmg3, int dmg4);
void A_SpawnMultiple(int spriteNum, int tileNum, int spawnCnt);

int  G_SetInterpolation(int32_t *posptr);
void G_DeleteAllLights(void);
void G_AddGameLight(int spriteNum, int sectNum, vec3_t const &offset, int lightRange, int lightRadius, int lightHoriz, uint32_t lightColor, int lightPrio);
void G_InterpolateLights(int smoothratio);
void G_DoConveyorInterp(int smoothratio);
void G_ResetConveyorInterp(void);
void G_ClearCameraView(DukePlayer_t *ps);
void G_DoInterpolations(int smoothRatio);
void G_MoveWorld(void);
void G_RefreshLights(void);
void G_StopInterpolation(const int32_t *posptr);

// PK 20110701: changed input argument: int32_t i (== sprite, whose sectnum...) --> sectnum directly
void                Sect_ToggleInterpolation(int sectnum, int setInterpolation);
static FORCE_INLINE void   Sect_ClearInterpolation(int sectnum) { Sect_ToggleInterpolation(sectnum, 0); }
static FORCE_INLINE void   Sect_SetInterpolation(int sectnum) { Sect_ToggleInterpolation(sectnum, 1); }

#if KRANDDEBUG
# define ACTOR_INLINE __fastcall
# define ACTOR_INLINE_HEADER extern __fastcall
#else
# define ACTOR_INLINE EXTERN_INLINE
# define ACTOR_INLINE_HEADER EXTERN_INLINE_HEADER
#endif

extern int32_t A_MoveSpriteClipdist(int32_t spritenum, vec3_t const &change, uint32_t cliptype, int32_t clipdist);
ACTOR_INLINE_HEADER int A_CheckEnemyTile(int tileNum);
ACTOR_INLINE_HEADER int A_SetSprite(int spriteNum, uint32_t cliptype);
ACTOR_INLINE_HEADER int32_t A_MoveSprite(int spriteNum, vec3_t const &change, uint32_t cliptype);

EXTERN_INLINE_HEADER int G_CheckForSpaceCeiling(int sectnum);
EXTERN_INLINE_HEADER int G_CheckForSpaceFloor(int sectnum);

EXTERN_INLINE_HEADER int A_CheckEnemySprite(void const * s);

#ifdef __cplusplus
}
#endif

#if defined actors_c_ || !defined DISABLE_INLINING

# if !KRANDDEBUG || (KRANDDEBUG && defined actors_c_)

ACTOR_INLINE int A_CheckEnemyTile(int const tileNum)
{
    return ((g_tile[tileNum].flags & (SFLAG_HARDCODED_BADGUY | SFLAG_BADGUY)) != 0);
}

ACTOR_INLINE int A_SetSprite(int const spriteNum, uint32_t cliptype)
{
    return (A_MoveSprite(spriteNum,
                         { (sprite[spriteNum].xvel * (sintable[(sprite[spriteNum].ang + 512) & 2047])) >> 14,
                           (sprite[spriteNum].xvel * (sintable[sprite[spriteNum].ang & 2047])) >> 14, sprite[spriteNum].zvel },
                         cliptype) == 0);
}

ACTOR_INLINE int A_SetSpriteNoZ(int const spriteNum, uint32_t cliptype)
{
    return (A_MoveSprite(spriteNum,
                         { (sprite[spriteNum].xvel * (sintable[(sprite[spriteNum].ang + 512) & 2047])) >> 14,
                           (sprite[spriteNum].xvel * (sintable[sprite[spriteNum].ang & 2047])) >> 14, 0 },
                         cliptype) == 0);
}

ACTOR_INLINE int32_t A_MoveSprite(int const spriteNum, vec3_t const &change, uint32_t cliptype)
{
    return A_MoveSpriteClipdist(spriteNum, change, cliptype, A_GetClipdist(spriteNum));
}

# endif

EXTERN_INLINE int G_CheckForSpaceCeiling(int const sectnum)
{
    return ((sector[sectnum].ceilingstat&1) && sector[sectnum].ceilingpal == 0 &&
            (sector[sectnum].ceilingpicnum==MOONSKY1 || sector[sectnum].ceilingpicnum==BIGORBIT1));
}

EXTERN_INLINE int G_CheckForSpaceFloor(int const sectnum)
{
    return ((sector[sectnum].floorstat&1) && sector[sectnum].ceilingpal == 0 &&
            (sector[sectnum].floorpicnum==MOONSKY1 || sector[sectnum].floorpicnum==BIGORBIT1));
}

EXTERN_INLINE int A_CheckEnemySprite(void const * const pSprite)
{
    return A_CheckEnemyTile(((uspriteptr_t) pSprite)->picnum);
}

#endif

#endif

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors

This file is part of EDuke32.

EDuke32 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.
*/
//-------------------------------------------------------------------------

#ifndef android_h_
#define android_h_

#ifdef __ANDROID__
#include "compat.h"
#include "control.h"
#include "in_android.h"

//extern int   android_sample_rate;

//extern int   android_audio_buffer_size;

extern void  CONTROL_Android_ClearButton(int32_t whichbutton);
extern void  CONTROL_Android_PollDevices(ControlInfo *info);
extern void  CONTROL_Android_SetLastWeapon(int w);

extern void  CONTROL_Android_ScrollMap(int32_t *angle,int32_t *x, int32_t *y, uint16_t *zoom );

#endif

#endif


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors

This file is part of EDuke32.

EDuke32 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 "baselayer.h"
#include "renderlayer.h"
#include "duke3d.h"
#include "animlib.h"
#include "mouse.h"
#include "compat.h"
#include "input.h"
#include "texcache.h"

#include "anim.h"

#ifdef USE_LIBVPX
# include "animvpx.h"
#endif

#include "vfs.h"

// animsound_t.sound
EDUKE32_STATIC_ASSERT(INT16_MAX >= MAXSOUNDS);

hashtable_t h_dukeanim = { 8, NULL };
dukeanim_t * g_animPtr;

dukeanim_t *Anim_Find(const char *s)
{
    intptr_t ptr = hash_findcase(&h_dukeanim, s);

    if (ptr == -1)
    {
        int const siz = Bstrlen(s) + 5;
        char * const str = (char *)Xcalloc(1, siz);

        maybe_append_ext(str, siz, s, ".anm");
        ptr = hash_findcase(&h_dukeanim, str);

        if (ptr == -1)
        {
            maybe_append_ext(str, siz, s, ".ivf");
            ptr = hash_findcase(&h_dukeanim, str);
        }

        Xfree(str);
    }

    return (dukeanim_t *)(ptr == -1 ? NULL : (dukeanim_t *)ptr);
}

dukeanim_t * Anim_Create(char const * fn)
{
    dukeanim_t * anim = (dukeanim_t *)Xcalloc(1, sizeof(dukeanim_t));

    hash_add(&h_dukeanim, fn, (intptr_t)anim, 0);

    return anim;
}

#ifndef EDUKE32_STANDALONE
#ifdef USE_DNAMES
static int32_t const StopAllSounds = -1;
#else
# define StopAllSounds -1
#endif
#endif

void Anim_Init(void)
{
    hash_init(&h_dukeanim);

    struct defaultanmsound {
#ifdef USE_DNAMES
        int32_t const & sound;
#else
        int16_t sound;
#endif
        uint8_t frame;
    };

    static defaultanmsound const logo[] =
    {
        { FLY_BY, 1 },
        { PIPEBOMB_EXPLODE, 19 },
    };

#ifndef EDUKE32_STANDALONE
    static defaultanmsound const cineov2[] =
    {
        { WIND_AMBIENCE, 1 },
        { ENDSEQVOL2SND1, 26 },
        { ENDSEQVOL2SND2, 36 },
        { THUD, 54 },
        { ENDSEQVOL2SND3, 62 },
        { ENDSEQVOL2SND4, 75 },
        { ENDSEQVOL2SND5, 81 },
        { ENDSEQVOL2SND6, 115 },
        { ENDSEQVOL2SND7, 124 },
    };

    static defaultanmsound const cineov3[] =
    {
        { WIND_REPEAT, 1 },
        { DUKE_GRUNT, 98 },
        { THUD, 82+20 },
        { SQUISHED, 82+20 },
        { ENDSEQVOL3SND3, 104+20 },
        { ENDSEQVOL3SND2, 114+20 },
        { PIPEBOMB_EXPLODE, 158 },
    };

    static defaultanmsound const vol42a[] =
    {
        { INTRO4_B, 1 },
        { SHORT_CIRCUIT, 12 },
        { INTRO4_5, 18 },
        { SHORT_CIRCUIT, 34 },
    };

    static defaultanmsound const vol41a[] =
    {
        { INTRO4_1, 1 },
        { INTRO4_3, 7 },
        { INTRO4_2, 12 },
        { INTRO4_4, 26 },
    };

    static defaultanmsound const vol43a[] =
    {
        { INTRO4_6, 10 },
    };

    static defaultanmsound const vol4e1[] =
    {
        { DUKE_UNDERWATER, 3 },
        { VOL4ENDSND1, 35 },
    };

    static defaultanmsound const vol4e2[] =
    {
        { DUKE_UNDERWATER, 11 },
        { VOL4ENDSND1, 20 },
        { VOL4ENDSND2, 39 },
        { StopAllSounds, 50 },
    };

    static defaultanmsound const vol4e3[] =
    {
        { BOSS4_DEADSPEECH, 1 },
        { VOL4ENDSND1, 40 },
        { DUKE_UNDERWATER, 40 },
        { BIGBANG, 50 },
    };
#endif

    struct defaultanm {
        char const *fn;
        defaultanmsound const *sounds;
        uint8_t numsounds;
        uint8_t fdelay;
    };

#define anmsnd(x) (x), ARRAY_SIZE(x)
    static defaultanm const anms[] =
    {
        { "logo.anm", anmsnd(logo), 9 },
        { "3dr.anm", NULL, 0, 10 },
#ifndef EDUKE32_STANDALONE
        { "vol4e1.anm", anmsnd(vol4e1), 10 },
        { "vol4e2.anm", anmsnd(vol4e2), 14 },
        { "vol4e3.anm", anmsnd(vol4e3), 10 },
        { "vol41a.anm", anmsnd(vol41a), 14 },
        { "vol42a.anm", anmsnd(vol42a), 18 },
        { "vol43a.anm", anmsnd(vol43a), 10 },
        { "duketeam.anm", NULL, 0, 10 },
        { "radlogo.anm", anmsnd(cineov3), 10 },
        { "cineov2.anm", anmsnd(cineov2), 18 },
        { "cineov3.anm", anmsnd(cineov3), 10 },
#endif
    };
#undef anmsnd

    for (defaultanm const & anm : anms)
    {
        dukeanim_t * anim = Anim_Create(anm.fn);
        anim->framedelay = anm.fdelay;

        if (anm.numsounds)
        {
            anim->sounds = (animsound_t *)Xmalloc(anm.numsounds * sizeof(animsound_t));
            int const numsounds = anm.numsounds;
            for (int i = 0; i < numsounds; ++i)
            {
                defaultanmsound const & src = anm.sounds[i];
                animsound_t & dst = anim->sounds[i];

                dst.sound = src.sound;
                dst.frame = src.frame;
            }
            anim->numsounds = numsounds;
        }

        anim->frameflags = 0;
    }
}

int32_t Anim_Play(const char *fn)
{
    dukeanim_t *anim = Anim_Find(fn);

    if (!anim)
    {
        LOG_F(ERROR, "Unable to play %s: undefined cutscene", fn);
        return 0;
    }

    uint16_t soundidx = 0;  // custom anim sounds
    int32_t running = 1, i;

    I_ClearAllInput();

#ifdef USE_LIBVPX
    uint16_t framenum = 0;
    while (videoGetRenderMode() >= REND_POLYMOST)  // if, really
    {
        char const * dot = Bstrrchr(fn, '.');
        if (!dot)
            break;

        dukeanim_t const * origanim = anim;
        buildvfs_kfd handle = buildvfs_kfd_invalid;
        if (!Bstrcmp(dot, ".ivf"))
        {
            handle = kopen4loadfrommod(fn, 0);
            if (handle == buildvfs_kfd_invalid)
                break;
        }
        else
        {
            char vpxfn[BMAX_PATH];
            Bstrncpyz(vpxfn, fn, BMAX_PATH);

            ptrdiff_t dotpos = dot - fn;
            if (dotpos + 4 >= BMAX_PATH)
                break;

            char *vpxfndot = vpxfn + dotpos;
            vpxfndot[1] = 'i';
            vpxfndot[2] = 'v';
            vpxfndot[3] = 'f';
            vpxfndot[4] = '\0';

            handle = kopen4loadfrommod(vpxfn, 0);
            if (handle == buildvfs_kfd_invalid)
                break;

            anim = Anim_Find(vpxfn);
        }

        animvpx_ivf_header_t info;
        i = animvpx_read_ivf_header(handle, &info);

        if (i)
        {
            LOG_F(ERROR, "Unable to play %s: %s", fn, animvpx_read_ivf_header_errmsg[i]);
            kclose(handle);
            return 0;
        }

        uint8_t fflags = anim ? anim->frameflags : origanim->frameflags;

        animvpx_codec_ctx codec;

        if (animvpx_init_codec(&info, handle, &codec))
        {
            LOG_F(ERROR, "Failed initializing VPX codec.");
            animvpx_restore_glstate();
            kclose(handle);
            return 0;
        }

        uint32_t const convnumer = 120 * info.fpsdenom;
        uint32_t const convdenom = info.fpsnumer * origanim->framedelay;

        uint32_t const msecsperframe = scale(info.fpsdenom, 1000, info.fpsnumer);
        uint32_t nextframetime = timerGetTicks();
        uint8_t *pic;

        //        OSD_Printf("msecs per frame: %d\n", msecsperframe);

        g_animPtr = anim;
        do
        {
            nextframetime += msecsperframe;

            animvpx_setup_glstate(fflags);
            i = animvpx_nextpic(&codec, &pic);
            if (i)
            {
                LOG_F(ERROR, "Failed getting next pic: %s", animvpx_nextpic_errmsg[i]);
                if (codec.errmsg)
                {
                    LOG_F(ERROR, "  %s", codec.errmsg);
                    if (codec.errmsg_detail)
                        LOG_F(ERROR, "  detail: %s", codec.errmsg_detail);
                }
                break;
            }

            if (!pic)
                break;  // no more pics!

            VM_OnEventWithReturn(EVENT_PRECUTSCENE, g_player[screenpeek].ps->i, screenpeek, framenum);

            videoClearScreen(0);

            ototalclock = totalclock + 1; // pause game like ANMs

            if (anim)
            {
                if (anim->frameaspect1 == 0 || anim->frameaspect2 == 0)
                    animvpx_render_frame(&codec, 0);
                else
                    animvpx_render_frame(&codec, anim->frameaspect1 / anim->frameaspect2);
            }
            else
            {
                if (origanim->frameaspect1 == 0 || origanim->frameaspect2 == 0)
                    animvpx_render_frame(&codec, 0);
                else
                    animvpx_render_frame(&codec, origanim->frameaspect1 / origanim->frameaspect2);
            }

            VM_OnEventWithReturn(EVENT_CUTSCENE, g_player[screenpeek].ps->i, screenpeek, framenum);

            // after rendering the frame but before displaying: maybe play sound...
            framenum++;
            if (anim)
            {
                while (soundidx < anim->numsounds && anim->sounds[soundidx].frame <= framenum)
                {
                    int16_t sound = anim->sounds[soundidx].sound;
                    if (sound == -1)
                        FX_StopAllSounds();
                    else
                        S_PlaySound(sound);

                    soundidx++;
                }
            }
            else
            {
                uint16_t convframenum = scale(framenum, convnumer, convdenom);
                while (soundidx < origanim->numsounds && origanim->sounds[soundidx].frame <= convframenum)
                {
                    int16_t sound = origanim->sounds[soundidx].sound;
                    if (sound == -1)
                        FX_StopAllSounds();
                    else
                        S_PlaySound(sound);

                    soundidx++;
                }
            }

            // this and showframe() instead of nextpage() are so that
            // nobody tramples on our carefully set up GL state!
            palfadedelta = 0;
            videoShowFrame(-1);

            //            I_ClearAllInput();

            do
            {
                gameHandleEvents();

                if (VM_OnEventWithReturn(EVENT_SKIPCUTSCENE, g_player[screenpeek].ps->i, screenpeek, I_GeneralTrigger()))
                {
                    running = 0;
                    break;
                }
            } while (timerGetTicks() < nextframetime);
        } while (running);
        g_animPtr = NULL;

        animvpx_print_stats(&codec);

        //
        kclose(handle);
        animvpx_restore_glstate();
        animvpx_uninit_codec(&codec);

        I_ClearAllInput();
        return !running;  // done with playing VP8!
    }
#endif
// ANM playback --- v v v ---

    buildvfs_kfd handle = kopen4load(fn, 0);

    if (handle == buildvfs_kfd_invalid)
        return 0;

    int32_t length = kfilelength(handle);

    if (length <= 4)
    {
        LOG_F(WARNING, "Unable to play %s: no data.", fn);
        goto end_anim;
    }

    anim->animlock = CACHE1D_PERMANENT;

    if (!anim->animbuf)
        g_cache.allocateBlock((intptr_t *)&anim->animbuf, length + 1, &anim->animlock);

    kread(handle, anim->animbuf, length);
    kclose(handle);

    uint32_t firstfour;
    Bmemcpy(&firstfour, anim->animbuf, 4);

    // "DKIF" (.ivf)
    if (firstfour == B_LITTLE32(0x46494B44u))
        goto end_anim;

    int32_t numframes;

    // "LPF " (.anm)
    if (firstfour != B_LITTLE32(0x2046504Cu) ||
        ANIM_LoadAnim(anim->animbuf, length) < 0 ||
        (numframes = ANIM_NumFrames()) <= 0)
    {
        // XXX: ANM_LoadAnim() still checks less than the bare minimum,
        // e.g. ANM file could still be too small and not contain any frames.
        LOG_F(ERROR, "Unable to play %s: malformed file.", fn);
        goto end_anim;
    }

    paletteSetColorTable(ANIMPAL, ANIM_GetPalette());

    // setpalette(0L,256L,tempbuf);
    // setbrightness(ud.brightness>>2,tempbuf,2);
    P_SetGamePalette(g_player[myconnectindex].ps, ANIMPAL, 8 + 2);

#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
    {
        if (!buildgl_samplerObjectsEnabled())
        {
            auto pth = texcache_fetch(TILE_ANIM, 0, 0, DAMETH_NOMASK);
            if (pth && pth->glpic)
            {
                int filter = -1;

                switch (anim->frameflags & CUTSCENE_FILTERMASK)
                {
                    case CUTSCENE_TEXTUREFILTER:
                        break;
                    case CUTSCENE_FORCEFILTER:
                        filter = TEXFILTER_ON;
                        break;
                    case CUTSCENE_FORCENOFILTER:
                        filter = TEXFILTER_OFF;
                        break;
                }

                bind_2d_texture(pth->glpic, filter);
            }
        }
        else
        {
            switch (anim->frameflags & CUTSCENE_FILTERMASK)
            {
                case CUTSCENE_TEXTUREFILTER:
                    buildgl_bindSamplerObject(0, (glfiltermodes[gltexfiltermode].min == GL_NEAREST) ? PTH_INDEXED : PTH_FORCEFILTER);
                    break;
                case CUTSCENE_FORCEFILTER:
                    buildgl_bindSamplerObject(0, PTH_FORCEFILTER);
                    break;
                case CUTSCENE_FORCENOFILTER:
                    buildgl_bindSamplerObject(0, PTH_INDEXED);
                    break;
            }
        }
    }
#endif

    if (g_restorePalette == 1)
    {
        P_SetGamePalette(g_player[myconnectindex].ps, ANIMPAL, 0);
        g_restorePalette = 0;
    }

    gameHandleEvents();
    ototalclock = totalclock;

    i = 1;
    int32_t frametime; frametime = 0;

    g_animPtr = anim;
    do
    {
        if (i > 4 && totalclock > frametime + 60)
        {
            LOG_F(WARNING, "Unable to maintain framerate playing %s, stopping playback to preserve dignity.", fn);
            goto end_anim_restore_gl;
        }

        gameHandleEvents();

        if (totalclock < ototalclock - 1)
            continue;

        i = VM_OnEventWithReturn(EVENT_PRECUTSCENE, g_player[screenpeek].ps->i, screenpeek, i);

        walock[TILE_ANIM] = CACHE1D_PERMANENT;
        waloff[TILE_ANIM] = (intptr_t)ANIM_DrawFrame(i);
        tileSetSize(TILE_ANIM, 200, 320);
        tileInvalidate(TILE_ANIM, 0, 1 << 4);  // JBF 20031228

        if (VM_OnEventWithReturn(EVENT_SKIPCUTSCENE, g_player[screenpeek].ps->i, screenpeek, I_GeneralTrigger()))
        {
            running = 0;
            goto end_anim_restore_gl;
        }

        I_ClearAllInput();

        if (g_restorePalette == 1)
        {
            P_SetGamePalette(g_player[myconnectindex].ps, ANIMPAL, 0);
            g_restorePalette = 0;
        }

        m_mouselastactivity = frametime = (int32_t) totalclock;

        videoClearScreen(0);

        int32_t z;
        if (anim->frameaspect1 > 0 && anim->frameaspect2 > 0 && ((anim->frameaspect1 / anim->frameaspect2) != (tilesiz[TILE_ANIM].y / (tilesiz[TILE_ANIM].x * 1.2))))
        {
            int32_t const oyxaspect = yxaspect;
            if ((anim->frameaspect1 / anim->frameaspect2) >= ((decltype(anim->frameaspect1))xdim / ydim))
                z = divscale16(320, tilesiz[TILE_ANIM].y);
            else
                z = divscale16(lrint(320 * ydim * anim->frameaspect1), lrint(tilesiz[TILE_ANIM].y * xdim * anim->frameaspect2));
            int32_t aspect = divscale16(lrint(tilesiz[TILE_ANIM].y * anim->frameaspect2), lrint(tilesiz[TILE_ANIM].x * anim->frameaspect1));
            renderSetAspect(viewingrange, aspect);
            rotatesprite_fs(160<<16, 100<<16, z, 512, TILE_ANIM, 0, 0, 2|4|8|64|1024);
            renderSetAspect(viewingrange, oyxaspect);
        }
        else
        {
            if ((tilesiz[TILE_ANIM].y / (tilesiz[TILE_ANIM].x * 1.2f)) > (1.f * xdim / ydim))
                z = divscale16(320 * xdim * 3, tilesiz[TILE_ANIM].y * ydim * 4);
            else
                z = divscale16(200, tilesiz[TILE_ANIM].x);
            rotatesprite_fs(160<<16, 100<<16, z, 512, TILE_ANIM, 0, 0, 2|4|8|64);
        }

        i = VM_OnEventWithReturn(EVENT_CUTSCENE, g_player[screenpeek].ps->i, screenpeek, i);

        videoNextPage();

        ototalclock += anim->framedelay;

        while (soundidx < anim->numsounds && anim->sounds[soundidx].frame <= (uint16_t)i)
        {
            int16_t sound = anim->sounds[soundidx].sound;
            if (sound == -1)
                FX_StopAllSounds();
            else
                S_PlaySound(sound);

            soundidx++;
        }

        ++i;
    } while (i < numframes);

end_anim_restore_gl:
end_anim:
    g_animPtr = NULL;
    I_ClearAllInput();
    ANIM_FreeAnim();

    tileSetSize(TILE_ANIM, 0, 0);
    walock[TILE_ANIM] = 0;
    waloff[TILE_ANIM] = 0;

    // this is the lock for anim->animbuf
    anim->animlock = CACHE1D_FREE;

    return !running;
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors

This file is part of EDuke32.

EDuke32 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.
*/
//-------------------------------------------------------------------------

#ifndef anim_h_
#define anim_h_

#include "compat.h"
#include "hash.h"

typedef struct {
    uint16_t frame;
    int16_t sound;
} animsound_t;

typedef struct
{
    double frameaspect1, frameaspect2;
    uint8_t* animbuf;
    animsound_t *sounds;
    uint16_t numsounds;
    uint8_t framedelay;
    uint8_t frameflags;
    char animlock;
} dukeanim_t;

extern dukeanim_t * g_animPtr;
extern hashtable_t h_dukeanim;
extern dukeanim_t * Anim_Find(const char *s);
extern dukeanim_t * Anim_Create(const char *fn);
int32_t Anim_Play(const char *fn);
void Anim_Init(void);

#endif