//-------------------------------------------------------------------------
/*
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

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

// JPLAYER.C    
// Copyright (c) 1996 by Jim Norwood

#include "build.h"
#include "compat.h"

#include "mytypes.h"
#include "keys.h"
#include "names2.h"
#include "panel.h"
#include "game.h"
#include "tags.h"
#include "player.h"
#include "lists.h"
#include "warp.h"
#include "quake.h"

#include "mathutil.h"
#include "function.h"
#include "control.h"
#include "trigger.h"

#include "savedef.h"
#include "menus.h"
#include "net.h"
#include "pal.h"

#include "bots.h"

BOOL WeaponOK(PLAYERp pp);

#define MAXANGVEL 80

/*
typedef struct 
    {
    SHORT vel;
    SHORT svel;
    CHAR angvel;
    CHAR aimvel;
    LONG bits;
    }SW_PACKET;
 */

// From build.h
#define CLIPMASK0 (((1L)<<16)+1L)
#define CLIPMASK1 (((256L)<<16)+64L)


// PLAYER QUOTES TO OTHER PLAYERS ////////////////////////////////////////////////////////////

#define STARTALPHANUM 4608  // New SW font for typing in stuff, It's in ASCII order.
#define ENDALPHANUM   4701
#define MINIFONT      2930  // Start of small font, it's blue for good palette swapping

#define NUMPAGES 1
#define NUMOFFIRSTTIMEACTIVE 100  // You can save up to 100 strings in the message history queue
                                  
char pus, pub;  // Global text vars
char fta_quotes[NUMOFFIRSTTIMEACTIVE][64];


int gametext(int x,int y,char *t,char s,short dabits)
{
    short ac,newx;
    char centre, *oldt;

    centre = ( x == (320>>1) );
    newx = 0;
    oldt = t;

    if(centre)
    {
        while(*t)
        {
            if(*t == 32) {newx+=5;t++;continue;}
            else ac = *t - '!' + STARTALPHANUM;

            if( ac < STARTALPHANUM || ac > ENDALPHANUM ) break;

            if(*t >= '0' && *t <= '9')
                newx += 8;
            else newx += tilesizx[ac];
            t++;
        }

        t = oldt;
        x = (320>>1)-(newx>>1);
    }

    while(*t)
    {
        if(*t == 32) {x+=5;t++;continue;}
        else ac = *t - '!' + STARTALPHANUM;

        if( ac < STARTALPHANUM || ac > ENDALPHANUM )
            break;

        rotatesprite(x<<16,y<<16,65536L,0,ac,s,16,dabits,0,0,xdim-1,ydim-1);

        if(*t >= '0' && *t <= '9')
            x += 8;
        else x += tilesizx[ac];

        t++;
    }

    return (x);
}

int minigametext(int x,int y,char *t,char s,short dabits)
{
    short ac,newx;
    char centre, *oldt;

    centre = ( x == (320>>1) );
    newx = 0;
    oldt = t;

    if(centre)
    {
        while(*t)
        {
            if(*t == 32) {newx+=4;t++;continue;}
            else ac = *t - '!' + 2930;

            if( (ac < 2930 || ac > 3023) && *t != asc_Space ) break;

            if (*t > asc_Space && *t < 127)
            {
                newx += tilesizx[ac];
            } else
                x += 4;
            
            t++;
        }

        t = oldt;
        x = (320>>1)-(newx>>1);
    }

    while(*t)
    {
        if(*t == 32) {x+=4;t++;continue;}
        else ac = *t - '!' + 2930;

        if( (ac < 2930 || ac > 3023) && *t != asc_Space ) break;

        if (*t > asc_Space && *t < 127)
        {
        rotatesprite(x<<16,y<<16,65536L,0,ac,-128,17,dabits,0,0,xdim-1,ydim-1);
        x += tilesizx[ac];
        } else
            x += 4;    

        t++;
    }

    return (x);
}

int minitext(int x,int y,char *t,char p,char sb)
{
    short ac;

    while(*t)
    {
        *t = toupper(*t);
        if(*t == 32) {x+=5;t++;continue;}
        else ac = *t - '!' + MINIFONT;

        rotatesprite(x<<16,y<<16,65536L,0,ac,0,p,sb,0,0,xdim-1,ydim-1);
        x += 4; // tilesizx[ac]+1;

        t++;
    }
    return (x);
}

