code:
int ActorFollowTrack(short SpriteNum,short locktics);
void ActorLeaveTrack(short SpriteNum);
void RefreshPoints(SECTOR_OBJECTp sop,int nx,int ny,SWBOOL dynamic);
void TrackSetup(void);
void PlaceSectorObject(SECTOR_OBJECTp sop,int newx,int newy);
void PlaceSectorObjectsOnTracks(void);
void PlaceActorsOnTracks(void);
void SetupSectorObject(short sectnum,short tag);
void PostSetupSectorObject(void);
void VehicleSetSmoke(SECTOR_OBJECTp sop,ANIMATORp animator);
void CollapseSectorObject(SECTOR_OBJECTp sop,int nx,int ny);
void KillSectorObjectSprites(SECTOR_OBJECTp sop);
void MoveSectorObjects(SECTOR_OBJECTp sop,short locktics);

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------

enum TriggerType {TRIGGER_TYPE_REMOTE_SO};


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "build.h"

#include "names2.h"
#include "panel.h"
#include "game.h"
#include "network.h"
#include "tags.h"
#include "sector.h"
#include "interp.h"
#include "text.h"
#include "sprite.h"
#include "weapon.h"

short DoVatorMatch(PLAYERp pp, short match);
SWBOOL TestVatorMatchActive(short match);
void InterpSectorSprites(short sectnum, SWBOOL state);
int InitBloodSpray(short, SWBOOL, short);

void ReverseVator(short SpriteNum)
{
    USERp u = User[SpriteNum];
    SPRITEp sp = u->SpriteP;

    // if paused go ahead and start it up again
    if (u->Tics)
    {
        u->Tics = 0;
        SetVatorActive(SpriteNum);
        return;
    }

    // moving toward to OFF pos
    if (u->z_tgt == u->oz)
    {
        if (sp->z == u->oz)
            u->z_tgt = u->sz;
        else if (u->sz == u->oz)
            u->z_tgt = sp->z;
    }
    else if (u->z_tgt == u->sz)
    {
        if (sp->z == u->oz)
            u->z_tgt = sp->z;
        else if (u->sz == u->oz)
            u->z_tgt = u->sz;
    }

    u->vel_rate = -u->vel_rate;
}

SWBOOL
VatorSwitch(short match, short setting)
{
    SPRITEp sp;
    short i,nexti;
    SWBOOL found = FALSE;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_DEFAULT], i, nexti)
    {
        sp = &sprite[i];

        if (sp->lotag == TAG_SPRITE_SWITCH_VATOR && sp->hitag == match)
        {
            found = TRUE;
            AnimateSwitch(sp, setting);
        }
    }

    return found;
}

void SetVatorActive(short SpriteNum)
{
    USERp u = User[SpriteNum];
    SPRITEp sp = u->SpriteP;
    SECTORp sectp = &sector[sp->sectnum];

    if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
        setinterpolation(&sectp->ceilingz);
    else
        setinterpolation(&sectp->floorz);

    InterpSectorSprites(sp->sectnum, ON);

    // play activate sound
    DoSoundSpotMatch(SP_TAG2(sp), 1, SOUND_OBJECT_TYPE);

    SET(u->Flags, SPR_ACTIVE);
    u->Tics = 0;

    // moving to the ON position
    if (u->z_tgt == sp->z)
        VatorSwitch(SP_TAG2(sp), ON);
    else
    // moving to the OFF position
    if (u->z_tgt == u->sz)
        VatorSwitch(SP_TAG2(sp), OFF);
}

void SetVatorInactive(short SpriteNum)
{
    USERp u = User[SpriteNum];
    SPRITEp sp = u->SpriteP;
    SECTORp sectp = &sector[sp->sectnum];

    if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
        stopinterpolation(&sectp->ceilingz);
    else
        stopinterpolation(&sectp->floorz);

    InterpSectorSprites(sp->sectnum, OFF);

    // play inactivate sound
    DoSoundSpotMatch(SP_TAG2(sp), 2, SOUND_OBJECT_TYPE);

    RESET(u->Flags, SPR_ACTIVE);
}

