raze-gles/source/sw/src/jplayer.cpp
NY00123 eaefc2576c SW: Rename q16avel -> q16angvel and q16horz -> q16aimvel.
While it is understandable that avel and horz came from Duke3D,
having both q16horiz and q16horz in the updated SW_PACKET struct
can be confusing, and the alternative notation is more consistent
with the original struct field names of angvel and aimvel, as well
as the differing uses of the name angvel still present in player.cpp.
2020-05-20 14:43:03 +02:00

898 lines
30 KiB
C++

//-------------------------------------------------------------------------
/*
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
*/
//-------------------------------------------------------------------------
// JPLAYER.C
// Copyright (c) 1996 by Jim Norwood
#include "ns.h"
#include "build.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 "common_game.h"
#include "gamecontrol.h"
#include "trigger.h"
#include "savedef.h"
#include "menus.h"
#include "network.h"
#include "pal.h"
#include "bots.h"
BEGIN_SW_NS
SWBOOL WeaponOK(PLAYERp pp);
#define MAXANGVEL 80
// 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 += tilesiz[ac].x;
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 += tilesiz[ac].x;
t++;
}
return x;
}
int minigametext(int x,int y,const char *t,short dabits)
{
short ac,newx;
char centre;
const char *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 (*t > asc_Space && *t < 127)
{
newx += tilesiz[ac].x;
}
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 (*t > asc_Space && *t < 127)
{
rotatesprite(x<<16,y<<16,65536L,0,ac,-128,17,dabits,0,0,xdim-1,ydim-1);
x += tilesiz[ac].x;
}
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; // tilesiz[ac].x+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; // tilesiz[ac].x+1;
t++;
}
return x;
}
int quotebot, quotebotgoal;
short user_quote_time[MAXUSERQUOTES];
char user_quote[MAXUSERQUOTES][256];
void adduserquote(const char *daquote)
{
int 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)
{
int 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],2+8);
else if (k > 2)
minigametext(320>>1,j,user_quote[i],2+8+1);
else
minigametext(320>>1,j,user_quote[i],2+8+1+32);
}
else
{
// dont fade out
minigametext(320>>1,j,user_quote[i],2+8);
}
j -= 6;
}
}
//////////// Console Message Queue ////////////////////////////////////
int conbot, conbotgoal;
char con_quote[MAXCONQUOTES][256];
void addconquote(const char *daquote)
{
int 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)
{
int i, j;
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;
}
}
}
int getspritescore(/*int snum, */int 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 int 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 int goalx[MAX_SW_PLAYERS_REG], goaly[MAX_SW_PLAYERS_REG], goalz[MAX_SW_PLAYERS_REG];
static int goalsect[MAX_SW_PLAYERS_REG], goalwall[MAX_SW_PLAYERS_REG], goalsprite[MAX_SW_PLAYERS_REG];
static int goalplayer[MAX_SW_PLAYERS_REG], clipmovecount[MAX_SW_PLAYERS_REG];
short searchsect[MAXSECTORS], searchparent[MAXSECTORS];
uint8_t dashow2dsector[(MAXSECTORS+7)>>3];
void computergetinput(int snum, SW_PACKET *syn)
{
int i, j, k, l, x1, y1, z1, x2, y2, z2, dx, dy, nextj;
int dist, daang, zang, fightdist, damyang, damysect;
int startsect, endsect, splc, send, startwall, endwall;
hitdata_t hitinfo;
PLAYERp p;
walltype *wal;
int myx, myy, myz, myang, mycursectnum;
USERp u;
//extern SWBOOL 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 = fix16_to_int(p->q16ang);
mycursectnum = p->cursectnum;
// Reset input bits
syn->vel = 0;
syn->svel = 0;
syn->q16angvel = 0;
syn->q16aimvel = 0;
syn->bits = 0;
x1 = p->posx;
y1 = p->posy;
z1 = p->posz;
damyang = fix16_to_int(p->q16ang);
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)
{
hitinfo.pos.x = sprite[j].x;
hitinfo.pos.y = sprite[j].y;
hitinfo.pos.z = sprite[j].z;
for (l=0; l<=8; l++)
{
if (tmulscale11(hitinfo.pos.x-x1,hitinfo.pos.x-x1,hitinfo.pos.y-y1,hitinfo.pos.y-y1,(hitinfo.pos.z-z1)>>4,(hitinfo.pos.z-z1)>>4) < 3072)
{
dx = sintable[(sprite[j].ang+512)&2047];
dy = sintable[sprite[j].ang&2047];
if ((x1-hitinfo.pos.x)*dy > (y1-hitinfo.pos.y)*dx) i = -k*512; else i = k*512;
syn->vel -= mulscale17(dy,i);
syn->svel += mulscale17(dx,i);
}
if (l < 7)
{
hitinfo.pos.x += (mulscale14(sprite[j].xvel,sintable[(sprite[j].ang+512)&2047])<<2);
hitinfo.pos.y += (mulscale14(sprite[j].xvel,sintable[sprite[j].ang&2047])<<2);
hitinfo.pos.z += (sprite[j].zvel<<2);
}
else
{
hitscan((vec3_t *)&sprite[j],sprite[j].sectnum,
mulscale14(sprite[j].xvel,sintable[(sprite[j].ang+512)&2047]),
mulscale14(sprite[j].xvel,sintable[sprite[j].ang&2047]),
(int)sprite[j].zvel,
&hitinfo,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)
{
vec3_t hit_pos = { x1, y1, z1-PLAYER_HEIGHT };
hitscan(&hit_pos,damysect,sintable[(damyang+512)&2047],sintable[damyang&2047],
(100-fix16_to_int(p->q16horiz)-fix16_to_int(p->q16horizoff))*32,&hitinfo,CLIPMASK1);
if ((hitinfo.pos.x-x1)*(hitinfo.pos.x-x1)+(hitinfo.pos.y-y1)*(hitinfo.pos.y-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));
hitinfo.pos.x = x2+((x1-x2)*fightdist/dist);
hitinfo.pos.y = y2+((y1-y2)*fightdist/dist);
syn->vel += (hitinfo.pos.x-x1)*2047/dist;
syn->svel += (hitinfo.pos.y-y1)*2047/dist;
//Strafe attack
if (fightdist)
{
j = (int32_t) 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->q16angvel = fix16_from_int(min(max((((daang+1024-damyang)&2047)-1024)>>1,-MAXANGVEL),MAXANGVEL)); //was 127
p->q16ang = fix16_from_int(daang);
syn->q16aimvel = fix16_from_int(min(max((zang-fix16_to_int(p->q16horiz))>>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;
hitinfo.pos.x = hitinfo.pos.y = 0;
for (i=startwall; i<endwall; i++)
{
hitinfo.pos.x += wall[i].x;
hitinfo.pos.y += wall[i].y;
}
hitinfo.pos.x /= (endwall-startwall);
hitinfo.pos.y /= (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 ((hitinfo.pos.x-x1)*(wall[i].y-y1) <= (hitinfo.pos.y-y1)*(wall[i].x-x1))
if ((hitinfo.pos.x-x1)*(wall[wall[i].point2].y-y1) >= (hitinfo.pos.y-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->q16angvel = fix16_from_int(min(max((((daang+1024-damyang)&2047)-1024)>>3,-MAXANGVEL),MAXANGVEL));
}
else
goalsprite[snum] = -1;
#endif
hitinfo.pos.x = p->posx; hitinfo.pos.y = p->posy; hitinfo.pos.z = p->posz; hitinfo.sect = p->cursectnum;
i = clipmove(&hitinfo.pos,&hitinfo.sect,p->xvect,p->yvect,164L,4L<<8,4L<<8,CLIPMASK0);
if (!i)
{
hitinfo.pos.x = p->posx; hitinfo.pos.y = p->posy; hitinfo.pos.z = p->posz+(24<<8); hitinfo.sect = p->cursectnum;
i = clipmove(&hitinfo.pos,&hitinfo.sect,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 = (int32_t) 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->q16angvel = fix16_from_int(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->q16angvel = fix16_from_int(min(max((((daang+1024-damyang)&2047)-1024)>>3,-MAXANGVEL),MAXANGVEL));
}
*/
}
END_SW_NS