mirror of
https://github.com/ZDoom/Raze.git
synced 2025-01-11 19:40:46 +00:00
1099 lines
28 KiB
C++
1099 lines
28 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
|
|
*/
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "ns.h"
|
|
// Added Ninja Sliced fix
|
|
// Fixed Ninja sliced dead and rotation
|
|
// Added s_NinjaDieSlicedHack[]
|
|
//
|
|
|
|
#include "build.h"
|
|
|
|
#include "names2.h"
|
|
#include "panel.h"
|
|
#include "misc.h"
|
|
#include "tags.h"
|
|
#include "weapon.h"
|
|
#include "sprite.h"
|
|
#include "gamefuncs.h"
|
|
#include "ai.h"
|
|
|
|
BEGIN_SW_NS
|
|
|
|
extern int jump_grav;
|
|
|
|
extern STATE s_DebrisNinja[];
|
|
extern STATE s_DebrisRat[];
|
|
extern STATE s_DebrisCrab[];
|
|
extern STATE s_DebrisStarFish[];
|
|
extern STATE s_NinjaDieSliced[];
|
|
extern STATE s_NinjaDieSlicedHack[];
|
|
|
|
extern STATE* sg_NinjaGrabThroat[];
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoScaleSprite(DSWActor* actor)
|
|
{
|
|
int scale_value;
|
|
|
|
if (actor->user.scale_speed)
|
|
{
|
|
actor->user.scale_value += actor->user.scale_speed * ACTORMOVETICS;
|
|
|
|
scale_value = actor->user.scale_value >> 8;
|
|
|
|
if (actor->user.scale_speed > 0)
|
|
{
|
|
if (scale_value > actor->user.scale_tgt)
|
|
actor->user.scale_speed = 0;
|
|
else
|
|
actor->spr.scale = DVector2(scale_value * REPEAT_SCALE, scale_value * REPEAT_SCALE);
|
|
}
|
|
else
|
|
{
|
|
if (scale_value < actor->user.scale_tgt)
|
|
actor->user.scale_speed = 0;
|
|
else
|
|
actor->spr.scale = DVector2(scale_value * REPEAT_SCALE, scale_value * REPEAT_SCALE);
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorDie(DSWActor* actor, DSWActor* weapActor, int meansofdeath)
|
|
{
|
|
change_actor_stat(actor, STAT_DEAD_ACTOR);
|
|
actor->user.Flags |= (SPR_DEAD);
|
|
actor->user.Flags &= ~(SPR_FALLING | SPR_JUMPING);
|
|
actor->user.floor_dist = (40);
|
|
|
|
// test for gibable dead bodies
|
|
actor->spr.extra |= (SPRX_BREAKABLE);
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BREAKABLE);
|
|
|
|
if (weapActor == nullptr)
|
|
{
|
|
// killed by one of these non-sprites
|
|
switch (meansofdeath)
|
|
{
|
|
case WPN_NM_LAVA:
|
|
actor->ChangeStateEnd();
|
|
break;
|
|
|
|
case WPN_NM_SECTOR_SQUISH:
|
|
actor->ChangeStateEnd();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (!weapActor->hasU()) return 0;
|
|
|
|
// killed by one of these sprites
|
|
switch (weapActor->user.ID)
|
|
{
|
|
// Coolie actually explodes himself
|
|
// he is the Sprite AND weapon
|
|
case COOLIE_RUN_R0:
|
|
actor->ChangeStateEnd();
|
|
actor->vel.X *= 2;
|
|
actor->clearActionFunc();
|
|
actor->spr.Angles.Yaw += DAngle180;
|
|
break;
|
|
|
|
case NINJA_RUN_R0:
|
|
if (actor->user.ID == NINJA_RUN_R0) // Cut in half!
|
|
{
|
|
if (weapActor->user.WeaponNum != WPN_FIST)
|
|
{
|
|
if (sw_ninjahack)
|
|
SpawnBlood(actor, actor);
|
|
InitPlasmaFountain(weapActor, actor);
|
|
InitPlasmaFountain(weapActor, actor);
|
|
PlaySound(DIGI_NINJAINHALF, actor, v3df_none);
|
|
if (sw_ninjahack)
|
|
ChangeState(actor, &s_NinjaDieSlicedHack[5]);
|
|
else
|
|
ChangeState(actor, &s_NinjaDieSliced[0]);
|
|
}
|
|
else
|
|
{
|
|
if (RandomRange(1000) > 500)
|
|
{
|
|
InitPlasmaFountain(weapActor, actor);
|
|
}
|
|
|
|
actor->ChangeStateEnd();
|
|
actor->clearActionFunc();
|
|
actor->vel.X = 12.5 + RandomRangeF(12.5);
|
|
actor->user.jump_speed = -200 - RandomRange(250);
|
|
DoActorBeginJump(actor);
|
|
actor->spr.Angles.Yaw = weapActor->spr.Angles.Yaw;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// test for gibable dead bodies
|
|
if (RandomRange(1000) > 500)
|
|
actor->spr.cstat |= (CSTAT_SPRITE_YFLIP);
|
|
actor->ChangeStateEnd();
|
|
actor->vel.X = 0;
|
|
actor->user.jump_speed = 0;
|
|
DoActorBeginJump(actor);
|
|
}
|
|
|
|
actor->user.__legacyState.RotNum = 0;
|
|
|
|
actor->clearActionFunc();
|
|
if (!sw_ninjahack)
|
|
actor->spr.Angles.Yaw = weapActor->spr.Angles.Yaw;
|
|
break;
|
|
|
|
case COOLG_RUN_R0:
|
|
case SKEL_RUN_R0:
|
|
case RIPPER_RUN_R0:
|
|
case RIPPER2_RUN_R0:
|
|
case EEL_RUN_R0:
|
|
case STAR1:
|
|
case SUMO_RUN_R0:
|
|
actor->ChangeStateEnd();
|
|
break;
|
|
|
|
case UZI_SMOKE:
|
|
if (RandomRange(1000) > 500)
|
|
actor->spr.cstat |= (CSTAT_SPRITE_YFLIP);
|
|
actor->ChangeStateEnd();
|
|
// Rippers still gotta jump or they fall off walls weird
|
|
if (actor->user.ID == RIPPER_RUN_R0 || actor->user.ID == RIPPER2_RUN_R0)
|
|
{
|
|
actor->vel.X *= 2;
|
|
actor->user.jump_speed = -100 - RandomRange(250);
|
|
DoActorBeginJump(actor);
|
|
}
|
|
else
|
|
{
|
|
actor->vel.X = 0;
|
|
actor->user.jump_speed = -10 - RandomRange(25);
|
|
DoActorBeginJump(actor);
|
|
}
|
|
actor->clearActionFunc();
|
|
// Get angle to player
|
|
actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos - actor->spr.pos.Y).Angle() + DAngle180;
|
|
break;
|
|
|
|
case UZI_SMOKE+1: // Shotgun
|
|
if (RandomRange(1000) > 500)
|
|
actor->spr.cstat |= (CSTAT_SPRITE_YFLIP);
|
|
actor->ChangeStateEnd();
|
|
|
|
// Rippers still gotta jump or they fall off walls weird
|
|
if (actor->user.ID == RIPPER_RUN_R0 || actor->user.ID == RIPPER2_RUN_R0)
|
|
{
|
|
actor->vel.X = (75./16.) + RandomRangeF(6.25);
|
|
actor->user.jump_speed = -100 - RandomRange(150);
|
|
}
|
|
else
|
|
{
|
|
actor->vel.X = 6.25 + RandomRangeF(12.5);
|
|
actor->user.jump_speed = -100 - RandomRange(250);
|
|
}
|
|
DoActorBeginJump(actor);
|
|
actor->clearActionFunc();
|
|
// Get angle to player
|
|
actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos - actor->spr.pos).Angle() + DAngle180;
|
|
break;
|
|
|
|
default:
|
|
switch (actor->user.ID)
|
|
{
|
|
case SKULL_R0:
|
|
case BETTY_R0:
|
|
actor->ChangeStateEnd();
|
|
break;
|
|
|
|
default:
|
|
if (RandomRange(1000) > 700)
|
|
{
|
|
InitPlasmaFountain(weapActor, actor);
|
|
}
|
|
|
|
if (RandomRange(1000) > 500)
|
|
actor->spr.cstat |= (CSTAT_SPRITE_YFLIP);
|
|
actor->ChangeStateEnd();
|
|
actor->clearActionFunc();
|
|
actor->vel.X = 18.75 + RandomRangeF(25);
|
|
actor->user.jump_speed = -300 - RandomRange(350);
|
|
DoActorBeginJump(actor);
|
|
actor->spr.Angles.Yaw = weapActor->spr.Angles.Yaw;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// These are too big to flip upside down
|
|
switch (actor->user.ID)
|
|
{
|
|
case RIPPER2_RUN_R0:
|
|
case COOLIE_RUN_R0:
|
|
case SUMO_RUN_R0:
|
|
case ZILLA_RUN_R0:
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_YFLIP);
|
|
break;
|
|
}
|
|
|
|
actor->user.ID = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void DoDebrisCurrent(DSWActor* actor)
|
|
{
|
|
int nx, ny;
|
|
auto sectp = actor->sector();
|
|
|
|
double spd = sectp->speed / 64.0;
|
|
|
|
auto vect = sectp->angle.ToVector() * spd;
|
|
|
|
Collision ret = move_sprite(actor, DVector3(vect, 0), actor->user.ceiling_dist, actor->user.floor_dist, 0, ACTORMOVETICS);
|
|
|
|
// attempt to move away from wall
|
|
if (ret.type != kHitNone)
|
|
{
|
|
DAngle rang = RandomAngle();
|
|
|
|
vect = (sectp->angle + rang).ToVector() * spd;
|
|
|
|
move_sprite(actor, DVector3(vect, 0), actor->user.ceiling_dist, actor->user.floor_dist, 0, ACTORMOVETICS);
|
|
}
|
|
|
|
actor->spr.pos.Z = actor->user.loz;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorSectorDamage(DSWActor* actor)
|
|
{
|
|
sectortype* sectp = actor->sector();
|
|
|
|
if (actor->user.Health <= 0)
|
|
return false;
|
|
|
|
if (sectp->hasU() && sectp->damage)
|
|
{
|
|
if ((sectp->flags & SECTFU_DAMAGE_ABOVE_SECTOR))
|
|
{
|
|
if ((actor->user.DamageTics -= synctics) < 0)
|
|
{
|
|
actor->user.DamageTics = 60;
|
|
actor->user.Health -= sectp->damage;
|
|
|
|
if (actor->user.Health <= 0)
|
|
{
|
|
UpdateSinglePlayKills(actor);
|
|
DoActorDie(actor, nullptr, WPN_NM_LAVA);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if (ActorZOfBottom(actor) >= sectp->floorz)
|
|
{
|
|
if ((actor->user.DamageTics -= synctics) < 0)
|
|
{
|
|
actor->user.DamageTics = 60;
|
|
actor->user.Health -= sectp->damage;
|
|
|
|
if (actor->user.Health <= 0)
|
|
{
|
|
UpdateSinglePlayKills(actor);
|
|
DoActorDie(actor, nullptr, WPN_NM_LAVA);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// note that most squishing is done in vator.c
|
|
if (actor->user.lo_sectp && actor->user.hi_sectp && abs(actor->user.loz - actor->user.hiz) < (ActorSizeZ(actor) * 0.5))
|
|
{
|
|
actor->user.Health = 0;
|
|
if (SpawnShrap(actor, nullptr, WPN_NM_SECTOR_SQUISH))
|
|
{
|
|
UpdateSinglePlayKills(actor);
|
|
SetSuicide(actor);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(true == false);
|
|
//DoActorDie(actor, nullptr, WPN_NM_SECTOR_SQUISH);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool move_debris(DSWActor* actor, const DVector2& change)
|
|
{
|
|
actor->user.coll = move_sprite(actor, DVector3(change, 0), actor->user.ceiling_dist, actor->user.floor_dist, 0, ACTORMOVETICS);
|
|
return actor->user.coll.type == kHitNone;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// !AIC - Supposed to allow floating of DEBRIS (dead bodies, flotsam, jetsam). Or if water has
|
|
// current move with the current.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorDebris(DSWActor* actor)
|
|
{
|
|
sectortype* sectp = actor->sector();
|
|
|
|
// This was move from DoActorDie so actor's can't be walked through until they are on the floor
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
// Don't let some actors float
|
|
switch (actor->user.ID)
|
|
{
|
|
case HORNET_RUN_R0:
|
|
case BUNNY_RUN_R0:
|
|
KillActor(actor);
|
|
return 0;
|
|
case ZILLA_RUN_R0:
|
|
calcSlope(actor->sector(), actor->spr.pos.X, actor->spr.pos.Y, &actor->user.hiz, &actor->user.loz);
|
|
actor->user.lo_sectp = actor->sector();
|
|
actor->user.hi_sectp = actor->sector();
|
|
actor->user.lowActor = nullptr;
|
|
actor->user.highActor = nullptr;
|
|
break;
|
|
}
|
|
|
|
if ((sectp->extra & SECTFX_SINK))
|
|
{
|
|
if ((sectp->extra & SECTFX_CURRENT))
|
|
{
|
|
DoDebrisCurrent(actor);
|
|
}
|
|
else
|
|
{
|
|
// todo: check correctness
|
|
DVector2 nvec = ACTORMOVETICS * maptoworld * actor->spr.Angles.Yaw.ToVector();
|
|
|
|
if (!move_debris(actor, nvec))
|
|
{
|
|
actor->spr.Angles.Yaw = RandomAngle();
|
|
}
|
|
}
|
|
|
|
if (actor->sector()->hasU() && FixedToInt(actor->sector()->depth_fixed) > 10) // JBF: added null check
|
|
{
|
|
actor->user.WaitTics = (actor->user.WaitTics + (ACTORMOVETICS << 3)) & 1023;
|
|
actor->spr.pos.Z = actor->user.loz - 2 * BobVal(actor->user.WaitTics);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
actor->spr.pos.Z = actor->user.loz;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoFireFly(DSWActor* actor)
|
|
{
|
|
actor->clipdist = 16;
|
|
if (!move_actor(actor, DVector3(actor->spr.Angles.Yaw.ToVector() * (0.25 * ACTORMOVETICS), 0)))
|
|
{
|
|
actor->spr.Angles.Yaw += DAngle180;
|
|
}
|
|
|
|
actor->user.WaitTics = (actor->user.WaitTics + (ACTORMOVETICS << 1)) & 2047;
|
|
|
|
actor->spr.pos.Z = actor->user.pos.Z + 32 * BobVal(actor->user.WaitTics);
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoGenerateSewerDebris(DSWActor* actor)
|
|
{
|
|
static STATE* Debris[] =
|
|
{
|
|
s_DebrisNinja,
|
|
s_DebrisRat,
|
|
s_DebrisCrab,
|
|
s_DebrisStarFish
|
|
};
|
|
|
|
actor->user.Tics -= ACTORMOVETICS;
|
|
|
|
if (actor->user.Tics <= 0)
|
|
{
|
|
actor->user.Tics = actor->user.WaitTics;
|
|
|
|
auto spawned = SpawnActor(STAT_DEAD_ACTOR, 0, Debris[RANDOM_P2(4<<8)>>8], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 12.5);
|
|
|
|
SetOwner(actor, spawned);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// !AIC - Tries to keep actors correctly on the floor. More that a bit messy.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void KeepActorOnFloor(DSWActor* actor)
|
|
{
|
|
sectortype* sectp;
|
|
int depth;
|
|
|
|
sectp = actor->sector();
|
|
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_YFLIP); // If upside down, reset it
|
|
|
|
if (actor->user.Flags & (SPR_JUMPING | SPR_FALLING))
|
|
return;
|
|
|
|
if (actor->user.lo_sectp && actor->user.lo_sectp->hasU())
|
|
depth = FixedToInt(actor->user.lo_sectp->depth_fixed);
|
|
else
|
|
depth = 0;
|
|
|
|
if ((sectp->extra & SECTFX_SINK) &&
|
|
depth > 35 &&
|
|
actor->hasState(NAME_Swim))
|
|
{
|
|
if (actor->user.Flags & (SPR_SWIMMING))
|
|
{
|
|
if (!actor->checkStateGroup(NAME_Run) && !actor->checkStateGroup(NAME_Swim) && !actor->checkStateGroup(NAME_Stand))
|
|
{
|
|
// was swimming but have now stopped
|
|
actor->user.Flags &= ~(SPR_SWIMMING);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_YCENTER);
|
|
actor->spr.pos.Z = actor->user.oz = actor->user.loz;
|
|
return;
|
|
}
|
|
|
|
if (actor->checkStateGroup(NAME_Run))
|
|
{
|
|
actor->setStateGroup(NAME_Swim);
|
|
}
|
|
|
|
// are swimming
|
|
actor->spr.pos.Z = actor->user.oz = actor->user.loz - depth;
|
|
}
|
|
else
|
|
{
|
|
// only start swimming if you are running
|
|
if (actor->checkStateGroup(NAME_Run) || actor->checkStateGroup(NAME_Swim))
|
|
{
|
|
actor->setStateGroup(NAME_Swim);
|
|
actor->spr.pos.Z = actor->user.oz = actor->user.loz - depth;
|
|
actor->user.Flags |= (SPR_SWIMMING);
|
|
actor->spr.cstat |= (CSTAT_SPRITE_YCENTER);
|
|
}
|
|
else
|
|
{
|
|
actor->user.Flags &= ~(SPR_SWIMMING);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_YCENTER);
|
|
actor->spr.pos.Z = actor->user.oz = actor->user.loz;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// NOT in a swimming situation
|
|
actor->user.Flags &= ~(SPR_SWIMMING);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_YCENTER);
|
|
|
|
#if 1
|
|
if (actor->user.Flags & (SPR_MOVED))
|
|
{
|
|
actor->spr.pos.Z = actor->user.oz = actor->user.loz;
|
|
}
|
|
else
|
|
{
|
|
double ceilz, florz;
|
|
Collision ctrash, ftrash;
|
|
FAFgetzrangepoint(actor->spr.pos, actor->sector(),&ceilz, &ctrash, &florz, &ftrash);
|
|
|
|
actor->spr.pos.Z = actor->user.oz = florz;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorBeginSlide(DSWActor* actor, DAngle ang, double vel)
|
|
{
|
|
actor->user.Flags |= (SPR_SLIDING);
|
|
|
|
actor->user.slide_ang = ang;
|
|
actor->user.slide_vel = vel;
|
|
actor->user.slide_dec = 5/16.;
|
|
|
|
//DoActorSlide(actor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// !AIC - Sliding can occur in different directions from movement of the actor.
|
|
// Has its own set of variables
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorSlide(DSWActor* actor)
|
|
{
|
|
auto vec = actor->user.slide_ang.ToVector() * actor->user.slide_vel;
|
|
|
|
if (!move_actor(actor, DVector3(vec, 0)))
|
|
{
|
|
actor->user.Flags &= ~(SPR_SLIDING);
|
|
return false;
|
|
}
|
|
|
|
actor->user.slide_vel -= actor->user.slide_dec * ACTORMOVETICS;
|
|
|
|
if (actor->user.slide_vel < 1.25)
|
|
{
|
|
actor->user.Flags &= ~(SPR_SLIDING);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// !AIC - Actor jumping and falling
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorBeginJump(DSWActor* actor)
|
|
{
|
|
actor->user.Flags |= (SPR_JUMPING);
|
|
actor->user.Flags &= ~(SPR_FALLING);
|
|
|
|
// actor->user.jump_speed = should be set before calling
|
|
|
|
// set up individual actor jump gravity
|
|
actor->user.jump_grav = ACTOR_GRAVITY;
|
|
|
|
// Change sprites state to jumping
|
|
if (actor->user.Flags & (SPR_DEAD))
|
|
actor->setStateGroup(NAME_DeathJump);
|
|
else
|
|
actor->setStateGroup(NAME_Jump);
|
|
|
|
actor->user.__legacyState.StateFallOverride = nullptr;
|
|
|
|
//DO NOT CALL DoActorJump! DoActorStopFall can cause an infinite loop and
|
|
//stack overflow if it is called.
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorJump(DSWActor* actor)
|
|
{
|
|
int jump_adj;
|
|
|
|
// precalculate jump value to adjust jump speed by
|
|
jump_adj = actor->user.jump_grav * ACTORMOVETICS;
|
|
|
|
// adjust jump speed by gravity - if jump speed greater than 0 player
|
|
// have started falling
|
|
if ((actor->user.jump_speed += jump_adj) > 0)
|
|
{
|
|
// Start falling
|
|
DoActorBeginFall(actor);
|
|
return 0;
|
|
}
|
|
|
|
// adjust height by jump speed
|
|
actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR;
|
|
|
|
// if player gets to close the ceiling while jumping
|
|
auto tex = TexMan.GetGameTexture(actor->spr.spritetexture());
|
|
double minh = actor->user.hiz + tex->GetDisplayHeight();
|
|
if (actor->spr.pos.Z < minh)
|
|
{
|
|
// put player at the ceiling
|
|
actor->spr.pos.Z = minh;
|
|
|
|
// reverse your speed to falling
|
|
actor->user.jump_speed = -actor->user.jump_speed;
|
|
|
|
// Change sprites state to falling
|
|
DoActorBeginFall(actor);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorBeginFall(DSWActor* actor)
|
|
{
|
|
actor->user.Flags |= (SPR_FALLING);
|
|
actor->user.Flags &= ~(SPR_JUMPING);
|
|
|
|
actor->user.jump_grav = ACTOR_GRAVITY;
|
|
|
|
// Change sprites state to falling
|
|
if (actor->user.Flags & (SPR_DEAD))
|
|
{
|
|
actor->setStateGroup(NAME_DeathFall);
|
|
}
|
|
else
|
|
actor->setStateGroup(NAME_Fall);
|
|
|
|
if (actor->user.__legacyState.StateFallOverride)
|
|
{
|
|
NewStateGroup(actor, actor->user.__legacyState.StateFallOverride);
|
|
}
|
|
|
|
DoActorFall(actor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorFall(DSWActor* actor)
|
|
{
|
|
// adjust jump speed by gravity
|
|
actor->user.jump_speed += actor->user.jump_grav * ACTORMOVETICS;
|
|
|
|
// adjust player height by jump speed
|
|
actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR;
|
|
|
|
// Stick like glue when you hit the ground
|
|
if (actor->spr.pos.Z > actor->user.loz)
|
|
{
|
|
DoActorStopFall(actor);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorStopFall(DSWActor* actor)
|
|
{
|
|
actor->spr.pos.Z = actor->user.loz;
|
|
|
|
actor->user.Flags &= ~(SPR_FALLING | SPR_JUMPING);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_YFLIP);
|
|
|
|
|
|
// don't stand on face or wall sprites - jump again
|
|
if (actor->user.lowActor && !(actor->user.lowActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR))
|
|
{
|
|
actor->spr.Angles.Yaw += DAngle180 + RandomAngle(DAngle90);
|
|
actor->user.jump_speed = -350;
|
|
|
|
DoActorBeginJump(actor);
|
|
return 0;
|
|
}
|
|
|
|
// Change sprites state to running
|
|
if (actor->user.__legacyState.ActorActionSet)
|
|
{
|
|
if (actor->user.Flags & (SPR_DEAD))
|
|
{
|
|
actor->setStateGroup(NAME_Dead);
|
|
PlaySound(DIGI_ACTORBODYFALL1, actor, v3df_none);
|
|
}
|
|
else
|
|
{
|
|
PlaySound(DIGI_ACTORHITGROUND, actor, v3df_none);
|
|
|
|
actor->setStateGroup(NAME_Run);
|
|
|
|
if ((actor->user.track >= 0) && (actor->user.jump_speed) > 800 && (actor->hasState(NAME_Sit)))
|
|
{
|
|
actor->user.WaitTics = 80;
|
|
actor->setStateGroup(NAME_Sit);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoActorDeathMove(DSWActor* actor)
|
|
{
|
|
if (actor->user.Flags & (SPR_JUMPING | SPR_FALLING))
|
|
{
|
|
if (actor->user.Flags & (SPR_JUMPING))
|
|
DoActorJump(actor);
|
|
else
|
|
DoActorFall(actor);
|
|
}
|
|
|
|
actor->clipdist = 12;
|
|
move_actor(actor, DVector3(actor->spr.Angles.Yaw.ToVector() * actor->vel.X, 0));
|
|
|
|
|
|
// only fall on top of floor sprite or sector
|
|
DoFindGroundPoint(actor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// !AIC - Jumping a falling for shrapnel and other stuff, not actors.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
int DoBeginJump(DSWActor* actor)
|
|
{
|
|
actor->user.Flags |= (SPR_JUMPING);
|
|
actor->user.Flags &= ~(SPR_FALLING);
|
|
|
|
// set up individual actor jump gravity
|
|
actor->user.jump_grav = ACTOR_GRAVITY;
|
|
|
|
DoJump(actor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoJump(DSWActor* actor)
|
|
{
|
|
int jump_adj;
|
|
|
|
// precalculate jump value to adjust jump speed by
|
|
jump_adj = actor->user.jump_grav * ACTORMOVETICS;
|
|
|
|
// adjust jump speed by gravity - if jump speed greater than 0 player
|
|
// have started falling
|
|
if ((actor->user.jump_speed += jump_adj) > 0)
|
|
{
|
|
// Start falling
|
|
DoBeginFall(actor);
|
|
return 0;
|
|
}
|
|
|
|
// adjust height by jump speed
|
|
actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR;
|
|
|
|
// if player gets to close the ceiling while jumping
|
|
auto tex = TexMan.GetGameTexture(actor->spr.spritetexture());
|
|
double minh = actor->user.hiz + tex->GetDisplayHeight();
|
|
if (actor->spr.pos.Z < minh)
|
|
{
|
|
// put player at the ceiling
|
|
actor->spr.pos.Z = minh;
|
|
|
|
// reverse your speed to falling
|
|
actor->user.jump_speed = -actor->user.jump_speed;
|
|
|
|
// Change sprites state to falling
|
|
DoBeginFall(actor);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoBeginFall(DSWActor* actor)
|
|
{
|
|
actor->user.Flags |= (SPR_FALLING);
|
|
actor->user.Flags &= ~(SPR_JUMPING);
|
|
|
|
actor->user.jump_grav = ACTOR_GRAVITY;
|
|
|
|
DoFall(actor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
int DoFall(DSWActor* actor)
|
|
{
|
|
// adjust jump speed by gravity
|
|
actor->user.jump_speed += actor->user.jump_grav * ACTORMOVETICS;
|
|
|
|
// adjust player height by jump speed
|
|
actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR;
|
|
|
|
// Stick like glue when you hit the ground
|
|
if (actor->spr.pos.Z > actor->user.loz - actor->user.floor_dist)
|
|
{
|
|
actor->spr.pos.Z = actor->user.loz - actor->user.floor_dist;
|
|
actor->user.Flags &= ~(SPR_FALLING);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
// helpers
|
|
|
|
void DSWActor::ChangeStateEnd()
|
|
{
|
|
ChangeState(this, user.__legacyState.StateEnd);
|
|
user.__legacyState.RotNum = 0;
|
|
|
|
}
|
|
|
|
Personality* DSWActor::getPersonality()
|
|
{
|
|
return nullptr; // not implemented yet.
|
|
}
|
|
|
|
static STATE** getLegacyState(ACTOR_ACTION_SET* a, FName label, int subl)
|
|
{
|
|
if (label == NAME_Run)
|
|
{
|
|
return a->Run;
|
|
}
|
|
if (label == NAME_Swim)
|
|
{
|
|
return a->Swim;
|
|
}
|
|
if (label == NAME_DeathJump)
|
|
{
|
|
return a->DeathJump;
|
|
}
|
|
if (label == NAME_Jump)
|
|
{
|
|
return a->Jump;
|
|
}
|
|
if (label == NAME_DeathFall)
|
|
{
|
|
return a->DeathFall;
|
|
}
|
|
if (label == NAME_Fall)
|
|
{
|
|
return a->Fall;
|
|
}
|
|
if (label == NAME_Dead)
|
|
{
|
|
return a->Dead;
|
|
}
|
|
if (label == NAME_Sit)
|
|
{
|
|
return a->Sit;
|
|
}
|
|
if (label == NAME_Stand)
|
|
{
|
|
return a->Stand;
|
|
}
|
|
if (label == NAME_Death1)
|
|
{
|
|
return a->Death1;
|
|
}
|
|
if (label == NAME_Death2)
|
|
{
|
|
return a->Death2;
|
|
}
|
|
if (label == NAME_Duck)
|
|
{
|
|
return a->Duck;
|
|
}
|
|
if (label == NAME_Rise)
|
|
{
|
|
return a->Rise;
|
|
}
|
|
if (label == NAME_Fly)
|
|
{
|
|
return a->Fly;
|
|
}
|
|
if (label == NAME_Crawl)
|
|
{
|
|
return a->Crawl;
|
|
}
|
|
if (label == NAME_Pain)
|
|
{
|
|
return a->Pain;
|
|
}
|
|
if (label == NAME_Climb)
|
|
{
|
|
return a->Climb;
|
|
}
|
|
if (label == NAME_Special)
|
|
{
|
|
return a->Special[1]; // special[0] is never used anywhere
|
|
}
|
|
if (label == NAME_CloseAttack)
|
|
{
|
|
return a->CloseAttack[subl];
|
|
}
|
|
if (label == NAME_Attack)
|
|
{
|
|
return a->Attack[subl];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void DSWActor::setStateGroup(FName label, int subl)
|
|
{
|
|
auto a = user.__legacyState.ActorActionSet;
|
|
if (a) NewStateGroup(this, getLegacyState(a, label, subl));
|
|
}
|
|
|
|
bool DSWActor::checkStateGroup(FName label, int subl)
|
|
{
|
|
auto a = user.__legacyState.ActorActionSet;
|
|
if (!a) return false;
|
|
return user.__legacyState.Rot == getLegacyState(a, label, subl);
|
|
}
|
|
|
|
bool DSWActor::hasState(FName label, int subl)
|
|
{
|
|
auto a = user.__legacyState.ActorActionSet;
|
|
if (!a) return false;
|
|
return getLegacyState(a, label, subl) != nullptr;
|
|
}
|
|
|
|
void DSWActor::setActionDecide() { user.ActorActionFunc = AF(DoActorDecide); }
|
|
|
|
void DSWActor::callAction()
|
|
{
|
|
callFunction(user.ActorActionFunc);
|
|
}
|
|
|
|
void DSWActor::callStateAction()
|
|
{
|
|
if (user.__legacyState.State && user.__legacyState.State->Animator)
|
|
callFunction(*user.__legacyState.State->Animator);
|
|
}
|
|
|
|
int DSWActor::callFunction(VMFunction* func)
|
|
{
|
|
int ret = 0;
|
|
if (func)
|
|
{
|
|
VMValue param[] = { this };
|
|
VMReturn r(&ret);
|
|
VMCall(func, param, 1, &r, 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
END_SW_NS
|