// called for operation from the space bar
short DoVatorOperate(PLAYERp pp, short sectnum)
{
    SPRITEp fsp;
    short match;
    short i,nexti;

    TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
    {
        fsp = &sprite[i];

        if (fsp->statnum == STAT_VATOR && SP_TAG1(fsp) == SECT_VATOR && SP_TAG3(fsp) == 0)
        {
            sectnum = fsp->sectnum;

            // single play only vator
            // SWBOOL 8 must be set for message to display
            if (TEST_BOOL4(fsp) && (gNet.MultiGameType == MULTI_GAME_COMMBAT || gNet.MultiGameType == MULTI_GAME_AI_BOTS))
            {
                if (pp && TEST_BOOL11(fsp)) PutStringInfo(pp,"This only opens in single play.");
                continue;
            }

            match = SP_TAG2(fsp);
            if (match > 0)
            {
                if (TestVatorMatchActive(match))
                    return -1;
                else
                    return DoVatorMatch(pp, match);
            }

            if (pp && SectUser[sectnum] && SectUser[sectnum]->stag == SECT_LOCK_DOOR && SectUser[sectnum]->number)
            {
                short key_num;

                key_num = SectUser[sectnum]->number;

#if 0
                if (pp->HasKey[key_num - 1])
                {
                    int i;
                    for (i=0; i<numsectors; i++)
                    {
                        if (SectUser[i] && SectUser[i]->stag == SECT_LOCK_DOOR && SectUser[i]->number == key_num)
                            SectUser[i]->number = 0;  // unlock all doors of this type
                    }
                    UnlockKeyLock(key_num);
                }
                else
#endif
                {
                    PutStringInfo(pp, KeyDoorMessage[key_num - 1]);
                    return FALSE;
                }
            }

            SetVatorActive(i);
            break;
        }
    }

    return i;
}

// called from switches and triggers
// returns first vator found
short
DoVatorMatch(PLAYERp pp, short match)
{
    USERp fu;
    SPRITEp fsp;
    short sectnum;
    short first_vator = -1;

    short i,nexti;

    //VatorSwitch(match, ON);

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_VATOR], i, nexti)
    {
        fsp = &sprite[i];

        if (SP_TAG1(fsp) == SECT_VATOR && SP_TAG2(fsp) == match)
        {
            fu = User[i];

            if (first_vator == -1)
                first_vator = i;

            // single play only vator
            // SWBOOL 8 must be set for message to display
            if (TEST_BOOL4(fsp) && (gNet.MultiGameType == MULTI_GAME_COMMBAT || gNet.MultiGameType == MULTI_GAME_AI_BOTS))
            {
                if (pp && TEST_BOOL11(fsp)) PutStringInfo(pp,"This only opens in single play.");
                continue;
            }

            // lock code
            sectnum = fsp->sectnum;
            if (pp && SectUser[sectnum] && SectUser[sectnum]->stag == SECT_LOCK_DOOR && SectUser[sectnum]->number)
            {
                short key_num;

                key_num = SectUser[sectnum]->number;

#if 0
                if (pp->HasKey[key_num - 1])
                {
                    int i;
                    for (i=0; i<numsectors; i++)
                    {
                        if (SectUser[i] && SectUser[i]->stag == SECT_LOCK_DOOR && SectUser[i]->number == key_num)
                            SectUser[i]->number = 0;  // unlock all doors of this type
                    }
                    UnlockKeyLock(key_num);
                }
                else
#endif
                {
                    PutStringInfo(pp, KeyDoorMessage[key_num - 1]);
                    return -1;
                }
            }

            // remember the player than activated it
            fu->PlayerP = pp;

            if (TEST(fu->Flags, SPR_ACTIVE))
            {
                ReverseVator(i);
                continue;
            }

            SetVatorActive(i);
        }
    }

    return first_vator;
}

SWBOOL
TestVatorMatchActive(short match)
{
    USERp fu;
    SPRITEp fsp;

    short i,nexti;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_VATOR], i, nexti)
    {
        fsp = &sprite[i];

        if (SP_TAG1(fsp) == SECT_VATOR && SP_TAG2(fsp) == match)
        {
            fu = User[i];

            // Does not have to be inactive to be operated
            if (TEST_BOOL6(fsp))
                continue;

            if (TEST(fu->Flags, SPR_ACTIVE) || fu->Tics)
                return TRUE;
        }
    }

    return FALSE;
}

