gzdoom/src/b_move.cpp
Christoph Oelckers 5474e01de8 - removed FraggleScript's 'sectortype' function. This was GZDoom exclusive and never documented so it got no public exposure. Since this is incompatible with the damage related changes, it has no more use.
- major overhaul of the static sector damage system:

* consolidated special based damage, Sector_SetDamage and UDMF properties into one set of damage properties. The parallel handling that could lead to double damage infliction was removed. This also means that damage through sector specials can be retroactively changed through Sector_SetDamage.
* all special cases were turned into flags. The new system can switch between Strife's delayed damage and regular damage, and it can also set whether terrain splashes are used or not. It also has access to the special properties of the end-level type (i.e. switching off god mode and ending the level.)
* the damage related flags are accessible through Sector_ChangeFlags, not the damage functions themselves.
2016-01-06 12:12:47 +01:00

358 lines
8.4 KiB
C++

/********************************
* B_Think.c *
* Description: *
* Movement/Roaming code for *
* the bot's *
*********************************/
#include "doomdef.h"
#include "doomstat.h"
#include "p_local.h"
#include "b_bot.h"
#include "g_game.h"
#include "d_ticcmd.h"
#include "m_random.h"
#include "i_system.h"
#include "p_lnspec.h"
#include "gi.h"
#include "a_keys.h"
#include "d_event.h"
#include "p_enemy.h"
static FRandom pr_botopendoor ("BotOpenDoor");
static FRandom pr_bottrywalk ("BotTryWalk");
static FRandom pr_botnewchasedir ("BotNewChaseDir");
// borrow some tables from p_enemy.cpp
extern dirtype_t opposite[9];
extern dirtype_t diags[4];
//Called while the bot moves after its dest mobj
//which can be a weapon/enemy/item whatever.
void DBot::Roam (ticcmd_t *cmd)
{
int delta;
if (Reachable(dest))
{ // Straight towards it.
angle = R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y);
}
else if (player->mo->movedir < 8) // turn towards movement direction if not there yet
{
angle &= (angle_t)(7<<29);
delta = angle - (player->mo->movedir << 29);
if (delta > 0)
angle -= ANG45;
else if (delta < 0)
angle += ANG45;
}
// chase towards destination.
if (--player->mo->movecount < 0 || !Move (cmd))
{
NewChaseDir (cmd);
}
}
bool DBot::Move (ticcmd_t *cmd)
{
fixed_t tryx, tryy;
bool try_ok;
int good;
if (player->mo->movedir == DI_NODIR)
return false;
if ((unsigned)player->mo->movedir >= 8)
I_Error ("Weird bot movedir!");
tryx = player->mo->x + 8*xspeed[player->mo->movedir];
tryy = player->mo->y + 8*yspeed[player->mo->movedir];
try_ok = bglobal.CleanAhead (player->mo, tryx, tryy, cmd);
if (!try_ok) //Anything blocking that could be opened etc..
{
if (!spechit.Size ())
return false;
player->mo->movedir = DI_NODIR;
good = 0;
line_t *ld;
while (spechit.Pop (ld))
{
bool tryit = true;
if (ld->special == Door_LockedRaise && !P_CheckKeys (player->mo, ld->args[3], false))
tryit = false;
else if (ld->special == Generic_Door && !P_CheckKeys (player->mo, ld->args[4], false))
tryit = false;
if (tryit &&
(P_TestActivateLine (ld, player->mo, 0, SPAC_Use) ||
P_TestActivateLine (ld, player->mo, 0, SPAC_Push)))
{
good |= ld == player->mo->BlockingLine ? 1 : 2;
}
}
if (good && ((pr_botopendoor() >= 203) ^ (good & 1)))
{
cmd->ucmd.buttons |= BT_USE;
cmd->ucmd.forwardmove = FORWARDRUN;
return true;
}
else
return false;
}
else //Move forward.
cmd->ucmd.forwardmove = FORWARDRUN;
return true;
}
bool DBot::TryWalk (ticcmd_t *cmd)
{
if (!Move (cmd))
return false;
player->mo->movecount = pr_bottrywalk() & 60;
return true;
}
void DBot::NewChaseDir (ticcmd_t *cmd)
{
fixed_t deltax;
fixed_t deltay;
dirtype_t d[3];
int tdir;
dirtype_t olddir;
dirtype_t turnaround;
if (!dest)
{
#ifndef BOT_RELEASE_COMPILE
Printf ("Bot tried move without destination\n");
#endif
return;
}
olddir = (dirtype_t)player->mo->movedir;
turnaround = opposite[olddir];
deltax = dest->x - player->mo->x;
deltay = dest->y - player->mo->y;
if (deltax > 10*FRACUNIT)
d[1] = DI_EAST;
else if (deltax < -10*FRACUNIT)
d[1] = DI_WEST;
else
d[1] = DI_NODIR;
if (deltay < -10*FRACUNIT)
d[2] = DI_SOUTH;
else if (deltay > 10*FRACUNIT)
d[2] = DI_NORTH;
else
d[2] = DI_NODIR;
// try direct route
if (d[1] != DI_NODIR && d[2] != DI_NODIR)
{
player->mo->movedir = diags[((deltay<0)<<1)+(deltax>0)];
if (player->mo->movedir != turnaround && TryWalk(cmd))
return;
}
// try other directions
if (pr_botnewchasedir() > 200
|| abs(deltay)>abs(deltax))
{
tdir=d[1];
d[1]=d[2];
d[2]=(dirtype_t)tdir;
}
if (d[1]==turnaround)
d[1]=DI_NODIR;
if (d[2]==turnaround)
d[2]=DI_NODIR;
if (d[1]!=DI_NODIR)
{
player->mo->movedir = d[1];
if (TryWalk (cmd))
return;
}
if (d[2]!=DI_NODIR)
{
player->mo->movedir = d[2];
if (TryWalk(cmd))
return;
}
// there is no direct path to the player,
// so pick another direction.
if (olddir!=DI_NODIR)
{
player->mo->movedir = olddir;
if (TryWalk(cmd))
return;
}
// randomly determine direction of search
if (pr_botnewchasedir()&1)
{
for ( tdir=DI_EAST;
tdir<=DI_SOUTHEAST;
tdir++ )
{
if (tdir!=turnaround)
{
player->mo->movedir = tdir;
if (TryWalk(cmd))
return;
}
}
}
else
{
for ( tdir=DI_SOUTHEAST;
tdir != (DI_EAST-1);
tdir-- )
{
if (tdir!=turnaround)
{
player->mo->movedir = tdir;
if (TryWalk(cmd))
return;
}
}
}
if (turnaround != DI_NODIR)
{
player->mo->movedir = turnaround;
if (TryWalk(cmd))
return;
}
player->mo->movedir = DI_NODIR; // can not move
}
//
// B_CleanAhead
// Check if a place is ok to move towards.
// This is also a traverse function for
// bots pre-rocket fire (preventing suicide)
//
bool FCajunMaster::CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cmd)
{
FCheckPosition tm;
if (!SafeCheckPosition (thing, x, y, tm))
return false; // solid wall or thing
if (!(thing->flags & MF_NOCLIP) )
{
fixed_t maxstep = thing->MaxStepHeight;
if (tm.ceilingz - tm.floorz < thing->height)
return false; // doesn't fit
if (!(thing->flags&MF_MISSILE))
{
if(tm.floorz > (thing->Sector->floorplane.ZatPoint (x, y)+MAXMOVEHEIGHT)) //Too high wall
return false;
//Jumpable
if(tm.floorz>(thing->Sector->floorplane.ZatPoint (x, y)+thing->MaxStepHeight))
cmd->ucmd.buttons |= BT_JUMP;
if ( !(thing->flags & MF_TELEPORT) &&
tm.ceilingz - thing->z < thing->height)
return false; // mobj must lower itself to fit
// jump out of water
// if((thing->eflags & (MF_UNDERWATER|MF_TOUCHWATER))==(MF_UNDERWATER|MF_TOUCHWATER))
// maxstep=37*FRACUNIT;
if ( !(thing->flags & MF_TELEPORT) &&
(tm.floorz - thing->z > maxstep ) )
return false; // too big a step up
if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
&& tm.floorz - tm.dropoffz > thing->MaxDropOffHeight )
return false; // don't stand over a dropoff
}
}
return true;
}
#define OKAYRANGE (5*ANGLE_1) //counts *2, when angle is in range, turning is not executed.
#define MAXTURN (15*ANGLE_1) //Max degrees turned in one tic. Lower is smother but may cause the bot not getting where it should = crash
#define TURNSENS 3 //Higher is smoother but slower turn.
void DBot::TurnToAng ()
{
int maxturn = MAXTURN;
if (player->ReadyWeapon != NULL)
{
if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
{
if (t_roam && !missile)
{ //Keep angle that where when shot where decided.
return;
}
}
if(enemy)
if(!dest) //happens when running after item in combat situations, or normal, prevents weak turns
if(player->ReadyWeapon->ProjectileType == NULL && !(player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON))
if(Check_LOS(enemy, SHOOTFOV+5*ANGLE_1))
maxturn = 3;
}
int distance = angle - player->mo->angle;
if (abs (distance) < OKAYRANGE && !enemy)
return;
distance /= TURNSENS;
if (abs (distance) > maxturn)
distance = distance < 0 ? -maxturn : maxturn;
player->mo->angle += distance;
}
void DBot::Pitch (AActor *target)
{
double aim;
double diff;
diff = target->z - player->mo->z;
aim = atan (diff / (double)P_AproxDistance (player->mo->x - target->x, player->mo->y - target->y));
player->mo->pitch = -(int)(aim * ANGLE_180/M_PI);
}
//Checks if a sector is dangerous.
bool FCajunMaster::IsDangerous (sector_t *sec)
{
return sec->damageamount > 0;
}