mirror of
https://github.com/ZDoom/raze-gles.git
synced 2025-01-08 01:10:40 +00:00
eaefc2576c
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.
898 lines
30 KiB
C++
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
|