void InterpSectorSprites(short sectnum, SWBOOL state)
{
    SPRITEp sp;
    short i,nexti;

    TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
    {
        sp = &sprite[i];

        if (User[i])
        {
            if (TEST(User[i]->Flags, SPR_SKIP4) && sp->statnum <= STAT_SKIP4_INTERP_END)
                continue;

            if (TEST(User[i]->Flags, SPR_SKIP2) && sp->statnum <= STAT_SKIP2_INTERP_END)
                continue;
        }

        if (state)
            setinterpolation(&sp->z);
        else
            stopinterpolation(&sp->z);
    }
}

void MoveSpritesWithSector(short sectnum, int z_amt, SWBOOL type)
{
    SPRITEp sp;
    short i,nexti;
    SWBOOL both = FALSE;

    if (SectUser[sectnum])
        both = !!TEST(SectUser[sectnum]->flags, SECTFU_VATOR_BOTH);

    TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
    {
        sp = &sprite[i];

        if (User[i])
        {
            switch (sp->statnum)
            {
            case STAT_ITEM:
            case STAT_NO_STATE:
            case STAT_MINE_STUCK:
            case STAT_WALLBLOOD_QUEUE:
            case STAT_FLOORBLOOD_QUEUE:
            case STAT_STATIC_FIRE:
                break;
            default:
                goto cont;
            }
        }
        else
        {
            switch (sp->statnum)
            {
            case STAT_STAR_QUEUE:
            case STAT_HOLE_QUEUE:
//              case STAT_WALLBLOOD_QUEUE:
//              case STAT_FLOORBLOOD_QUEUE:
                goto cont;
            }
        }

        if (TEST(sp->extra, SPRX_STAY_PUT_VATOR))
            continue;

        if (both)
        {
            // sprite started close to floor
            if (TEST(sp->cstat, CSTAT_SPRITE_CLOSE_FLOOR))
            {
                // this is a ceiling
                if (type == 1)
                    continue;
            }
            else
            {
                // this is a floor
                if (type == 0)
                    continue;
            }
        }

        sp->z += z_amt;

cont:
        continue;
    }
}

int DoVatorMove(short SpriteNum, int *lptr)
{
    USERp u = User[SpriteNum];
    int zval;
    int move_amt;

    zval = *lptr;

    // if LESS THAN goal
    if (zval < u->z_tgt)
    {
        // move it DOWN
        zval += (synctics * u->jump_speed);

        u->jump_speed += u->vel_rate * synctics;

        // if the other way make it equal
        if (zval > u->z_tgt)
            zval = u->z_tgt;
    }

    // if GREATER THAN goal
    if (zval > u->z_tgt)
    {
        // move it UP
        zval -= (synctics * u->jump_speed);

        u->jump_speed += u->vel_rate * synctics;

        if (zval < u->z_tgt)
            zval = u->z_tgt;
    }

    move_amt = zval - *lptr;
    *lptr = zval;

    return move_amt;
}