int minitextshade(int x,int y,char *t,char s,char p,char sb)
{
    short ac;

    while(*t)
    {
        *t = toupper(*t);
        if(*t == 32) {x+=5;t++;continue;}
        else ac = *t - '!' + MINIFONT;

        rotatesprite(x<<16,y<<16,65536L,0,ac,s,p,sb,0,0,xdim-1,ydim-1);
        x += 4; // tilesizx[ac]+1;

        t++;
    }
    return (x);
}

long quotebot, quotebotgoal;
short user_quote_time[MAXUSERQUOTES];
char user_quote[MAXUSERQUOTES][256];

void adduserquote(char *daquote)
{
    long i;

    SetRedrawScreen(Player+myconnectindex);
    
    for(i=MAXUSERQUOTES-1;i>0;i--)
    {
        strcpy(user_quote[i],user_quote[i-1]);
        user_quote_time[i] = user_quote_time[i-1];
    }
    strcpy(user_quote[0],daquote);
    user_quote_time[0] = 180;
}

void operatefta(void)
{
     long i, j, k;

     j=MESSAGE_LINE; // Base line position on screen
     quotebot = min(quotebot,j);
     quotebotgoal = min(quotebotgoal,j);
     if (MessageInputMode) 
         j -= 6; // Bump all lines up one to make room for new line
     quotebotgoal = j; 
     j = quotebot;

     for(i=0;i<MAXUSERQUOTES;i++)
     {
         k = user_quote_time[i]; 
         if (k <= 0) 
             break;

         if (gs.BorderNum <= BORDER_BAR+1)    
            {
            // dont fade out
             if (k > 4)
                  minigametext(320>>1,j,user_quote[i],0,2+8);
             else 
             if (k > 2) 
                minigametext(320>>1,j,user_quote[i],0,2+8+1);
             else 
                minigametext(320>>1,j,user_quote[i],0,2+8+1+32);
            }    
        else
            {
            // dont fade out
            minigametext(320>>1,j,user_quote[i],0,2+8);
            }
            
         j -= 6;
     }
}

//////////// Console Message Queue ////////////////////////////////////
long conbot, conbotgoal;
char con_quote[MAXCONQUOTES][256];

void addconquote(char *daquote)
{
    long i;

    for(i=MAXCONQUOTES-1;i>0;i--)
    {
        strcpy(con_quote[i],con_quote[i-1]);
    }
    strcpy(con_quote[0],daquote);
}

#define CON_ROT_FLAGS (ROTATE_SPRITE_CORNER|ROTATE_SPRITE_SCREEN_CLIP|ROTATE_SPRITE_NON_MASK)
void operateconfta(void)
{
     long i, j, k;

     if(!ConPanel) return;  // If panel isn't up, don't draw anything

     // Draw the background console pic
     rotatesprite((0)<<16,(0)<<16,65536L,0,5119,0,0,CON_ROT_FLAGS,0,0,xdim-1,ydim-1);

     j=99; // Base line position on screen
     conbot = min(conbot,j);
     conbotgoal = min(conbotgoal,j);
     if(ConInputMode) j -= 6; // Bump all lines up one to make room for new line
     conbotgoal = j; j = conbot;

     for(i=0;i<MAXCONQUOTES;i++)
     {
         MNU_DrawSmallString(27, j, con_quote[i], 0, 17); // 17 = white
         j -= 6;
     }
}

// BOT STUFF ////////////////////////////////////////////////////////////////////////////////

void BOT_UseInventory(PLAYERp p, short targetitem, SW_PACKET *syn)
    {
    // Try to get to item
    if (p->InventoryNum == targetitem)
        syn->bits |= (1<<SK_INV_USE);
    else
        {
        syn->bits |= (1<<SK_INV_LEFT);  // Scroll to it    
        syn->bits |= (1<<SK_INV_USE); // Use whatever you're on too
        }
    }

void BOT_ChooseWeapon(PLAYERp p, USERp u, SW_PACKET *syn)
    { 
    short weap;

    // If you have a nuke, fire it
    if (u->WeaponNum == WPN_MICRO && p->WpnRocketNuke && p->WpnRocketType != 2)
        {
        syn->bits ^= 15;
        syn->bits |= 4;
        } else
    for(weap=9;weap>=0;weap--)
        {
        if (weap <= u->WeaponNum) break;
        if (TEST(p->WpnFlags, BIT(weap)) && p->WpnAmmo[weap] > DamageData[weap].min_ammo)
            {
            syn->bits ^= 15;
            syn->bits |= weap;
            break;
            }
        }
    } 
       
