mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-25 11:10:47 +00:00
899 lines
30 KiB
C++
899 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,char s,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],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 ////////////////////////////////////
|
|
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, 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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];
|
|
char 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;
|
|
short weap;
|
|
//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 = 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)
|
|
{
|
|
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-p->horiz-p->horizoff)*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->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;
|
|
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->angvel = 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->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);
|
|
}
|
|
*/
|
|
}
|
|
|
|
END_SW_NS
|