int DoVator(short SpriteNum)
{
    USERp u = User[SpriteNum];
    SPRITEp sp = u->SpriteP;
    SECTORp sectp = &sector[sp->sectnum];
    int *lptr;
    int amt;

    // u->sz        - where the sector z started
    // u->z_tgt     - current target z
    // u->oz        - original z - where it initally starts off
    // sp->z        - z of the sprite
    // u->vel_rate  - velocity

    if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
    {
        lptr = &sectp->ceilingz;
        amt = DoVatorMove(SpriteNum, lptr);
        MoveSpritesWithSector(sp->sectnum, amt, 1); // ceiling
    }
    else
    {
        lptr = &sectp->floorz;
        amt = DoVatorMove(SpriteNum, lptr);
        MoveSpritesWithSector(sp->sectnum, amt, 0); // floor
    }

    // EQUAL this entry has finished
    if (*lptr == u->z_tgt)
    {
        // in the ON position
        if (u->z_tgt == sp->z)
        {
            // change target
            u->z_tgt = u->sz;
            u->vel_rate = -u->vel_rate;

            SetVatorInactive(SpriteNum);

            // if tag6 and nothing blocking door
            if (SP_TAG6(sp) && !TEST_BOOL8(sp))
                DoMatchEverything(u->PlayerP, SP_TAG6(sp), -1);
        }
        else
        // in the OFF position
        if (u->z_tgt == u->sz)
        {
            short match = SP_TAG2(sp);

            // change target
            u->jump_speed = u->vel_tgt;
            u->vel_rate = labs(u->vel_rate);
            u->z_tgt = sp->z;

            RESET_BOOL8(sp);
            SetVatorInactive(SpriteNum);

            // set owner swith back to OFF
            // only if ALL vators are inactive
            if (!TestVatorMatchActive(match))
            {
                //VatorSwitch(match, OFF);
            }

            if (SP_TAG6(sp) && TEST_BOOL5(sp))
                DoMatchEverything(u->PlayerP, SP_TAG6(sp), -1);
        }

        // operate only once
        if (TEST_BOOL2(sp))
        {
            SetVatorInactive(SpriteNum);
            KillSprite(SpriteNum);
            return 0;
        }

        // setup to go back to the original z
        if (*lptr != u->oz)
        {
            if (u->WaitTics)
                u->Tics = u->WaitTics;
        }
    }
    else // if (*lptr == u->z_tgt)
    {
        // if heading for the OFF (original) position and should NOT CRUSH
        if (TEST_BOOL3(sp) && u->z_tgt == u->oz)
        {
            int i,nexti;
            SPRITEp bsp;
            USERp bu;
            SWBOOL found = FALSE;

            TRAVERSE_SPRITE_SECT(headspritesect[sp->sectnum], i, nexti)
            {
                bsp = &sprite[i];
                bu = User[i];

                if (bsp->statnum == STAT_ENEMY)
                {
                    if (labs(sectp->ceilingz - sectp->floorz) < SPRITEp_SIZE_Z(bsp))
                    {
                        InitBloodSpray(i, TRUE, -1);
                        UpdateSinglePlayKills(i);
                        KillSprite(i);
                        continue;
                    }
                }

                if (bu && TEST(bsp->cstat, CSTAT_SPRITE_BLOCK) && TEST(bsp->extra, SPRX_PLAYER_OR_ENEMY))
                {
                    // found something blocking so reverse to ON position
                    ReverseVator(SpriteNum);
                    SET_BOOL8(sp); // tell vator that something blocking door
                    found = TRUE;
                    break;
                }
            }

            if (!found)
            {
                short pnum;
                PLAYERp pp;
                // go ahead and look for players clip box bounds
                TRAVERSE_CONNECT(pnum)
                {
                    pp = Player + pnum;

                    if (pp->lo_sectp == &sector[sp->sectnum] ||
                        pp->hi_sectp == &sector[sp->sectnum])
                    {
                        ReverseVator(SpriteNum);

                        u->vel_rate = -u->vel_rate;
                        found = TRUE;
                    }
                }
            }
        }
        else
        {
            int i,nexti;
            SPRITEp bsp;

            TRAVERSE_SPRITE_SECT(headspritesect[sp->sectnum], i, nexti)
            {
                bsp = &sprite[i];

                if (bsp->statnum == STAT_ENEMY)
                {
                    if (labs(sectp->ceilingz - sectp->floorz) < SPRITEp_SIZE_Z(bsp))
                    {
                        InitBloodSpray(i, TRUE, -1);
                        UpdateSinglePlayKills(i);
                        KillSprite(i);
                        continue;
                    }
                }
            }
        }
    }

    return 0;
}

int DoVatorAuto(short SpriteNum)
{
    USERp u = User[SpriteNum];
    SPRITEp sp = u->SpriteP;
    SECTORp sectp = &sector[sp->sectnum];
    int *lptr;
    int amt;

    if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
    {
        lptr = &sectp->ceilingz;
        amt = DoVatorMove(SpriteNum, lptr);
        MoveSpritesWithSector(sp->sectnum, amt, 1); // ceiling
    }
    else
    {
        lptr = &sectp->floorz;
        amt = DoVatorMove(SpriteNum, lptr);
        MoveSpritesWithSector(sp->sectnum, amt, 0); // floor
    }

    // EQUAL this entry has finished
    if (*lptr == u->z_tgt)
    {
        // in the UP position
        if (u->z_tgt == sp->z)
        {
            // change target
            u->z_tgt = u->sz;
            u->vel_rate = -u->vel_rate;
            u->Tics = u->WaitTics;

            if (SP_TAG6(sp))
                DoMatchEverything(u->PlayerP, SP_TAG6(sp), -1);
        }
        else
        // in the DOWN position
        if (u->z_tgt == u->sz)
        {
            // change target
            u->jump_speed = u->vel_tgt;
            u->vel_rate = labs(u->vel_rate);
            u->z_tgt = sp->z;
            u->Tics = u->WaitTics;

            if (SP_TAG6(sp) && TEST_BOOL5(sp))
                DoMatchEverything(NULL, SP_TAG6(sp), -1);
        }
    }

    return 0;
}