long getspritescore(long snum, long dapicnum)
{

    switch(dapicnum)
    {
        case ICON_STAR: return(5);
        case ICON_UZI: return(20); 
        case ICON_UZIFLOOR: return(20);
        case ICON_LG_UZI_AMMO: return(15);
        case ICON_HEART: return(160);
        case ICON_HEART_LG_AMMO: return(60);
        case ICON_GUARD_HEAD: return(170); 
        case ICON_FIREBALL_LG_AMMO: return(70); 
        case ICON_ROCKET: return(100);    
        case ICON_SHOTGUN: return(130);   
        case ICON_LG_ROCKET: return(100); 
        case ICON_LG_SHOTSHELL: return(30);
        case ICON_MICRO_GUN: return(200); 
        case ICON_MICRO_BATTERY: return(100);
        case ICON_GRENADE_LAUNCHER: return(150);
        case ICON_LG_GRENADE: return(50); 
        case ICON_LG_MINE: return(150); 
        case ICON_RAIL_GUN: return(180); 
        case ICON_RAIL_AMMO: return(80);

        case ST_QUICK_EXIT: 
        case ST_QUICK_SCAN: 
        case ICON_MEDKIT:
        case ICON_CHEMBOMB:
        case ICON_FLASHBOMB:
        case ICON_NUKE:
        case ICON_CALTROPS:
        case TRACK_SPRITE:
        case ST1:
        case ST2:
        case ST_QUICK_JUMP:
        case ST_QUICK_JUMP_DOWN:
        case ST_QUICK_SUPER_JUMP: return(120); break;

        // Commented out for now, example.
//        case FREEZEAMMO: if (ps[snum].ammo_amount[FREEZE_WEAPON] < max_ammo_amount[FREEZE_WEAPON]) return(10); else return(0);
    }


    return(0);
}

static long fdmatrix[13][13] =
{
 //SWRD  SHUR  UZI SHOT RPG  40MM MINE RAIL HEAD HEAD2HEAD3HEART
   { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},   //SWRD
   {1024, 512, 128, 128,2560, 128,2560, 128,2560,2560,2560, 128, 128},   //SHUR
   {2560,1024, 512, 512,2560, 128,2560,2560,1024,2560,2560,2560,2560},   //UZI
   { 512, 512, 512, 512,2560, 128,2560, 512, 512, 512, 512, 512, 512},   //SHOT
   {2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560},   //RPG
   { 512, 512, 512, 512,2048, 512,2560,2560, 512,2560,2560,2560,2560},   //40MM
   { 128, 128, 128, 128, 512, 128, 128, 128, 128, 128, 128, 128, 128},   //MINE
   {1536,1536,1536,1536,2560,1536,1536,1536,1536,1536,1536,1536,1536},   //RAIL
   {2560,1024, 512,1024,1024,1024,2560, 512,1024,2560,2560, 512, 512},   //HEAD1
   { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},   //HEAD2
   { 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512},   //HEAD3
   {1024, 512, 128, 128,2560, 512,2560,1024, 128,2560,1024,1024,1024},   //HEART
};

static long goalx[MAX_SW_PLAYERS_REG], goaly[MAX_SW_PLAYERS_REG], goalz[MAX_SW_PLAYERS_REG];
static long goalsect[MAX_SW_PLAYERS_REG], goalwall[MAX_SW_PLAYERS_REG], goalsprite[MAX_SW_PLAYERS_REG];
static long goalplayer[MAX_SW_PLAYERS_REG], clipmovecount[MAX_SW_PLAYERS_REG];
short searchsect[MAXSECTORS], searchparent[MAXSECTORS];
char dashow2dsector[(MAXSECTORS+7)>>3];