#include "saveable.h"

static saveable_code saveable_vator_code[] =
{
    SAVE_CODE(ReverseVator),
    SAVE_CODE(VatorSwitch),
    SAVE_CODE(SetVatorActive),
    SAVE_CODE(SetVatorInactive),
    SAVE_CODE(DoVatorOperate),
    SAVE_CODE(DoVatorMatch),
    SAVE_CODE(TestVatorMatchActive),
    SAVE_CODE(InterpSectorSprites),
    SAVE_CODE(MoveSpritesWithSector),
    SAVE_CODE(DoVatorMove),
    SAVE_CODE(DoVator),
    SAVE_CODE(DoVatorAuto),
};

saveable_module saveable_vator =
{
    // code
    saveable_vator_code,
    SIZ(saveable_vator_code),

    // data
    NULL,0
};


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "build.h"

#include "names2.h"
#include "game.h"
#include "tags.h"
#include "common_game.h"
#include "break.h"
#include "quake.h"
#include "pal.h"
#include "sprite.h"

extern short NormalVisibility;  // player.c

#define VIS_VisCur(sp) (SP_TAG2(sp))
#define VIS_VisDir(sp) (SP_TAG3(sp))
#define VIS_VisGoal(sp) (SP_TAG4(sp))

void ProcessVisOn(void)
{
    short i, nexti;
    SPRITEp sp;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_VIS_ON], i, nexti)
    {
        sp = &sprite[i];

        if (VIS_VisDir(sp))
        {
            // get brighter
            VIS_VisCur(sp) >>= 1;
            //VIS_VisCur(sp) -= 16;
            if (VIS_VisCur(sp) <= VIS_VisGoal(sp))
            {
                VIS_VisCur(sp) = VIS_VisGoal(sp);
                VIS_VisDir(sp) ^= 1;
            }
        }
        else
        {
            // get darker
            VIS_VisCur(sp) <<= 1;
            VIS_VisCur(sp) += 1;
            //VIS_VisCur(sp) += 16;
            if (VIS_VisCur(sp) >= NormalVisibility)
            {
                VIS_VisCur(sp) = NormalVisibility;
                if (sp->owner >= 0)
                {
                    ASSERT(User[sp->owner]);
                    RESET(User[sp->owner]->Flags2, SPR2_VIS_SHADING);
                }
                KillSprite(i);
            }
        }
    }
}

void VisViewChange(PLAYERp pp, int *vis)
{
    short i, nexti;
    SPRITEp sp;
    short BrightestVis = NormalVisibility;
    int x,y,z;
    short sectnum;

    if (GamePaused)
        return;

    // find the closest quake - should be a strength value
    TRAVERSE_SPRITE_STAT(headspritestat[STAT_VIS_ON], i, nexti)
    {
        sp = &sprite[i];

        if (sp->owner >= 0)
        {
            x = sprite[sp->owner].x;
            y = sprite[sp->owner].y;
            z = sprite[sp->owner].z;
            sectnum = sprite[sp->owner].sectnum;
        }
        else
        {
            x = sp->x;
            y = sp->y;
            z = sp->z;
            sectnum = sp->sectnum;
        }

        // save off the brightest vis that you can see
        if (FAFcansee(pp->posx, pp->posy, pp->posz, pp->cursectnum, x, y, z, sectnum))
        {
            if (VIS_VisCur(sp) < BrightestVis)
                BrightestVis = VIS_VisCur(sp);
        }
    }

    *vis = BrightestVis;
}

int SpawnVis(short Parent, short sectnum, int x, int y, int z, int amt)
{
    short SpriteNum;
    SPRITEp sp;
    short i,nexti;

    if (Parent >= 0)
    {
        if (sector[sprite[Parent].sectnum].floorpal == PALETTE_FOG)
            return -1;

        if (sector[sprite[Parent].sectnum].floorpal == PALETTE_DIVE_LAVA)
            return -1;

        // kill any others with the same parent
        TRAVERSE_SPRITE_STAT(headspritestat[STAT_VIS_ON], i, nexti)
        {
            sp = &sprite[i];
            if (sp->owner == Parent)
            {
                KillSprite(i);
            }
        }

        SpriteNum = COVERinsertsprite(sprite[Parent].sectnum, STAT_VIS_ON);
        sp = &sprite[SpriteNum];

        sp->owner = Parent;

        ASSERT(User[Parent]);
        SET(User[Parent]->Flags2, SPR2_CHILDREN);

        sp->x = sprite[Parent].x;
        sp->y = sprite[Parent].y;
        sp->z = sprite[Parent].z;

        SET(User[Parent]->Flags2, SPR2_VIS_SHADING);
    }
    else
    {
        if (sector[sectnum].floorpal == PALETTE_FOG)
            return -1;

        SpriteNum = COVERinsertsprite(sectnum, STAT_VIS_ON);
        sp = &sprite[SpriteNum];

        sp->x = x;
        sp->y = y;
        sp->z = z - Z(20);
        sp->owner = -1;
    }

    sp->cstat = 0;
    sp->extra = 0;

    VIS_VisDir(sp) = 1;
    VIS_VisCur(sp) = NormalVisibility;
    VIS_VisGoal(sp) = amt;

    return SpriteNum;
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------

void ProcessVisOn(void);
void VisViewChange(PLAYERp pp, int *vis);
int SpawnVis(short Parent, short sectnum, int x, int y, int z, int amt);


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "build.h"

//#include "keys.h"
#include "names2.h"
//#include "panel.h"
#include "game.h"
#include "tags.h"
#include "common_game.h"
#include "weapon.h"
#include "sprite.h"

//#include "ai.h"

SECTOR_OBJECTp DetectSectorObjectByWall(WALLp);

void SOwallmove(SECTOR_OBJECTp sop, SPRITEp sp, WALLp find_wallp, int dist, int *nx, int *ny)
{
    int j,k,wallcount;
    WALLp wp;
    short startwall,endwall;
    SECTORp *sectp;

    if (TEST(sop->flags, SOBJ_SPRITE_OBJ))
        return;

    wallcount = 0;
    for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
    {
        startwall = (*sectp)->wallptr;
        endwall = startwall + (*sectp)->wallnum - 1;

        // move all walls in sectors back to the original position
        for (wp = &wall[startwall], k = startwall; k <= endwall; wp++, k++)
        {
            // find the one wall we want to adjust
            if (wp == find_wallp)
            {
                short ang;
                // move orig x and y in saved angle
                ASSERT(User[sp - sprite]);
                ang = User[sp - sprite]->sang;

                *nx = ((dist * sintable[NORM_ANGLE(ang + 512)])>>14);
                *ny = ((dist * sintable[ang])>>14);

                sop->xorig[wallcount] -= *nx;
                sop->yorig[wallcount] -= *ny;

                SET(sop->flags, SOBJ_UPDATE_ONCE);
                return;
            }

            wallcount++;
        }
    }
}

int DoWallMove(SPRITEp sp)
{
    int dist,nx,ny;
    short shade1,shade2,ang,picnum1,picnum2;
    WALLp wallp;
    short prev_wall;
    SWBOOL found = FALSE;
    short dang;
    SWBOOL SOsprite = FALSE;

    dist = SP_TAG13(sp);
    ang = SP_TAG4(sp);
    picnum1 = SP_TAG5(sp);
    picnum2 = SP_TAG6(sp);
    shade1 = SP_TAG7(sp);
    shade2 = SP_TAG8(sp);
    dang = ((int)SP_TAG10(sp)) << 3;

    if (dang)
        ang = NORM_ANGLE(ang + (RANDOM_RANGE(dang) - dang/2));

    nx = (dist * sintable[NORM_ANGLE(ang + 512)])>>14;
    ny = (dist * sintable[ang])>>14;

    for (wallp = wall; wallp < &wall[numwalls]; wallp++)
    {
        if (wallp->x == sp->x && wallp->y == sp->y)
        {
            found = TRUE;

            if (TEST(wallp->extra, WALLFX_SECTOR_OBJECT))
            {
                SECTOR_OBJECTp sop;
                sop = DetectSectorObjectByWall(wallp);
                ASSERT(sop);
                SOwallmove(sop, sp, wallp, dist, &nx, &ny);

                SOsprite = TRUE;
            }
            else
            {
                wallp->x = sp->x + nx;
                wallp->y = sp->y + ny;
            }

            if (shade1)
                wallp->shade = shade1;
            if (picnum1)
                wallp->picnum = picnum1;

            // find the previous wall
            prev_wall = PrevWall(wallp - wall);
            if (shade2)
                wall[prev_wall].shade = shade2;
            if (picnum2)
                wall[prev_wall].picnum = picnum2;
        }
    }

    SP_TAG9(sp)--;
    if ((signed char)SP_TAG9(sp) <= 0)
    {
        KillSprite(sp - sprite);
    }
    else
    {
        if (SOsprite)
        {
            // move the sprite offset from center
            User[sp - sprite]->sx -= nx;
            User[sp - sprite]->sy -= ny;
        }
        else
        {
            sp->x += nx;
            sp->y += ny;
        }
    }

    return found;
}

SWBOOL CanSeeWallMove(SPRITEp wp, short match)
{
    short i,nexti;
    SWBOOL found = FALSE;
    SPRITEp sp;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_WALL_MOVE_CANSEE], i, nexti)
    {
        sp = &sprite[i];

        if (SP_TAG2(sp) == match)
        {
            found = TRUE;

            if (cansee(wp->x,wp->y,wp->z,wp->sectnum,sp->x,sp->y,sp->z,sp->sectnum))
            {
                return TRUE;
            }
        }
    }

    if (found)
        return FALSE;
    else
        return TRUE;
}