void computergetinput(long snum, SW_PACKET *syn)
{
    long i, j, k, l, x1, y1, z1, x2, y2, z2, x3, y3, z3, dx, dy, nextj;
    long dist, daang, zang, fightdist, damyang, damysect;
    long startsect, endsect, splc, send, startwall, endwall;
    short dasect, dawall, daspr;
    PLAYERp p;
    walltype *wal;
    long myx, myy, myz, myang, mycursectnum;
    USERp u;
    short weap;
    //extern BOOL Pachinko_Win_Cheat;

    if(!MoveSkip4) return;  // Make it so the bots don't slow the game down so bad!

    p = &Player[snum];
    u = User[p->PlayerSprite];  // Set user struct

    // Copy current weapon number to player struct
    p->WpnNum = u->WeaponNum;
    if (p->WpnNum >= MAX_WEAPONS) p->WpnNum = MAX_WEAPONS-1;
    
    // Init local position variables
    myx = p->posx;
    myy = p->posy;
    myz = p->posz;
    myang = p->pang;
    mycursectnum = p->cursectnum;

    // Reset input bits
    syn->vel = 0;
    syn->svel = 0;
    syn->angvel = 0;
    syn->aimvel = 0;
    syn->bits = 0;

    x1 = p->posx;
    y1 = p->posy;
    z1 = p->posz;
    
    damyang = p->pang;
    damysect = sprite[p->PlayerSprite].sectnum;
    if ((numplayers >= 2) && (snum == myconnectindex))
        { x1 = myx; y1 = myy; z1 = myz+PLAYER_HEIGHT; damyang = myang; damysect = mycursectnum; }

    // Always operate everything    
    syn->bits |= (1<<SK_OPERATE);

    // If bot can't see the goal enemy, set target to himself so that he
    // will pick a new target    
    if (TEST(Player[goalplayer[snum]].Flags, PF_DEAD) || STD_RANDOM_RANGE(1000) > 800)
        goalplayer[snum] = snum;
    else
        {    
        x2 = Player[goalplayer[snum]].posx;
        y2 = Player[goalplayer[snum]].posy;
        z2 = Player[goalplayer[snum]].posz;
        if (!FAFcansee(x1,y1,z1-(48<<8),damysect,x2,y2,z2-(48<<8),sprite[Player[goalplayer[snum]].PlayerSprite].sectnum))
            goalplayer[snum] = snum;
        }    

    // Pick a new target player if goal is dead or target is itself
    if (goalplayer[snum] == snum)
    {
        j = 0x7fffffff;
        for(i=connecthead;i>=0;i=connectpoint2[i])
            if (i != snum)
            {
                if (TEST(Player[i].Flags, PF_DEAD))
                    continue;

                x2 = Player[i].posx;
                y2 = Player[i].posy;
                z2 = Player[i].posz;

                if (!FAFcansee(x1,y1,z1-(48<<8),damysect,x2,y2,z2-(48<<8),sprite[Player[i].PlayerSprite].sectnum))
                    continue;

                dist = ksqrt((sprite[Player[i].PlayerSprite].x-x1)*(sprite[Player[i].PlayerSprite].x-x1)+(sprite[Player[i].PlayerSprite].y-y1)*(sprite[Player[i].PlayerSprite].y-y1));

                if (dist < j) { j = dist; goalplayer[snum] = i; }
            }
    }

    // Pick a weapon    
    BOT_ChooseWeapon(p, u, syn);

    // x2,y2,z2 is the coordinates of the target sprite
    x2 = Player[goalplayer[snum]].posx;
    y2 = Player[goalplayer[snum]].posy;
    z2 = Player[goalplayer[snum]].posz;

    // If bot is dead, either barf or respawn
    if (TEST(p->Flags, PF_DEAD))
        {
        if (STD_RANDOM_RANGE(1000) > 990)
            {
            syn->bits |= (1<<SK_SPACE_BAR);  // Respawn
            }
        else
            syn->bits |= (1<<SK_SHOOT);    // Try to barf
        }

    // Need Health?
    if(u->Health < p->MaxHealth)
        BOT_UseInventory(p, INVENTORY_MEDKIT, syn);

    // Check the missile stat lists to see what's being fired and
    // take the appropriate action    
    TRAVERSE_SPRITE_STAT(headspritestat[STAT_MISSILE], j, nextj)
    {
        switch (sprite[j].picnum)
        {
            case FIREBALL: k = 0; break;
            case BOLT_THINMAN_R0:
                k = 0;
                syn->bits |= (1<<SK_JUMP); // Always jump when rockets being fired!
            break;
            default: k = 0; break;
        }
        if (k)
        {
            x3 = sprite[j].x;
            y3 = sprite[j].y;
            z3 = sprite[j].z;
            for(l=0;l<=8;l++)
            {
                if (tmulscale11(x3-x1,x3-x1,y3-y1,y3-y1,(z3-z1)>>4,(z3-z1)>>4) < 3072)
                {
                    dx = sintable[(sprite[j].ang+512)&2047];
                    dy = sintable[sprite[j].ang&2047];
                    if ((x1-x3)*dy > (y1-y3)*dx) i = -k*512; else i = k*512;
                    syn->vel -= mulscale17(dy,i);
                    syn->svel += mulscale17(dx,i);
                }

                if (l < 7)
                {
                    x3 += (mulscale14(sprite[j].xvel,sintable[(sprite[j].ang+512)&2047])<<2);
                    y3 += (mulscale14(sprite[j].xvel,sintable[sprite[j].ang&2047])<<2);
                    z3 += (sprite[j].zvel<<2);
                }
                else
                {
                    hitscan(sprite[j].x,sprite[j].y,sprite[j].z,sprite[j].sectnum,
                     mulscale14(sprite[j].xvel,sintable[(sprite[j].ang+512)&2047]),
                     mulscale14(sprite[j].xvel,sintable[sprite[j].ang&2047]),
                     (long)sprite[j].zvel,
                     &dasect,&dawall,&daspr,&x3,&y3,&z3,CLIPMASK1);
                }
            }
        }
    }


    if (!TEST(Player[goalplayer[snum]].Flags, PF_DEAD) && snum != goalplayer[snum] &&
        ((FAFcansee(x1,y1,z1-(24<<8),damysect,x2,y2,z2-(24<<8),sprite[Player[goalplayer[snum]].PlayerSprite].sectnum)) ||
         (FAFcansee(x1,y1,z1-(48<<8),damysect,x2,y2,z2-(48<<8),sprite[Player[goalplayer[snum]].PlayerSprite].sectnum))))
    {
        // Shoot how often by skill level
        short shootrnd=0;

        shootrnd = STD_RANDOM_RANGE(1000);
        
        if ((Skill == 0 && shootrnd > 990) ||
            (Skill == 1 && shootrnd > 550) ||
            (Skill == 2 && shootrnd > 350) ||
            (Skill == 3))
            syn->bits |= (1<<SK_SHOOT);
        else    
            syn->bits &= ~(1<<SK_SHOOT);

        // Jump sometimes, to try to be evasive
        if(STD_RANDOM_RANGE(256) > 252)
            syn->bits |= (1<<SK_JUMP);

        // Make sure selected weapon is in range
        //ASSERT(p->WpnNum < MAX_WEAPONS);
        //ASSERT(Player[goalplayer[snum]].WpnNum < MAX_WEAPONS);

        // Only fire explosive type weaps if you are not too close to the target!
        if (u->WeaponNum == WPN_MICRO || u->WeaponNum == WPN_GRENADE || u->WeaponNum == WPN_RAIL)
        {
            long x4,y4,z4;
            hitscan(x1,y1,z1-PLAYER_HEIGHT,damysect,sintable[(damyang+512)&2047],sintable[damyang&2047],
                (100-p->horiz-p->horizoff)*32,&dasect,&dawall,&daspr,&x4,&y4,&z4,CLIPMASK1);
            if ((x4-x1)*(x4-x1)+(y4-y1)*(y4-y1) < 2560*2560) syn->bits &= ~(1<<SK_SHOOT);
        }

        // Get fighting distance based on you and your opponents current weapons    
        fightdist = fdmatrix[p->WpnNum][Player[goalplayer[snum]].WpnNum];
        if (fightdist < 128) fightdist = 128;
            
        // Figure out your distance from the enemy target sprite    
        dist = ksqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (dist == 0) dist = 1;
        daang = NORM_ANGLE(getangle(x2+(Player[goalplayer[snum]].xvect>>14)-x1,y2+(Player[goalplayer[snum]].yvect>>14)-y1));

        zang = 100-((z2-z1)*8)/dist;
        fightdist = max(fightdist,(klabs(z2-z1)>>4));

        x3 = x2+((x1-x2)*fightdist/dist);
        y3 = y2+((y1-y2)*fightdist/dist);
        syn->vel += (x3-x1)*2047/dist;
        syn->svel += (y3-y1)*2047/dist;

        //Strafe attack
        if (fightdist)
        {
            j = totalclock+snum*13468;
            i = sintable[(j<<6)&2047];
            i += sintable[((j+4245)<<5)&2047];
            i += sintable[((j+6745)<<4)&2047];
            i += sintable[((j+15685)<<3)&2047];
            dx = sintable[(sprite[Player[goalplayer[snum]].PlayerSprite].ang+512)&2047];
            dy = sintable[sprite[Player[goalplayer[snum]].PlayerSprite].ang&2047];
            if ((x1-x2)*dy > (y1-y2)*dx) i += 8192; else i -= 8192;
            syn->vel += ((sintable[(daang+1024)&2047]*i)>>17);
            syn->svel += ((sintable[(daang+512)&2047]*i)>>17);
        }

        // Make aiming and running speed suck by skill level
        if (Skill == 0)
            {
            daang = NORM_ANGLE((daang-256) + STD_RANDOM_RANGE(512));
            syn->vel -= syn->vel/2;
            syn->svel -= syn->svel/2;
            }
        else
        if (Skill == 1)    
            {
            daang = NORM_ANGLE((daang-128) + STD_RANDOM_RANGE(256));
            syn->vel -= syn->vel/8;
            syn->svel -= syn->svel/8;
            }
        else
        if (Skill == 2)
            daang = NORM_ANGLE((daang-64) + STD_RANDOM_RANGE(128));

        // Below formula fails in certain cases    
        //syn->angvel = min(max((((daang+1024-damyang)&2047)-1024)>>1,-MAXANGVEL),MAXANGVEL); //was 127
        p->pang = daang;
        syn->aimvel = min(max((zang-p->horiz)>>1,-PLAYER_HORIZ_MAX),PLAYER_HORIZ_MAX);
        // Sets type of aiming, auto aim for bots
        syn->bits |= (1<<SK_AUTO_AIM);
        return;
    }
    
    goalsect[snum] = -1;

#if 1
    if (goalsect[snum] < 0)
    {
        goalwall[snum] = -1;
        startsect = sprite[p->PlayerSprite].sectnum;
        endsect = sprite[Player[goalplayer[snum]].PlayerSprite].sectnum;

        clearbufbyte(dashow2dsector,(MAXSECTORS+7)>>3,0L);
        searchsect[0] = startsect;
        searchparent[0] = -1;
        dashow2dsector[startsect>>3] |= (1<<(startsect&7));
        for(splc=0,send=1;splc<send;splc++)
        {
            startwall = sector[searchsect[splc]].wallptr;
            endwall = startwall + sector[searchsect[splc]].wallnum;
            for(i=startwall,wal=&wall[startwall];i<endwall;i++,wal++)
            {
                j = wal->nextsector; if (j < 0) continue;

                dx = ((wall[wal->point2].x+wal->x)>>1);
                dy = ((wall[wal->point2].y+wal->y)>>1);
                if ((getceilzofslope(j,dx,dy) > getflorzofslope(j,dx,dy)-(28<<8)) && ((sector[j].lotag < 15) || (sector[j].lotag > 22)))
                    continue;
                if (getflorzofslope(j,dx,dy) < getflorzofslope(searchsect[splc],dx,dy)-(72<<8))
                    continue;
                if ((dashow2dsector[j>>3]&(1<<(j&7))) == 0)
                {
                    dashow2dsector[j>>3] |= (1<<(j&7));
                    searchsect[send] = (short)j;
                    searchparent[send] = (short)splc;
                    send++;
                    if (j == endsect)
                    {
                        clearbufbyte(dashow2dsector,(MAXSECTORS+7)>>3,0L);
                        for(k=send-1;k>=0;k=searchparent[k])
                            dashow2dsector[searchsect[k]>>3] |= (1<<(searchsect[k]&7));

                        for(k=send-1;k>=0;k=searchparent[k])
                            if (!searchparent[k]) break;

                        goalsect[snum] = searchsect[k];
                        startwall = sector[goalsect[snum]].wallptr;
                        endwall = startwall+sector[goalsect[snum]].wallnum;
                        x3 = y3 = 0;
                        for(i=startwall;i<endwall;i++)
                        {
                            x3 += wall[i].x;
                            y3 += wall[i].y;
                        }
                        x3 /= (endwall-startwall);
                        y3 /= (endwall-startwall);

                        startwall = sector[startsect].wallptr;
                        endwall = startwall+sector[startsect].wallnum;
                        l = 0; k = startwall;
                        for(i=startwall;i<endwall;i++)
                        {
                            if (wall[i].nextsector != goalsect[snum]) continue;
                            dx = wall[wall[i].point2].x-wall[i].x;
                            dy = wall[wall[i].point2].y-wall[i].y;

                            //if (dx*(y1-wall[i].y) <= dy*(x1-wall[i].x))
                            //   if (dx*(y2-wall[i].y) >= dy*(x2-wall[i].x))
                                    if ((x3-x1)*(wall[i].y-y1) <= (y3-y1)*(wall[i].x-x1))
                                        if ((x3-x1)*(wall[wall[i].point2].y-y1) >= (y3-y1)*(wall[wall[i].point2].x-x1))
                                            { k = i; break; }

                            dist = ksqrt(dx*dx+dy*dy);
                            if (dist > l) { l = dist; k = i; }
                        }
                        goalwall[snum] = k;
                        daang = ((getangle(wall[wall[k].point2].x-wall[k].x,wall[wall[k].point2].y-wall[k].y)+1536)&2047);
                        goalx[snum] = ((wall[k].x+wall[wall[k].point2].x)>>1)+(sintable[(daang+512)&2047]>>8);
                        goaly[snum] = ((wall[k].y+wall[wall[k].point2].y)>>1)+(sintable[daang&2047]>>8);
                        goalz[snum] = sector[goalsect[snum]].floorz-(32<<8);
                        break;
                    }
                }
            }

            #if 0    
            for(i=headspritesect[searchsect[splc]];i>=0;i=nextspritesect[i])
                if (sprite[i].lotag == 7)
                {
                    j = sprite[sprite[i].owner].sectnum;
                    if ((dashow2dsector[j>>3]&(1<<(j&7))) == 0)
                    {
                        dashow2dsector[j>>3] |= (1<<(j&7));
                        searchsect[send] = (short)j;
                        searchparent[send] = (short)splc;
                        send++;
                        if (j == endsect)
                        {
                            clearbufbyte(dashow2dsector,(MAXSECTORS+7)>>3,0L);
                            for(k=send-1;k>=0;k=searchparent[k])
                                dashow2dsector[searchsect[k]>>3] |= (1<<(searchsect[k]&7));

                            for(k=send-1;k>=0;k=searchparent[k])
                                if (!searchparent[k]) break;

                            goalsect[snum] = searchsect[k];
                            startwall = sector[startsect].wallptr;
                            endwall = startwall+sector[startsect].wallnum;
                            l = 0; k = startwall;
                            for(i=startwall;i<endwall;i++)
                            {
                                dx = wall[wall[i].point2].x-wall[i].x;
                                dy = wall[wall[i].point2].y-wall[i].y;
                                dist = ksqrt(dx*dx+dy*dy);
                                if ((wall[i].nextsector == goalsect[snum]) && (dist > l))
                                    { l = dist; k = i; }
                            }
                            goalwall[snum] = k;
                            daang = ((getangle(wall[wall[k].point2].x-wall[k].x,wall[wall[k].point2].y-wall[k].y)+1536)&2047);
                            goalx[snum] = ((wall[k].x+wall[wall[k].point2].x)>>1)+(sintable[(daang+512)&2047]>>8);
                            goaly[snum] = ((wall[k].y+wall[wall[k].point2].y)>>1)+(sintable[daang&2047]>>8);
                            goalz[snum] = sector[goalsect[snum]].floorz-(32<<8);
                            break;
                        }
                    }
                }
            if (goalwall[snum] >= 0) break;
            #endif
        }
    }

    if ((goalsect[snum] < 0) || (goalwall[snum] < 0))
    {
        if (goalsprite[snum] < 0)
        {
            for(k=0;k<4;k++)
            {
                i = (rand()%numsectors);
                for(j=headspritesect[i];j>=0;j=nextspritesect[j])
                {
                    if ((sprite[j].xrepeat <= 0) || (sprite[j].yrepeat <= 0)) continue;
                    if (getspritescore(snum,sprite[j].picnum) <= 0) continue;
                        if (FAFcansee(x1,y1,z1-(32<<8),damysect,sprite[j].x,sprite[j].y,sprite[j].z-(4<<8),i))
                        { goalx[snum] = sprite[j].x; goaly[snum] = sprite[j].y; goalz[snum] = sprite[j].z; goalsprite[snum] = j; break; }
                }
            }
        }
        x2 = goalx[snum];
        y2 = goaly[snum];
        dist = ksqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (!dist) return;
        daang = getangle(x2-x1,y2-y1);
        syn->vel += (x2-x1)*2047/dist;
        syn->svel += (y2-y1)*2047/dist;
        syn->angvel = min(max((((daang+1024-damyang)&2047)-1024)>>3,-MAXANGVEL),MAXANGVEL);
    }
    else
        goalsprite[snum] = -1;
#endif

    x3 = p->posx; y3 = p->posy; z3 = p->posz; dasect = p->cursectnum;
    i = clipmove(&x3,&y3,&z3,&dasect,p->xvect,p->yvect,164L,4L<<8,4L<<8,CLIPMASK0);
    if (!i)
    {
        x3 = p->posx; y3 = p->posy; z3 = p->posz+(24<<8); dasect = p->cursectnum;
        i = clipmove(&x3,&y3,&z3,&dasect,p->xvect,p->yvect,164L,4L<<8,4L<<8,CLIPMASK0);
    }
    if (i)
    {
        clipmovecount[snum]++;

        j = 0;
        if ((i&0xc000) == 32768)  //Hit a wall (49152 for sprite)
            if (wall[i&(MAXWALLS-1)].nextsector >= 0)
            {
                if (getflorzofslope(wall[i&(MAXWALLS-1)].nextsector,p->posx,p->posy) <= p->posz+(24<<8)) j |= 1;
                if (getceilzofslope(wall[i&(MAXWALLS-1)].nextsector,p->posx,p->posy) >= p->posz-(24<<8)) j |= 2;
            }
        if ((i&0xc000) == 49152) j = 1;
        // Jump
        if (j&1) if (clipmovecount[snum] == 4) syn->bits |= (1<<SK_JUMP);
        // Crawl
        if (j&2) syn->bits |= (1<<SK_CRAWL);

            //Strafe attack
        daang = getangle(x2-x1,y2-y1);
        if ((i&0xc000) == 32768)
            daang = getangle(wall[wall[i&(MAXWALLS-1)].point2].x-wall[i&(MAXWALLS-1)].x,wall[wall[i&(MAXWALLS-1)].point2].y-wall[i&(MAXWALLS-1)].y);
        j = totalclock+snum*13468;
        i = sintable[(j<<6)&2047];
        i += sintable[((j+4245)<<5)&2047];
        i += sintable[((j+6745)<<4)&2047];
        i += sintable[((j+15685)<<3)&2047];
        syn->vel += ((sintable[(daang+1024)&2047]*i)>>17);
        syn->svel += ((sintable[(daang+512)&2047]*i)>>17);

        // Try to Open
        if ((clipmovecount[snum]&31) == 2) syn->bits |= (1<<SK_OPERATE);
        // *TODO: In Duke, this is Kick, but I need to select sword then fire in SW 
//        if ((clipmovecount[snum]&31) == 17) syn->bits |= (1<<22);
        if (clipmovecount[snum] > 32) { goalsect[snum] = -1; goalwall[snum] = -1; clipmovecount[snum] = 0; }

        goalsprite[snum] = -1;
    }
    else
        clipmovecount[snum] = 0;

    if ((goalsect[snum] >= 0) && (goalwall[snum] >= 0))
    {
        x2 = goalx[snum];
        y2 = goaly[snum];
        dist = ksqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (!dist) return;
        daang = getangle(x2-x1,y2-y1);
        if ((goalwall[snum] >= 0) && (dist < 4096))
            daang = ((getangle(wall[wall[goalwall[snum]].point2].x-wall[goalwall[snum]].x,wall[wall[goalwall[snum]].point2].y-wall[goalwall[snum]].y)+1536)&2047);
        syn->vel += (x2-x1)*2047/dist;
        syn->svel += (y2-y1)*2047/dist;
        syn->angvel = min(max((((daang+1024-damyang)&2047)-1024)>>3,-MAXANGVEL),MAXANGVEL);
    }


/*    
    // Use extended bot logic for navigation through level    
    goalsect[snum] = -1;
    goalwall[snum] = -1;
    goalsprite[snum] = -1;

    // Go to a goal place
    if ((goalsect[snum] >= 0) && (goalwall[snum] >= 0))
    {
        x2 = goalx[snum];
        y2 = goaly[snum];
        dist = ksqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); if (!dist) return;
        daang = getangle(x2-x1,y2-y1);
        if ((goalwall[snum] >= 0) && (dist < 4096))
            daang = ((getangle(wall[wall[goalwall[snum]].point2].x-wall[goalwall[snum]].x,wall[wall[goalwall[snum]].point2].y-wall[goalwall[snum]].y)+1536)&2047);
        syn->vel += (x2-x1)*2047/dist;
        syn->svel += (y2-y1)*2047/dist;
        syn->angvel = min(max((((daang+1024-damyang)&2047)-1024)>>3,-MAXANGVEL),MAXANGVEL);
    }
*/    
}