int DoWallMoveMatch(short match)
{
    SPRITEp sp;
    short i,nexti;
    SWBOOL found = FALSE;

    // just all with the same matching tags
    TRAVERSE_SPRITE_STAT(headspritestat[STAT_WALL_MOVE], i, nexti)
    {
        sp = &sprite[i];

        if (SP_TAG2(sp) == match)
        {
            found = TRUE;
            DoWallMove(sp);
        }
    }

    return found;
}

#include "saveable.h"

static saveable_code saveable_wallmove_code[] =
{
    SAVE_CODE(DoWallMove),
    SAVE_CODE(CanSeeWallMove),
    SAVE_CODE(DoWallMoveMatch),
};

saveable_module saveable_wallmove =
{
    // code
    saveable_wallmove_code,
    SIZ(saveable_wallmove_code),

    // data
    NULL,0
};


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "build.h"

#include "names2.h"
#include "panel.h"
#include "game.h"

#include "warp.h"

////////////////////////////////////////////////////////////////////////////////
//
// WARPING - PLANE STYLE
//
////////////////////////////////////////////////////////////////////////////////

extern SWBOOL Prediction;

SWBOOL
WarpPlaneSectorInfo(short sectnum, SPRITEp *sp_ceiling, SPRITEp *sp_floor)
{
    int i,nexti;
    SPRITEp sp;

    *sp_floor = NULL;
    *sp_ceiling = NULL;

    if (Prediction)
        return FALSE;

    if (sectnum < 0 || !TEST(sector[sectnum].extra, SECTFX_WARP_SECTOR))
        return FALSE;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_WARP], i, nexti)
    {
        sp = &sprite[i];

        if (sp->sectnum == sectnum)
        {
            // skip - don't teleport
            if (SP_TAG10(sp) == 1)
                continue;

            if (sp->hitag == WARP_CEILING_PLANE)
            {
                *sp_ceiling = sp;
            }
            else if (sp->hitag == WARP_FLOOR_PLANE)
            {
                *sp_floor = sp;
            }
        }
    }

    return TRUE;
}

SPRITEp
WarpPlane(int32_t* x, int32_t* y, int32_t* z, int16_t* sectnum)
{
    SPRITEp sp_floor, sp_ceiling;

    if (Prediction)
        return NULL;

    if (!WarpPlaneSectorInfo(*sectnum, &sp_ceiling, &sp_floor))
        return NULL;

    if (sp_ceiling)
    {
        if (*z <= sp_ceiling->z)
        {
            return WarpToArea(sp_ceiling, x, y, z, sectnum);
        }
    }

    if (sp_floor)
    {
        if (*z >= sp_floor->z)
        {
            return WarpToArea(sp_floor, x, y, z, sectnum);
        }
    }

    return NULL;
}

SPRITEp
WarpToArea(SPRITEp sp_from, int32_t* x, int32_t* y, int32_t* z, int16_t* sectnum)
{
    int xoff;
    int yoff;
    int zoff;
    short i, nexti;
    SPRITEp sp = sp_from;
    short match;
    short to_tag = 0;
    short match_rand[16];
    int z_adj = 0;

    xoff = *x - sp->x;
    yoff = *y - sp->y;
    zoff = *z - sp->z;
    match = sp->lotag;

#if 0
    TAG 2 = match
            TAG 3 = Type
                    Sprite - 0,32 always teleports you to the center at the angle the sprite is facing
    Offset - 1 always teleports you by the offset.Does not touch the angle
    TAG 4 = angle
            TAG 5 to 8 = random match locations
#endif

    memset(match_rand,0,sizeof(match_rand));

    switch (sp->hitag)
    {
    case WARP_TELEPORTER:
        to_tag = WARP_TELEPORTER;

        // if tag 5 has something this is a random teleporter
        if (SP_TAG5(sp))
        {
            short ndx = 0;
            match_rand[ndx++] = SP_TAG2(sp);
            match_rand[ndx++] = SP_TAG5(sp);
            if (SP_TAG6(sp))
                match_rand[ndx++] = SP_TAG6(sp);
            if (SP_TAG7(sp))
                match_rand[ndx++] = SP_TAG7(sp);
            if (SP_TAG8(sp))
                match_rand[ndx++] = SP_TAG8(sp);

            // reset the match you are looking for
            match = match_rand[RANDOM_RANGE(ndx)];
        }
        break;
    case WARP_CEILING_PLANE:
        to_tag = WARP_FLOOR_PLANE;
        // make sure you warp outside of warp plane
        z_adj = -Z(2);
        break;
    case WARP_FLOOR_PLANE:
        to_tag = WARP_CEILING_PLANE;
        // make sure you warp outside of warp plane
        z_adj = Z(2);
        break;
    }

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_WARP], i, nexti)
    {
        SPRITEp sp = &sprite[i];

        if (sp->lotag == match && sp != sp_from)
        {
            // exp: WARP_CEILING or WARP_CEILING_PLANE
            if (sp->hitag == to_tag)
            {
                if ((unsigned)sp->sectnum >= MAXSECTORS)
                    return nullptr;

                // determine new x,y,z position
                *x = sp->x + xoff;
                *y = sp->y + yoff;
                *z = sp->z + zoff;

                // make sure you warp outside of warp plane
                *z += z_adj;

                // get new sector
                *sectnum = sp->sectnum;
                updatesector(*x, *y, sectnum);

                return sp;
            }
        }
    }

    return NULL;
}

////////////////////////////////////////////////////////////////////////////////
//
// Warp - Teleporter style
//
////////////////////////////////////////////////////////////////////////////////

SWBOOL
WarpSectorInfo(short sectnum, SPRITEp *sp_warp)
{
    int i,nexti;
    SPRITEp sp;

    *sp_warp = NULL;

    if (!TEST(sector[sectnum].extra, SECTFX_WARP_SECTOR))
        return FALSE;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_WARP], i, nexti)
    {
        sp = &sprite[i];

        if (sp->sectnum == sectnum)
        {
            // skip - don't teleport
            if (SP_TAG10(sp) == 1)
                continue;

            if (sp->hitag == WARP_TELEPORTER)
            {
                *sp_warp = sp;
            }
        }
    }

    return TRUE;
}

SPRITEp
Warp(int32_t* x, int32_t* y, int32_t* z, int16_t* sectnum)
{
    SPRITEp sp_warp;

    if (Prediction)
        return NULL;

    if (!WarpSectorInfo(*sectnum, &sp_warp))
        return NULL;

    if (sp_warp)
    {
        return WarpToArea(sp_warp, x, y, z, sectnum);
    }

    return NULL;
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------

SWBOOL WarpPlaneSectorInfo(short sectnum, SPRITEp *sp_ceiling, SPRITEp *sp_floor);
SPRITEp WarpPlane(int32_t* x, int32_t* y, int32_t* z, int16_t* sectnum);
SPRITEp WarpToArea(SPRITEp sp_from, int32_t* x, int32_t* y, int32_t* z, int16_t* sectnum);
SWBOOL WarpSectorInfo(short sectnum, SPRITEp *sp_warp);
SPRITEp Warp(int32_t* x, int32_t* y, int32_t* z, int16_t* sectnum);