raze/source/games/sw/src/track.cpp

3578 lines
100 KiB
C++
Raw Normal View History

//-------------------------------------------------------------------------
/*
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"
#include "build.h"
#include "names2.h"
#include "panel.h"
#include "tags.h"
#include "sector.h"
#include "ai.h"
#include "player.h"
#include "game.h"
#include "interpso.h"
#include "network.h"
#include "sprite.h"
2020-08-05 22:18:45 +00:00
#include "misc.h"
#include "weapon.h"
BEGIN_SW_NS
2021-12-31 14:50:44 +00:00
void DoTrack(SECTOR_OBJECT* sop, short locktics, int *nx, int *ny);
void DoAutoTurretObject(SECTOR_OBJECT* sop);
void DoTornadoObject(SECTOR_OBJECT* sop);
2021-11-02 17:56:26 +00:00
int PickJumpSpeed(DSWActor*, int pix_height);
DSWActor* FindNearSprite(DSWActor, short);
ANIMATOR NinjaJumpActionFunc;
#define ACTOR_STD_JUMP (-384)
int GlobSpeedSO;
// determine if moving down the track will get you closer to the player
2021-12-31 14:13:05 +00:00
short TrackTowardPlayer(DSWActor* actor, TRACK* t, TRACK_POINT* start_point)
{
2021-12-31 14:13:05 +00:00
TRACK_POINT* end_point;
int end_dist, start_dist;
// determine which end of the Track we are starting from
if (start_point == t->TrackPoint)
{
end_point = t->TrackPoint + t->NumPoints - 1;
}
else
{
end_point = t->TrackPoint;
}
end_dist = Distance(end_point->x, end_point->y, actor->spr.pos.X, actor->spr.pos.Y);
start_dist = Distance(start_point->x, start_point->y, actor->spr.pos.X, actor->spr.pos.Y);
if (end_dist < start_dist)
{
return true;
}
return false;
}
2021-12-31 14:13:05 +00:00
short TrackStartCloserThanEnd(DSWActor* actor, TRACK* t, TRACK_POINT* start_point)
{
2021-12-31 14:13:05 +00:00
TRACK_POINT* end_point;
int end_dist, start_dist;
// determine which end of the Track we are starting from
if (start_point == t->TrackPoint)
{
end_point = t->TrackPoint + t->NumPoints - 1;
}
else
{
end_point = t->TrackPoint;
}
end_dist = Distance(end_point->x, end_point->y, actor->spr.pos.X, actor->spr.pos.Y);
start_dist = Distance(start_point->x, start_point->y, actor->spr.pos.X, actor->spr.pos.Y);
if (start_dist < end_dist)
{
return true;
}
return false;
}
/*
!AIC - Looks at endpoints to figure direction of the track and the closest
point to the sprite.
*/
2021-11-25 17:16:56 +00:00
short ActorFindTrack(DSWActor* actor, int8_t player_dir, int track_type, int* track_point_num, int* track_dir)
{
int dist, near_dist = 999999, zdiff;
2021-11-21 20:45:12 +00:00
int i;
2021-11-25 17:16:56 +00:00
short end_point[2] = { 0,0 };
2021-12-31 14:13:05 +00:00
TRACK* t, *near_track = nullptr;
TRACK_POINT* tp, *near_tp = nullptr;
2021-11-05 23:37:16 +00:00
enum
{
TOWARD_PLAYER = 1,
AWAY_FROM_PLAYER = -1
};
// look at all tracks finding the closest endpoint
for (t = &Track[0]; t < &Track[MAX_TRACKS]; t++)
{
tp = t->TrackPoint;
// Skip if high tag is not ONE of the track type we are looking for
if (!(t->ttflags & track_type))
continue;
// Skip if already someone on this track
if ((t->flags & TF_TRACK_OCCUPIED))
{
continue;
}
switch (track_type)
{
case BIT(TT_DUCK_N_SHOOT):
{
2021-12-26 00:30:29 +00:00
if (!actor->user.ActorActionSet->Duck)
return -1;
end_point[1] = 0;
break;
}
// for ladders only look at first track point
case BIT(TT_LADDER):
{
2021-12-26 00:30:29 +00:00
if (!actor->user.ActorActionSet->Climb)
return -1;
end_point[1] = 0;
break;
}
case BIT(TT_JUMP_UP):
case BIT(TT_JUMP_DOWN):
{
2021-12-26 00:30:29 +00:00
if (!actor->user.ActorActionSet->Jump)
return -1;
end_point[1] = 0;
break;
}
case BIT(TT_TRAVERSE):
{
2021-12-26 00:30:29 +00:00
if (!actor->user.ActorActionSet->Crawl || !actor->user.ActorActionSet->Jump)
return -1;
break;
}
// look at end point also
default:
end_point[1] = t->NumPoints - 1;
break;
}
zdiff = Z(16);
// Look at both track end points to see wich is closer
for (i = 0; i < 2; i++)
{
tp = t->TrackPoint + end_point[i];
dist = Distance(tp->x, tp->y, actor->spr.pos.X, actor->spr.pos.Y);
if (dist < 15000 && dist < near_dist)
{
// make sure track start is on approximate z level - skip if
// not
if (labs(actor->spr.pos.Z - tp->z) > zdiff)
{
continue;
}
// determine if the track leads in the direction we want it
// to
if (player_dir == TOWARD_PLAYER)
{
2021-12-26 00:30:29 +00:00
if (!TrackTowardPlayer(actor->user.targetActor, t, tp))
{
continue;
}
}
else if (player_dir == AWAY_FROM_PLAYER)
{
2021-12-26 00:30:29 +00:00
if (TrackTowardPlayer(actor->user.targetActor, t, tp))
{
continue;
}
}
// make sure the start distance is closer than the end
// distance
2021-11-05 23:37:16 +00:00
if (!TrackStartCloserThanEnd(actor, t, tp))
{
continue;
}
near_dist = dist;
near_track = t;
near_tp = tp;
*track_point_num = end_point[i];
*track_dir = i ? -1 : 1;
}
}
}
2021-11-25 17:16:56 +00:00
auto track_sect = &sector[0];
if (near_dist < 15000)
{
// get the sector number of the point
updatesector(near_tp->x, near_tp->y, &track_sect);
// if can see the point, return the track number
if (track_sect && FAFcansee(actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z - Z(16), actor->sector(), near_tp->x, near_tp->y, track_sect->floorz - Z(32), track_sect))
{
2021-05-12 14:50:20 +00:00
return short(near_track - &Track[0]);
}
}
2021-11-25 17:16:56 +00:00
return -1;
}
2021-12-31 14:50:44 +00:00
void NextTrackPoint(SECTOR_OBJECT* sop)
{
sop->point += sop->dir;
if (sop->point > Track[sop->track].NumPoints - 1)
sop->point = 0;
if (sop->point < 0)
sop->point = Track[sop->track].NumPoints - 1;
}
2021-11-05 23:37:16 +00:00
void NextActorTrackPoint(DSWActor* actor)
{
2021-12-26 00:30:29 +00:00
actor->user.point += actor->user.track_dir;
2021-12-26 00:30:29 +00:00
if (actor->user.point > Track[actor->user.track].NumPoints - 1)
actor->user.point = 0;
2021-12-26 00:30:29 +00:00
if (actor->user.point < 0)
actor->user.point = Track[actor->user.track].NumPoints - 1;
}
2021-12-31 14:13:05 +00:00
void TrackAddPoint(TRACK* t, TRACK_POINT* tp, DSWActor* actor)
{
2021-12-31 14:13:05 +00:00
TRACK_POINT* tpoint = (tp + t->NumPoints);
tpoint->x = actor->spr.pos.X;
tpoint->y = actor->spr.pos.Y;
tpoint->z = actor->spr.pos.Z;
tpoint->ang = actor->spr.ang;
tpoint->tag_low = actor->spr.lotag;
tpoint->tag_high = actor->spr.hitag;
t->NumPoints++;
2021-10-30 20:53:24 +00:00
KillActor(actor);
}
2021-11-05 23:20:27 +00:00
DSWActor* TrackClonePoint(DSWActor* actor)
{
auto actorNew = insertActor(actor->sector(), actor->spr.statnum);
2021-12-24 18:41:29 +00:00
actorNew->spr.cstat = 0;
actorNew->spr.extra = 0;
2022-02-01 18:41:58 +00:00
actorNew->set_int_pos(actor->spr.pos);
actorNew->spr.ang = actor->spr.ang;
actorNew->spr.lotag = actor->spr.lotag;
actorNew->spr.hitag = actor->spr.hitag;
2021-11-05 23:20:27 +00:00
return actorNew;
}
void QuickJumpSetup(short stat, short lotag, short type)
{
2021-11-05 23:20:27 +00:00
int ndx;
2021-12-31 14:13:05 +00:00
TRACK_POINT* tp;
TRACK* t;
2021-11-05 23:20:27 +00:00
DSWActor* start_sprite,* end_sprite;
// make short quick jump tracks
2021-11-05 23:20:27 +00:00
SWStatIterator it(stat);
while (auto actor = it.Next())
{
// find an open track
for (ndx = 0; ndx < MAX_TRACKS; ndx++)
{
if (Track[ndx].NumPoints == 0)
break;
}
ASSERT(ndx < MAX_TRACKS);
Track[ndx].SetTrackSize(4);
tp = Track[ndx].TrackPoint;
t = &Track[ndx];
// set track type
t->ttflags |= (BIT(type));
t->flags = 0;
// clone point
2021-11-05 23:20:27 +00:00
end_sprite = TrackClonePoint(actor);
start_sprite = TrackClonePoint(actor);
// add start point
2021-12-25 00:07:58 +00:00
start_sprite->spr.lotag = TRACK_START;
start_sprite->spr.hitag = 0;
TrackAddPoint(t, tp, start_sprite);
// add jump point
2021-12-25 00:07:58 +00:00
actor->spr.pos.X += MulScale(64, bcos(actor->spr.ang), 14);
actor->spr.pos.Y += MulScale(64, bsin(actor->spr.ang), 14);
actor->spr.lotag = lotag;
2021-11-05 23:20:27 +00:00
TrackAddPoint(t, tp, actor);
// add end point
2021-12-25 00:07:58 +00:00
end_sprite->spr.pos.X += MulScale(2048, bcos(end_sprite->spr.ang), 14);
end_sprite->spr.pos.Y += MulScale(2048, bsin(end_sprite->spr.ang), 14);
end_sprite->spr.lotag = TRACK_END;
end_sprite->spr.hitag = 0;
TrackAddPoint(t, tp, end_sprite);
}
}
void QuickScanSetup(short stat, short lotag, short type)
{
2021-11-05 23:20:27 +00:00
int ndx;
2021-12-31 14:13:05 +00:00
TRACK_POINT* tp;
TRACK* t;
2021-11-05 23:20:27 +00:00
DSWActor* start_sprite,* end_sprite;
// make short quick jump tracks
2021-11-05 23:20:27 +00:00
SWStatIterator it(stat);
while (auto actor = it.Next())
{
// find an open track
for (ndx = 0; ndx < MAX_TRACKS; ndx++)
{
if (Track[ndx].NumPoints == 0)
break;
}
ASSERT(ndx < MAX_TRACKS);
// save space for 3 points
Track[ndx].SetTrackSize(4);
ASSERT(Track[ndx].TrackPoint != nullptr);
tp = Track[ndx].TrackPoint;
t = &Track[ndx];
// set track type
t->ttflags |= (BIT(type));
t->flags = 0;
// clone point
2021-11-05 23:20:27 +00:00
end_sprite = TrackClonePoint(actor);
start_sprite = TrackClonePoint(actor);
// add start point
2021-12-25 00:07:58 +00:00
start_sprite->spr.lotag = TRACK_START;
start_sprite->spr.hitag = 0;
start_sprite->spr.pos.X += MulScale(64, -bcos(start_sprite->spr.ang), 14);
start_sprite->spr.pos.Y += MulScale(64, -bsin(start_sprite->spr.ang), 14);
TrackAddPoint(t, tp, start_sprite);
// add jump point
2021-12-25 00:07:58 +00:00
actor->spr.lotag = lotag;
2021-11-05 23:20:27 +00:00
TrackAddPoint(t, tp, actor);
// add end point
2021-12-25 00:07:58 +00:00
end_sprite->spr.pos.X += MulScale(64, bcos(end_sprite->spr.ang), 14);
end_sprite->spr.pos.Y += MulScale(64, bsin(end_sprite->spr.ang), 14);
end_sprite->spr.lotag = TRACK_END;
end_sprite->spr.hitag = 0;
TrackAddPoint(t, tp, end_sprite);
}
}
void QuickExitSetup(short stat, short type)
{
2021-11-05 23:20:27 +00:00
int ndx;
2021-12-31 14:13:05 +00:00
TRACK_POINT* tp;
TRACK* t;
2021-11-05 23:20:27 +00:00
DSWActor* start_sprite,* end_sprite;
2021-11-05 23:20:27 +00:00
SWStatIterator it(stat);
while (auto actor = it.Next())
{
// find an open track
for (ndx = 0; ndx < MAX_TRACKS; ndx++)
{
if (Track[ndx].NumPoints == 0)
break;
}
ASSERT(ndx < MAX_TRACKS);
// save space for 3 points
Track[ndx].SetTrackSize(4);
ASSERT(Track[ndx].TrackPoint != nullptr);
tp = Track[ndx].TrackPoint;
t = &Track[ndx];
// set track type
t->ttflags |= (BIT(type));
t->flags = 0;
// clone point
2021-11-05 23:20:27 +00:00
end_sprite = TrackClonePoint(actor);
start_sprite = TrackClonePoint(actor);
// add start point
2021-12-25 00:07:58 +00:00
start_sprite->spr.lotag = TRACK_START;
start_sprite->spr.hitag = 0;
TrackAddPoint(t, tp, start_sprite);
2021-10-30 20:53:24 +00:00
KillActor(actor);
// add end point
2021-12-25 00:07:58 +00:00
end_sprite->spr.pos.X += MulScale(1024, bcos(end_sprite->spr.ang), 14);
end_sprite->spr.pos.Y += MulScale(1024, bsin(end_sprite->spr.ang), 14);
end_sprite->spr.lotag = TRACK_END;
end_sprite->spr.hitag = 0;
TrackAddPoint(t, tp, end_sprite);
}
}
void QuickLadderSetup(short stat, short lotag, short type)
{
2021-11-05 23:20:27 +00:00
int ndx;
2021-12-31 14:13:05 +00:00
TRACK_POINT* tp;
TRACK* t;
2021-11-05 23:20:27 +00:00
DSWActor* start_sprite,* end_sprite;
2021-11-05 23:20:27 +00:00
SWStatIterator it(stat);
while (auto actor = it.Next())
{
// find an open track
for (ndx = 0; ndx < MAX_TRACKS; ndx++)
{
if (Track[ndx].NumPoints == 0)
break;
}
ASSERT(ndx < MAX_TRACKS);
// save space for 3 points
Track[ndx].SetTrackSize(4);
ASSERT(Track[ndx].TrackPoint != nullptr);
tp = Track[ndx].TrackPoint;
t = &Track[ndx];
// set track type
t->ttflags |= (BIT(type));
t->flags = 0;
// clone point
2021-11-05 23:20:27 +00:00
end_sprite = TrackClonePoint(actor);
start_sprite = TrackClonePoint(actor);
// add start point
2021-12-25 00:07:58 +00:00
start_sprite->spr.lotag = TRACK_START;
start_sprite->spr.hitag = 0;
start_sprite->spr.pos.X += MOVEx(256,start_sprite->spr.ang + 1024);
start_sprite->spr.pos.Y += MOVEy(256,start_sprite->spr.ang + 1024);
TrackAddPoint(t, tp, start_sprite);
// add climb point
2021-12-25 00:07:58 +00:00
actor->spr.lotag = lotag;
2021-11-05 23:20:27 +00:00
TrackAddPoint(t, tp, actor);
// add end point
2021-12-25 00:07:58 +00:00
end_sprite->spr.pos.X += MOVEx(512,end_sprite->spr.ang);
end_sprite->spr.pos.Y += MOVEy(512,end_sprite->spr.ang);
end_sprite->spr.lotag = TRACK_END;
end_sprite->spr.hitag = 0;
TrackAddPoint(t, tp, end_sprite);
}
}
2021-11-05 23:10:04 +00:00
void TrackSetup(void)
{
2021-11-05 23:37:16 +00:00
int ndx;
2021-12-31 14:13:05 +00:00
TRACK_POINT* tp;
TRACK* t;
TRACK_POINT* New;
int size;
// put points on track
for (ndx = 0; ndx < MAX_TRACKS; ndx++)
{
2021-11-05 23:10:04 +00:00
SWStatIterator it(STAT_TRACK + ndx);
if (!it.Next())
{
// for some reason I need at least one record allocated
// can't remember why at this point
2021-12-31 14:13:05 +00:00
Track[ndx].TrackPoint = (TRACK_POINT*)CallocMem(sizeof(TRACK_POINT) * 1, 1);
continue;
}
ASSERT(Track[ndx].TrackPoint == nullptr);
// make the track array rather large. I'll resize it to correct size
// later.
2021-12-31 14:13:05 +00:00
Track[ndx].TrackPoint = (TRACK_POINT*)CallocMem(sizeof(TRACK_POINT) * 500, 1);
ASSERT(Track[ndx].TrackPoint != nullptr);
tp = Track[ndx].TrackPoint;
t = &Track[ndx];
// find the first point and save it
2021-11-05 23:10:04 +00:00
it.Reset(STAT_TRACK + ndx);
while (auto actor = it.Next())
{
if (actor->spr.lotag == TRACK_START)
{
ASSERT(t->NumPoints == 0);
2021-11-05 23:20:27 +00:00
TrackAddPoint(t, tp, actor);
break;
}
}
// didn't find the start point of the track
if (t->NumPoints == 0)
{
2020-10-15 15:45:07 +00:00
int i;
2021-11-05 23:10:04 +00:00
it.Reset(STAT_TRACK + ndx);
auto itActor = it.Next();
2021-12-24 23:45:19 +00:00
Printf("WARNING: Did not find first point of Track Number %d, x %d, y %d\n", ndx, itActor->spr.pos.X, itActor->spr.pos.Y);
2021-11-05 23:10:04 +00:00
it.Reset(STAT_TRACK + ndx);
while (auto actor = it.Next())
{
// neuter the track's sprite list
2021-11-05 23:20:27 +00:00
KillActor(actor);
}
continue;
}
// set up flags for track types
if (tp->tag_low == TRACK_START && tp->tag_high)
t->ttflags |= (BIT(tp->tag_high));
// while there are still sprites on this status list
2021-11-05 23:10:04 +00:00
while (it.Reset(STAT_TRACK + ndx), it.Next())
{
2021-11-05 23:10:04 +00:00
DSWActor* next_actor = nullptr;
int dist, low_dist = 999999;
// find the closest point to the last point
2021-11-05 23:10:04 +00:00
it.Reset(STAT_TRACK + ndx);
while (auto actor = it.Next())
{
dist = Distance((tp + t->NumPoints - 1)->x, (tp + t->NumPoints - 1)->y, actor->spr.pos.X, actor->spr.pos.Y);
if (dist < low_dist)
{
2021-11-05 23:10:04 +00:00
next_actor = actor;
low_dist = dist;
}
}
// save the closest one off and kill it
2021-11-05 23:10:04 +00:00
if (next_actor != nullptr)
{
2021-11-05 23:20:27 +00:00
TrackAddPoint(t, tp, next_actor);
}
}
size = (Track[ndx].NumPoints + 1) * sizeof(TRACK_POINT);
2021-12-31 14:13:05 +00:00
New = (TRACK_POINT*)CallocMem(size, 1);
memcpy(New, Track[ndx].TrackPoint, size);
FreeMem(Track[ndx].TrackPoint);
Track[ndx].TrackPoint = New;
ASSERT(Track[ndx].TrackPoint != nullptr);
}
QuickJumpSetup(STAT_QUICK_JUMP, TRACK_ACTOR_QUICK_JUMP, TT_JUMP_UP);
QuickJumpSetup(STAT_QUICK_JUMP_DOWN, TRACK_ACTOR_QUICK_JUMP_DOWN, TT_JUMP_DOWN);
QuickJumpSetup(STAT_QUICK_SUPER_JUMP, TRACK_ACTOR_QUICK_SUPER_JUMP, TT_SUPER_JUMP_UP);
QuickScanSetup(STAT_QUICK_SCAN, TRACK_ACTOR_QUICK_SCAN, TT_SCAN);
QuickLadderSetup(STAT_QUICK_LADDER, TRACK_ACTOR_CLIMB_LADDER, TT_LADDER);
QuickExitSetup(STAT_QUICK_EXIT, TT_EXIT);
QuickJumpSetup(STAT_QUICK_OPERATE, TRACK_ACTOR_QUICK_OPERATE, TT_OPERATE);
QuickJumpSetup(STAT_QUICK_DUCK, TRACK_ACTOR_QUICK_DUCK, TT_DUCK_N_SHOOT);
QuickJumpSetup(STAT_QUICK_DEFEND, TRACK_ACTOR_QUICK_DEFEND, TT_HIDE_N_SHOOT);
}
DSWActor* FindBoundSprite(int tag)
{
SWStatIterator it(STAT_ST1);
while (auto actor = it.Next())
{
if (actor->spr.hitag == tag)
{
return actor;
}
}
return nullptr;
}
2021-12-31 14:50:44 +00:00
void SectorObjectSetupBounds(SECTOR_OBJECT* sop)
{
int xlow, ylow, xhigh, yhigh;
2021-11-02 18:07:05 +00:00
int startwall, endwall;
int i, k, j;
DSWActor* BoundActor = nullptr;
2020-09-09 18:32:24 +00:00
bool FoundOutsideLoop = false;
bool SectorInBounds;
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
2021-12-26 00:22:41 +00:00
DSWActor* child = sop->sp_child;
2021-11-05 23:37:16 +00:00
static const uint8_t StatList[] =
{
STAT_DEFAULT, STAT_MISC, STAT_ITEM, STAT_TRAP,
STAT_SPAWN_SPOT, STAT_SOUND_SPOT, STAT_WALL_MOVE,
STAT_WALLBLOOD_QUEUE,
STAT_SPRITE_HIT_MATCH,
STAT_AMBIENT,
STAT_DELETE_SPRITE,
STAT_SPAWN_TRIGGER, // spawing monster trigger - for Randy's bullet train.
//STAT_FLOOR_PAN, STAT_CEILING_PAN
};
// search for 2 sprite bounding tags
BoundActor = FindBoundSprite(500 + (int(sop - SectorObject) * 5));
if (BoundActor == nullptr)
{
I_Error("SOP bound sprite with hitag %d not found", 500 + (int(sop - SectorObject) * 5));
}
2021-12-25 00:43:36 +00:00
xlow = BoundActor->spr.pos.X;
ylow = BoundActor->spr.pos.Y;
KillActor(BoundActor);
BoundActor = FindBoundSprite(501 + (int(sop - SectorObject) * 5));
if (BoundActor == nullptr)
{
I_Error("SOP bound sprite with hitag %d not found", 501 + (int(sop - SectorObject) * 5));
}
2021-12-25 00:35:54 +00:00
xhigh = BoundActor->spr.pos.X;
yhigh = BoundActor->spr.pos.Y;
KillActor(BoundActor);
// set radius for explosion checking - based on bounding box
2021-12-26 00:22:41 +00:00
child->user.Radius = ((xhigh - xlow) + (yhigh - ylow)) >> 2;
child->user.Radius -= (child->user.Radius >> 2); // trying to get it a good size
// search for center sprite if it exists
BoundActor = FindBoundSprite(SECT_SO_CENTER);
if (BoundActor)
{
sop->pmid.X = BoundActor->spr.pos.X;
sop->pmid.Y = BoundActor->spr.pos.Y;
sop->pmid.Z = BoundActor->spr.pos.Z;
KillActor(BoundActor);
}
#if 0
// look for players on sector object
2021-12-31 14:59:11 +00:00
PLAYER* pp;
short pnum;
TRAVERSE_CONNECT(pnum)
{
pp = &Player[pnum];
if (pp->posx > xlow && pp->posx < xhigh && pp->posy > ylow && pp->posy < yhigh)
{
pp->RevolveAng = pp->angle.ang;
pp->Revolve.X = pp->pos.X;
pp->Revolve.Y = pp->pos.Y;
pp->RevolveDeltaAng = 0;
pp->Flags |= (PF_PLAYER_RIDING);
pp->sop_riding = sop;
}
}
#endif
// look through all sectors for whole sectors that are IN bounds
for (auto&sec: sector)
{
auto sect = &sec;
SectorInBounds = true;
for(auto& wal : wallsofsector(sect))
{
// all walls have to be in bounds to be in sector object
if (!(wal.wall_int_pos().X > xlow && wal.wall_int_pos().X < xhigh && wal.wall_int_pos().Y > ylow && wal.wall_int_pos().Y < yhigh))
{
SectorInBounds = false;
break;
}
}
if (SectorInBounds)
{
sop->sectp[sop->num_sectors] = sect;
sop->sectp[sop->num_sectors+1] = nullptr;
// all sectors in sector object have this flag set - for colision
// detection and recognition
sect->extra |= SECTFX_SECTOR_OBJECT;
sop->zorig_floor[sop->num_sectors] = sect->floorz;
sop->zorig_ceiling[sop->num_sectors] = sect->ceilingz;
if ((sect->extra & SECTFX_SINK))
2021-11-20 22:20:43 +00:00
sop->zorig_floor[sop->num_sectors] += Z(FixedToInt(sect->depth_fixed));
// lowest and highest floorz's
if (sect->floorz > sop->floor_loz)
sop->floor_loz = sect->floorz;
if (sect->floorz < sop->floor_hiz)
sop->floor_hiz = sect->floorz;
sop->num_sectors++;
}
ASSERT((uint16_t)sop->num_sectors < SIZ(SectorObject[0].sectp));
}
//
// Make sure every sector object has an outer loop tagged - important
//
FoundOutsideLoop = false;
for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
{
// move all walls in sectors
2021-11-24 19:59:19 +00:00
for(auto& wal : wallsofsector(*sectp))
{
// for morph point - tornado style
2021-11-24 19:59:19 +00:00
if (wal.lotag == TAG_WALL_ALIGN_SLOPE_TO_POINT)
2021-11-25 17:16:56 +00:00
sop->morph_wall_point = &wal;
if (wal.extra && (wal.extra & WALLFX_LOOP_OUTER))
FoundOutsideLoop = true;
// each wall has this set - for collision detection
wal.extra |= WALLFX_SECTOR_OBJECT|WALLFX_DONT_STICK;
2021-11-24 19:59:19 +00:00
if (wal.twoSided())
wal.nextWall()->extra |= WALLFX_SECTOR_OBJECT|WALLFX_DONT_STICK;
}
}
if (!FoundOutsideLoop)
{
I_Error("Forgot to tag outer loop for Sector Object #%d", (int)(sop - SectorObject));
}
so_addinterpolation(sop);
for (i = 0; i < (int)SIZ(StatList); i++)
{
SWStatIterator it(StatList[i]);
while (auto itActor = it.Next())
{
2021-12-25 00:07:58 +00:00
if (itActor->spr.pos.X > xlow && itActor->spr.pos.X < xhigh && itActor->spr.pos.Y > ylow && itActor->spr.pos.Y < yhigh)
{
// some delete sprites ride others don't
2021-12-25 00:07:58 +00:00
if (itActor->spr.statnum == STAT_DELETE_SPRITE)
{
2021-12-25 00:02:25 +00:00
if (!TEST_BOOL2(itActor))
continue;
}
if (!itActor->hasU())
2021-12-26 00:22:41 +00:00
SpawnUser(itActor, 0, nullptr);
2021-12-26 00:22:41 +00:00
itActor->user.RotNum = 0;
itActor->backuppos();
2021-12-30 17:21:02 +00:00
itActor->user.oz = itActor->opos.Z;
2021-12-25 00:07:58 +00:00
switch (itActor->spr.statnum)
{
case STAT_WALL_MOVE:
break;
case STAT_DEFAULT:
2021-12-25 00:07:58 +00:00
switch (itActor->spr.hitag)
{
case SO_CLIP_BOX:
{
short ang2;
sop->clipdist = 0;
2021-12-25 00:07:58 +00:00
sop->clipbox_dist[sop->clipbox_num] = itActor->spr.lotag;
sop->clipbox_xoff[sop->clipbox_num] = sop->pmid.X - itActor->spr.pos.X;
sop->clipbox_yoff[sop->clipbox_num] = sop->pmid.Y - itActor->spr.pos.Y;
sop->clipbox_vdist[sop->clipbox_num] = ksqrt(SQ(sop->pmid.X - itActor->spr.pos.X) + SQ(sop->pmid.Y - itActor->spr.pos.Y));
ang2 = getangle(itActor->spr.pos.X - sop->pmid.X, itActor->spr.pos.Y - sop->pmid.Y);
sop->clipbox_ang[sop->clipbox_num] = getincangle(ang2, sop->ang);
sop->clipbox_num++;
KillActor(itActor);
goto cont;
}
case SO_SHOOT_POINT:
ClearOwner(itActor);
change_actor_stat(itActor, STAT_SO_SHOOT_POINT);
2021-12-27 18:07:39 +00:00
itActor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
break;
default:
break;
}
break;
}
itActor->user.pos.X = sop->pmid.X - itActor->spr.pos.X;
itActor->user.pos.Y = sop->pmid.Y - itActor->spr.pos.Y;
itActor->user.pos.Z = sop->mid_sector->floorz - itActor->spr.pos.Z;
itActor->user.Flags |= (SPR_SO_ATTACHED);
2021-12-26 00:22:41 +00:00
itActor->user.sang = itActor->spr.ang;
itActor->user.spal = itActor->spr.pal;
// search SO's sectors to make sure that it is not on a
// sector
// place all sprites on list
int sn;
2021-11-02 17:45:21 +00:00
for (sn = 0; sn < (int)SIZ(sop->so_actors); sn++)
{
2021-11-02 17:45:21 +00:00
if (sop->so_actors[sn] == nullptr)
break;
}
2021-11-02 17:45:21 +00:00
sop->so_actors[sn] = itActor;
so_setspriteinterpolation(sop, itActor);
if (!(sop->flags & SOBJ_SPRITE_OBJ))
{
// determine if sprite is on a SO sector - set flag if
// true
for (j = 0; j < sop->num_sectors; j++)
{
if (sop->sectp[j] == itActor->sector())
{
itActor->user.Flags |= (SPR_ON_SO_SECTOR);
itActor->user.pos.Z = itActor->sector()->floorz - itActor->spr.pos.Z;
break;
}
}
}
}
cont:
continue;
}
}
2021-12-26 00:30:29 +00:00
// for SPRITE OBJECT sprites, set the actor->user.sz value to the difference
// between the zmid and the actor->spr.z
if ((sop->flags & SOBJ_SPRITE_OBJ))
{
int zmid = -9999999;
// choose the lowest sprite for the zmid
2021-11-02 17:45:21 +00:00
for (i = 0; sop->so_actors[i] != nullptr; i++)
{
2021-12-24 23:45:19 +00:00
auto actor = sop->so_actors[i];
2021-12-24 23:45:19 +00:00
if (actor->spr.pos.Z > zmid)
zmid = actor->spr.pos.Z;
}
ASSERT(zmid != -9999999);
sop->pmid.Z = zmid;
2021-11-02 17:45:21 +00:00
for (i = 0; sop->so_actors[i] != nullptr; i++)
{
2021-12-24 23:45:19 +00:00
auto actor = sop->so_actors[i];
actor->user.pos.Z = sop->pmid.Z - actor->spr.pos.Z;
}
}
}
2021-11-24 21:39:01 +00:00
void SetupSectorObject(sectortype* sectp, short tag)
{
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* sop;
2021-10-30 18:00:02 +00:00
int object_num;
short j;
tag -= (TAG_OBJECT_CENTER - 1);
object_num = tag / 5;
sop = &SectorObject[object_num];
// initialize stuff first time through
if (sop->num_sectors == -1)
{
2021-12-31 14:50:44 +00:00
void DoTornadoObject(SECTOR_OBJECT* sop);
void MorphTornado(SECTOR_OBJECT* sop);
void MorphFloor(SECTOR_OBJECT* sop);
void ScaleSectorObject(SECTOR_OBJECT* sop);
void DoAutoTurretObject(SECTOR_OBJECT* sop);
memset(sop->sectp, 0, sizeof(sop->sectp));
2021-11-02 17:45:21 +00:00
memset(sop->so_actors, 0, sizeof(sop->so_actors));
2021-11-25 17:16:56 +00:00
sop->morph_wall_point = nullptr;
sop->op_main_sector = nullptr;
2021-11-24 22:07:02 +00:00
sop->scratch = nullptr; // this is a guard field for sectp, because several loops do not test the end properly.
2021-11-02 17:52:59 +00:00
sop->match_event_actor = nullptr;
sop->crush_z = 0;
sop->drive_angspeed = 0;
sop->drive_angslide = 0;
sop->drive_slide = 0;
sop->drive_speed = 0;
sop->num_sectors = 0;
sop->update = 15000;
sop->flags = 0;
sop->clipbox_num = 0;
sop->bob_amt = 0;
sop->vel_rate = 6;
sop->z_rate = 256;
sop->zdelta = sop->z_tgt = 0;
sop->wait_tics = 0;
sop->spin_speed = 0;
sop->spin_ang = 0;
sop->ang_orig = 0;
sop->clipdist = 1024;
sop->target_dist = 0;
sop->turn_speed = 4;
sop->floor_loz = -9999999;
sop->floor_hiz = 9999999;
sop->player_xoff = sop->player_yoff = 0;
sop->ang_tgt = sop->ang = sop->ang_moving = 0;
2021-11-24 22:07:02 +00:00
sop->op_main_sector = nullptr;
sop->ram_damage = 0;
sop->max_damage = -9999;
sop->scale_type = SO_SCALE_NONE;
sop->scale_dist = 0;
sop->scale_speed = 20;
sop->scale_dist_min = -1024;
sop->scale_dist_max = 1024;
sop->scale_rand_freq = 64>>3;
sop->scale_x_mult = 256;
sop->scale_y_mult = 256;
sop->morph_ang = RANDOM_P2(2048);
sop->morph_z_speed = 20;
sop->morph_speed = 32;
sop->morph_dist_max = 1024;
sop->morph_rand_freq = 64;
sop->morph_dist = 0;
sop->morph_xoff = 0;
sop->morph_yoff = 0;
sop->PreMoveAnimator = nullptr;
sop->PostMoveAnimator = nullptr;
sop->Animator = nullptr;
}
switch (tag % 5)
{
case TAG_OBJECT_CENTER - 500:
2021-11-24 21:49:00 +00:00
sop->mid_sector = sectp;
SectorMidPoint(sectp, &sop->pmid.X, &sop->pmid.Y, &sop->pmid.Z);
sop->dir = 1;
2021-11-24 21:39:01 +00:00
sop->track = sectp->hitag;
// spawn a sprite to make it easier to integrate with sprite routines
2021-11-24 21:39:01 +00:00
auto actorNew = SpawnActor(STAT_SO_SP_CHILD, 0, nullptr, sectp,
sop->pmid.X, sop->pmid.Y, sop->pmid.Z, 0, 0);
2021-11-01 19:07:53 +00:00
sop->sp_child = actorNew;
2021-12-26 00:22:41 +00:00
actorNew->user.sop_parent = sop;
actorNew->user.Flags2 |= (SPR2_SPRITE_FAKE_BLOCK); // for damage test
// check for any ST1 sprites laying on the center sector
2021-11-24 21:39:01 +00:00
SWSectIterator it(sectp);
2021-10-30 18:00:02 +00:00
while (auto actor = it.Next())
{
if (actor->spr.statnum == STAT_ST1)
{
switch (actor->spr.hitag)
{
case SO_SCALE_XY_MULT:
if (SP_TAG5(actor))
sop->scale_x_mult = SP_TAG5(actor);
if (SP_TAG6(actor))
sop->scale_y_mult = SP_TAG6(actor);
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_SCALE_POINT_INFO:
memset(sop->scale_point_dist,0,sizeof(sop->scale_point_dist));
sop->scale_point_base_speed = SP_TAG2(actor);
for (j = 0; j < (int)SIZ(sop->scale_point_speed); j++)
{
sop->scale_point_speed[j] = SP_TAG2(actor);
}
if (SP_TAG4(actor))
sop->scale_point_rand_freq = (uint8_t)SP_TAG4(actor);
else
sop->scale_point_rand_freq = 64;
sop->scale_point_dist_min = -SP_TAG5(actor);
sop->scale_point_dist_max = SP_TAG6(actor);
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_SCALE_INFO:
sop->flags |= (SOBJ_DYNAMIC);
sop->scale_speed = SP_TAG2(actor);
sop->scale_dist_min = -SP_TAG5(actor);
sop->scale_dist_max = SP_TAG6(actor);
sop->scale_type = SP_TAG4(actor);
sop->scale_active_type = SP_TAG7(actor);
if (SP_TAG8(actor))
sop->scale_rand_freq = (uint8_t)SP_TAG8(actor);
else
sop->scale_rand_freq = 64>>3;
if (SP_TAG3(actor) == 0)
sop->scale_dist = sop->scale_dist_min;
else if (SP_TAG3(actor) == 1)
sop->scale_dist = sop->scale_dist_max;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SPAWN_SPOT:
if (actor->spr.clipdist == 3)
{
2021-10-30 18:00:02 +00:00
change_actor_stat(actor, STAT_NO_STATE);
2021-12-26 00:22:41 +00:00
SpawnUser(actor, 0, nullptr);
actor->user.ActorActionFunc = nullptr;
}
break;
case SO_AUTO_TURRET:
sop->Animator = DoAutoTurretObject;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_TORNADO:
if (SW_SHAREWARE) break;
sop->vel = 120;
sop->flags |= (SOBJ_DYNAMIC);
sop->scale_type = SO_SCALE_CYCLE;
// spin stuff
sop->spin_speed = 16;
sop->last_ang = sop->ang;
// animators
sop->Animator = DoTornadoObject;
sop->PreMoveAnimator = ScaleSectorObject;
sop->PostMoveAnimator = MorphTornado;
// clip
sop->clipdist = 2500;
// morph point
sop->morph_speed = 16;
sop->morph_z_speed = 6;
sop->morph_dist_max = 1024;
sop->morph_rand_freq = 8;
sop->scale_dist_min = -768;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_FLOOR_MORPH:
if (SW_SHAREWARE) break;
sop->flags |= (SOBJ_DYNAMIC);
sop->scale_type = SO_SCALE_NONE;
sop->morph_speed = 120;
sop->morph_z_speed = 7;
sop->PostMoveAnimator = MorphFloor;
sop->morph_dist_max = 4000;
sop->morph_rand_freq = 8;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_AMOEBA:
sop->flags |= (SOBJ_DYNAMIC);
//sop->scale_type = SO_SCALE_CYCLE;
sop->scale_type = SO_SCALE_RANDOM_POINT;
sop->PreMoveAnimator = ScaleSectorObject;
memset(sop->scale_point_dist,0,sizeof(sop->scale_point_dist));;
sop->scale_point_base_speed = SCALE_POINT_SPEED;
for (j = 0; j < (int)SIZ(sop->scale_point_speed); j++)
sop->scale_point_speed[j] = SCALE_POINT_SPEED;
sop->scale_point_dist_min = -256;
sop->scale_point_dist_max = 256;
sop->scale_point_rand_freq = 32;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_MAX_DAMAGE:
2021-12-26 00:22:41 +00:00
actorNew->user.MaxHealth = SP_TAG2(actor);
if (SP_TAG5(actor) != 0)
sop->max_damage = SP_TAG5(actor);
else
2021-12-26 00:22:41 +00:00
sop->max_damage = actorNew->user.MaxHealth;
switch (actor->spr.clipdist)
{
case 0:
break;
case 1:
sop->flags |= (SOBJ_DIE_HARD);
break;
}
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_DRIVABLE_ATTRIB:
sop->drive_angspeed = SP_TAG2(actor);
sop->drive_angspeed <<= 5;
2021-12-25 00:02:25 +00:00
sop->drive_angslide = SP_TAG3(actor);
if (sop->drive_angslide <= 0 || sop->drive_angslide == 32)
sop->drive_angslide = 1;
2021-12-25 00:02:25 +00:00
sop->drive_speed = SP_TAG6(actor);
sop->drive_speed <<= 5;
2021-12-25 00:02:25 +00:00
sop->drive_slide = SP_TAG7(actor);
if (sop->drive_slide <= 0)
sop->drive_slide = 1;
2021-12-25 00:02:25 +00:00
if (TEST_BOOL1(actor))
sop->flags |= (SOBJ_NO_QUAKE);
2021-12-25 00:02:25 +00:00
if (TEST_BOOL3(actor))
sop->flags |= (SOBJ_REMOTE_ONLY);
2021-12-25 00:02:25 +00:00
if (TEST_BOOL4(actor))
{
sop->crush_z = actor->spr.pos.Z;
sop->flags |= (SOBJ_RECT_CLIP);
}
2021-10-30 20:53:24 +00:00
//KillActor(actor);
break;
case SO_RAM_DAMAGE:
sop->ram_damage = actor->spr.lotag;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SECT_SO_CLIP_DIST:
sop->clipdist = actor->spr.lotag;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SECT_SO_SPRITE_OBJ:
sop->flags |= (SOBJ_SPRITE_OBJ);
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SECT_SO_DONT_ROTATE:
sop->flags |= (SOBJ_DONT_ROTATE);
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_LIMIT_TURN:
sop->limit_ang_center = actor->spr.ang;
sop->limit_ang_delta = actor->spr.lotag;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_MATCH_EVENT:
sop->match_event = actor->spr.lotag;
2021-11-02 17:52:59 +00:00
sop->match_event_actor = actor;
break;
case SO_SET_SPEED:
sop->vel = actor->spr.lotag * 256;
sop->vel_tgt = sop->vel;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_SPIN:
if (sop->spin_speed)
break;
sop->spin_speed = actor->spr.lotag;
sop->last_ang = sop->ang;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_ANGLE:
sop->ang = sop->ang_moving = actor->spr.ang;
sop->last_ang = sop->ang_orig = sop->ang;
sop->spin_ang = 0;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_SPIN_REVERSE:
sop->spin_speed = actor->spr.lotag;
sop->last_ang = sop->ang;
if (sop->spin_speed >= 0)
sop->spin_speed = -sop->spin_speed;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_BOB_START:
sop->bob_amt = Z(actor->spr.lotag);
sop->bob_sine_ndx = 0;
sop->bob_speed = 4;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_TURN_SPEED:
sop->turn_speed = actor->spr.lotag;
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_SYNC1:
sop->flags |= (SOBJ_SYNC1);
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_SYNC2:
sop->flags |= (SOBJ_SYNC2);
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
case SO_KILLABLE:
sop->flags |= (SOBJ_KILLABLE);
2021-10-30 20:53:24 +00:00
KillActor(actor);
break;
}
}
}
if (sop->vel == -1)
sop->vel = sop->vel_tgt = 8 * 256;
SectorObjectSetupBounds(sop);
if (sop->track >= SO_OPERATE_TRACK_START)
{
switch (sop->track)
{
case SO_TURRET_MGUN:
case SO_TURRET:
case SO_VEHICLE:
sop->vel = 0;
sop->flags |= (SOBJ_OPERATIONAL);
break;
#if 0
case SO_SPEED_BOAT:
sop->vel = 0;
sop->bob_amt = Z(2);
sop->bob_speed = 4;
sop->flags |= (SOBJ_OPERATIONAL);
break;
#endif
default:
sop->flags |= (SOBJ_OPERATIONAL);
break;
}
}
2021-11-24 21:39:01 +00:00
sectp->lotag = 0;
sectp->hitag = 0;
if (sop->max_damage <= 0)
VehicleSetSmoke(sop, SpawnVehicleSmoke);
break;
}
}
2021-11-05 23:37:16 +00:00
void PostSetupSectorObject(void)
{
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* sop;
for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
{
if (SO_EMPTY(sop))
continue;
FindMainSector(sop);
}
}
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* PlayerOnObject(sectortype* match)
{
short i, j;
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* sop;
// place each sector object on the track
//for (i = 0; !SO_EMPTY(&SectorObject[i]) && (i < MAX_SECTOR_OBJECTS); i++)
for (i = 0; (i < MAX_SECTOR_OBJECTS); i++)
{
sop = &SectorObject[i];
if (sop->track < SO_OPERATE_TRACK_START)
continue;
for (j = 0; j < sop->num_sectors; j++)
{
if (sop->sectp[j] == match && (match->extra & SECTFX_OPERATIONAL))
{
return sop;
}
}
}
return nullptr;
}
2021-11-05 23:37:16 +00:00
void PlaceSectorObjectsOnTracks(void)
{
short i, j, k, startwall, endwall;
2020-09-09 18:32:24 +00:00
bool found;
// place each sector object on the track
for (i = 0; i < MAX_SECTOR_OBJECTS; i++)
{
int low_dist = 999999, dist;
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* sop = &SectorObject[i];
2021-12-31 14:13:05 +00:00
TRACK_POINT* tpoint = nullptr;
if (SO_EMPTY(sop))
continue;
// save off the original x and y locations of the walls AND sprites
sop->num_walls = 0;
for (j = 0; sop->sectp[j] != nullptr; j++)
{
// move all walls in sectors
for (auto& wal : wallsofsector(sop->sectp[j]))
{
sop->xorig[sop->num_walls] = sop->pmid.X - wal.wall_int_pos().X;
sop->yorig[sop->num_walls] = sop->pmid.Y - wal.wall_int_pos().Y;
sop->num_walls++;
}
}
ASSERT((uint16_t)sop->num_walls < SIZ(sop->xorig));
if (sop->track <= -1)
continue;
if (sop->track >= SO_OPERATE_TRACK_START)
continue;
found = false;
// find the closest point on the track and put SOBJ on it
for (j = 0; j < Track[sop->track].NumPoints; j++)
{
tpoint = Track[sop->track].TrackPoint;
dist = Distance((tpoint + j)->x, (tpoint + j)->y, sop->pmid.X, sop->pmid.Y);
if (dist < low_dist)
{
low_dist = dist;
sop->point = j;
found = true;
}
}
if (!found)
{
sop->track = -1;
continue;
}
NextTrackPoint(sop);
sop->ang = getangle((tpoint + sop->point)->x - sop->pmid.X, (tpoint + sop->point)->y - sop->pmid.Y);
sop->ang_moving = sop->ang_tgt = sop->ang;
}
}
2021-11-05 23:37:16 +00:00
void PlaceActorsOnTracks(void)
{
2021-11-05 23:37:16 +00:00
short j, tag;
2021-12-31 14:13:05 +00:00
TRACK_POINT* tpoint = nullptr;
// place each actor on the track
2021-11-05 23:37:16 +00:00
SWStatIterator it(STAT_ENEMY);
while (auto actor = it.Next())
{
int low_dist = 999999, dist;
tag = actor->spr.lotag;
if (tag < TAG_ACTOR_TRACK_BEGIN || tag > TAG_ACTOR_TRACK_END)
continue;
// setup sprite track defaults
2021-12-26 00:30:29 +00:00
actor->user.track = tag - TAG_ACTOR_TRACK_BEGIN;
// if facing left go backward
if (actor->spr.ang >= 513 && actor->spr.ang <= 1535)
{
2021-12-26 00:30:29 +00:00
actor->user.track_dir = -1;
}
else
{
2021-12-26 00:30:29 +00:00
actor->user.track_dir = 1;
}
2021-12-26 00:30:29 +00:00
actor->user.track_vel = actor->spr.xvel * 256;
actor->user.vel_tgt = actor->user.track_vel;
actor->user.vel_rate = 6;
// find the closest point on the track and put SOBJ on it
2021-12-26 00:30:29 +00:00
for (j = 0; j < Track[actor->user.track].NumPoints; j++)
{
2021-12-26 00:30:29 +00:00
tpoint = Track[actor->user.track].TrackPoint;
dist = Distance((tpoint + j)->x, (tpoint + j)->y, actor->spr.pos.X, actor->spr.pos.Y);
if (dist < low_dist)
{
low_dist = dist;
2021-12-26 00:30:29 +00:00
actor->user.point = j;
}
}
2021-11-05 23:37:16 +00:00
NextActorTrackPoint(actor);
2021-12-26 00:30:29 +00:00
if (Track[actor->user.track].NumPoints == 0)
{
2021-12-26 00:30:29 +00:00
Printf("WARNING: Sprite %d (%d, %d) placed on track %d with no points!\n", actor->GetIndex(), actor->spr.pos.X, actor->spr.pos.Y, actor->user.track);
continue;
}
// check angle in the "forward" direction
2021-12-26 00:30:29 +00:00
actor->spr.ang = getangle((tpoint + actor->user.point)->x - actor->spr.pos.X, (tpoint + actor->user.point)->y - actor->spr.pos.Y);
}
}
2021-12-31 14:59:11 +00:00
void MovePlayer(PLAYER* pp, SECTOR_OBJECT* sop, int nx, int ny)
{
2021-12-31 14:59:11 +00:00
void DoPlayerZrange(PLAYER* pp);
// make sure your standing on the so
2021-12-27 18:34:06 +00:00
if (pp->Flags & (PF_JUMPING | PF_FALLING | PF_FLYING))
return;
pp->sop_riding = sop;
// if player has NOT moved and player is NOT riding
// set up the player for riding
if (!(pp->Flags & PF_PLAYER_MOVED) && !(pp->Flags & PF_PLAYER_RIDING))
{
pp->Flags |= (PF_PLAYER_RIDING);
pp->RevolveAng = pp->angle.ang;
pp->Revolve.X = pp->pos.X;
pp->Revolve.Y = pp->pos.Y;
// set the delta angle to 0 when moving
pp->RevolveDeltaAng = 0;
}
pp->pos.X += nx;
pp->pos.Y += ny;
if ((sop->flags & SOBJ_DONT_ROTATE))
{
UpdatePlayerSprite(pp);
return;
}
2021-12-27 18:34:06 +00:00
if (pp->Flags & (PF_PLAYER_MOVED))
{
// Player is moving
// save the current information so when Player stops
// moving then you
// know where he was last
pp->RevolveAng = pp->angle.ang;
pp->Revolve.X = pp->pos.X;
pp->Revolve.Y = pp->pos.Y;
// set the delta angle to 0 when moving
pp->RevolveDeltaAng = 0;
}
else
{
// Player is NOT moving
// Move saved x&y variables
pp->Revolve.X += nx;
pp->Revolve.Y += ny;
// Last known angle is now adjusted by the delta angle
pp->RevolveAng = pp->angle.ang - buildang(pp->RevolveDeltaAng);
}
// increment Players delta angle
Revert "SW: Improve main game loop." This reverts commit e878c5bab8bc15e00e7a999a018f59ba4980505c. Revert "SW: Use Q16.16 for horiz." This reverts commit f07a0ae01ef8340c4b32c2b110bcb1431ae93c4f. Revert "SW: Use Q16.16 for angle." This reverts commit 1ecc74c2ecd8bb310c0099249ce148a28a73e26a. Revert "SW: Minor repairs for Q16.16 implementation." This reverts commit d78d046bad6c1679fff32efaa98bc6b4478d20eb. Revert "SW: Process input at frame rate." This reverts commit c162014dab5433dff2c5c3e5397178c992e1e761. Revert "SW: Amendments to accommodate changes in master." This reverts commit eaa51138add0d83568fafb79bc9afa35fe67a64f. Revert "SW: Fix incorrectly declared function input type." This reverts commit 1cdd5b08d82a88eb57013e78a34e4ef9e0796d34. Revert "SW: Amend scaleAdjustmentToInterval() with correct value for SW." This reverts commit d4dd737cd54e5c30c41a339fba6cefad696f3a42. Revert "SW: Refinements to new input code." This reverts commit 5ebc65a1fb19f86c2add664470108002a0078945. Revert "SW: Adjust look and snap up/down keys and slightly tune PLAYER_TURN_AMOUNT." This reverts commit 2852536dbf50f4cab58a158083b8c6e2de6ac1dd. Revert "SW: Get PLAYER_TURN_SCALE to be just right." This reverts commit 4630c8a0b7a24819e04a14a64efff5d4b36271cf. Revert "SW: Make map follow mode work better." This reverts commit 8e94c48eff599078364a7b0a7b94750cd40daf28. Revert "SW: Remove line accidentally left from 'MoveScrollMode2D()'." This reverts commit 5db8047b413548d3ef02d08d1d581c303a3d5907. Revert "Fix multiplayer desync after the change to q16 angle and horiz." This reverts commit 3bc46078b833e27fab42eaa77a0f5071c35dd584. Revert "SW: Revert commented out horiz->q16horiz renames in DSPRINTF strings" This reverts commit 537313f620dc98a832d862aff86a78e0b3b3cb23. Revert "sw/src/draw.cpp:drawscreen: We can set the pp->si* fields just once," This reverts commit d2e9595980c283d62de28f029b5f6394bf2522ff. Revert "sw/src/game.cpp:LoadLevel: Rename q16ang -> ang" This reverts commit a178961a3eed2522147f60d7265bb2d08f4a0bfe. Revert "SW: Minor tweaks." This reverts commit 377ba68344e34495638c6fa7685ff78c9a0ed6f8. Revert "SW: Further refine turning and optimise horizon adjustment." This reverts commit 039022d9ac476cc784e47633bfbac039a965f217. Revert "SW: Don't process input at frame rate if ScrollMode2D is true." This reverts commit 1aa1e62c4d4dc1fff24201ff8eaebcccef6d7646. Revert "SW: Use a bit more Q16.16 in places." This reverts commit 40ca656f38de5d941cf5deac440eeafa965df3bd. Revert "SW: Use the old interpolation path in drawscreen if player is dead" This reverts commit 2d734664259e47b999fe989b924ecaa0a97bb109. Revert "SW: Smooth out 180 degree turn landing and replace some fix16_min/max with fix16_clamp." This reverts commit 0996e87f7991490d70fba9e5b525e7c7404ce637. Revert "Change Next/Previous Weapon button handling for Shadow Warrior." This reverts commit f6b8ca6a223923b0ec5665aedacd729391ea1d4d. Revert "SW: Make "Center_View" key return smoothly." This reverts commit 23c401fbc28845a7ecb1ad9b29d0e72781b052b5. Revert "SW: Use the old interpolation path in drawscreen if player is dead" This reverts commit 43ec16eb5576124bed3699943bb70b9ea943bd93. Revert "Interpolation fixes for SW:" This reverts commit ac8a7ecfbdfbe9152a8ead605723c1d18eca7798. Revert "SW: Reset the number of interpolations on level load" This reverts commit 04bf8499e72fd48e1e6e7487d743b38a2a206a14. Revert "Another change modifying saved game format in SW:" This reverts commit e80888523ea62df60c69b098236f6af1f994177a. Revert "SW: Interpolate sector objects in non-demo, single player games." This reverts commit 996ab77cf47b5eeb142d6c93e7b307418f3b53a6. Revert "- fixed merge errors in SW." This reverts commit b8cfa94568b85e4ed51c558f09734ede794ad988. Revert "- fix interpolation stutters when opening console for SW." This reverts commit 99fdbfb6cb62bd02d91f4b2d95339cbd918c71fa. Revert "- reset buttonMap button states after returning from pause for SW (stops keys acting stuck down if down prior to pausing)." This reverts commit 693b6955dac84b13fc5de91aee29364c3be6e526. Revert "SW: fix stupid input scaling bug" This reverts commit 1c79e6e17c0028e650f525db6cdbd1b38095708e. Revert "SW: Make vehicle input better." This reverts commit 670a53c402f08ac0aecd721f8c5cf1543f699250. Revert "SW: Change fix16_from_float() to fix16_from_int() that was changed in 4630c8a0b7a24819e04a14a64efff5d4b36271cf but should have been reverted in 377ba68344e34495638c6fa7685ff78c9a0ed6f8." This reverts commit 423c9da071844bdfb716d39e1cd2cf103727a37c. Revert "SW: Remove ScrollMode2D extern boolean and move into PLAYERp struct." This reverts commit 31eb55c1fa69bfe90c09ce9611e0d32445341284.
2020-05-19 11:44:52 +00:00
pp->RevolveDeltaAng = NORM_ANGLE(pp->RevolveDeltaAng + GlobSpeedSO);
rotatepoint(sop->pmid.vec2, *(vec2_t *)&pp->Revolve.X, pp->RevolveDeltaAng, &pp->pos.vec2);
// THIS WAS CAUSING PROLEMS!!!!
// Sectors are still being manipulated so you can end up in a void (-1) sector
// New angle is formed by taking last known angle and
// adjusting by the delta angle
pp->angle.addadjustment(pp->angle.ang - (pp->RevolveAng + buildang(pp->RevolveDeltaAng)));
UpdatePlayerSprite(pp);
}
2021-12-31 14:50:44 +00:00
void MovePoints(SECTOR_OBJECT* sop, short delta_ang, int nx, int ny)
{
2021-11-24 21:04:11 +00:00
int j;
vec2_t rxy;
2021-11-24 21:04:11 +00:00
int pnum;
2021-12-31 14:59:11 +00:00
PLAYER* pp;
2022-02-01 18:41:58 +00:00
sectortype** sectp;
2021-11-24 21:04:11 +00:00
int i, rot_ang;
2020-09-09 18:32:24 +00:00
bool PlayerMove = true;
if (sop->pmid.X >= MAXSO)
PlayerMove = false;
// move along little midpoint
sop->pmid.X += nx;
sop->pmid.Y += ny;
if (sop->pmid.X >= MAXSO)
PlayerMove = false;
// move child sprite along also
2022-02-01 18:41:58 +00:00
sop->sp_child->set_int_xy(sop->pmid.X, sop->pmid.Y);
// setting floorz if need be
if ((sop->flags & SOBJ_ZMID_FLOOR))
sop->pmid.Z = sop->mid_sector->floorz;
DVector2 pivot = { sop->pmid.X * inttoworld, sop->pmid.Y * inttoworld };
DVector2 move = { nx * inttoworld, ny * inttoworld };
for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
{
if ((sop->flags & (SOBJ_SPRITE_OBJ | SOBJ_DONT_ROTATE)))
goto PlayerPart;
// move all walls in sectors
2021-11-24 21:04:11 +00:00
for(auto& wal : wallsofsector(*sectp))
{
if ((wal.extra & (WALLFX_LOOP_DONT_SPIN | WALLFX_DONT_MOVE)))
continue;
if (wal.extra && (wal.extra & WALLFX_LOOP_OUTER))
{
dragpoint(&wal, wal.pos + move);
}
else
{
wal.move(wal.pos + move);
}
rot_ang = delta_ang;
if ((wal.extra & WALLFX_LOOP_REVERSE_SPIN))
rot_ang = -delta_ang;
if ((wal.extra & WALLFX_LOOP_SPIN_2X))
rot_ang = NORM_ANGLE(rot_ang * 2);
if ((wal.extra & WALLFX_LOOP_SPIN_4X))
rot_ang = NORM_ANGLE(rot_ang * 4);
auto vec = rotatepoint(pivot, wal.pos, buildang(rot_ang));
if (wal.extra && (wal.extra & WALLFX_LOOP_OUTER))
{
dragpoint(&wal, vec);
}
else
{
wal.move(vec);
}
}
PlayerPart:
TRAVERSE_CONNECT(pnum)
{
pp = Player + pnum;
// if controlling a sector object
if (pp->sop)
continue;
if (!pp->lo_sectp)
continue;
if ((pp->lo_sectp->extra & SECTFX_NO_RIDE))
{
continue;
}
// move the player
if (pp->lo_sectp == sop->sectp[j])
{
if (PlayerMove)
MovePlayer(pp, sop, nx, ny);
}
}
}
2021-11-02 17:45:21 +00:00
for (i = 0; sop->so_actors[i] != nullptr; i++)
{
2021-12-07 18:22:30 +00:00
DSWActor* actor = sop->so_actors[i];
if (!actor) continue;
// if its a player sprite || NOT attached
if (!actor->hasU() || actor->user.PlayerP || !(actor->user.Flags & SPR_SO_ATTACHED))
continue;
// move the player
TRAVERSE_CONNECT(pnum)
{
pp = Player + pnum;
if (pp->lowActor && pp->lowActor == actor)
{
if (PlayerMove)
MovePlayer(pp, sop, nx, ny);
}
}
2022-02-01 18:41:58 +00:00
actor->set_int_xy(sop->pmid.X - actor->user.pos.X, sop->pmid.Y - actor->user.pos.Y);
// sprites z update
if ((sop->flags & SOBJ_SPRITE_OBJ))
{
// Sprite Objects follow zmid
actor->spr.pos.Z = sop->pmid.Z - actor->user.pos.Z;
}
else
{
// Sector Objects can either have sprites ON or OFF of the sector
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_ON_SO_SECTOR))
{
// move with sector its on
actor->spr.pos.Z = actor->sector()->floorz - actor->user.pos.Z;
}
else
{
// move with the mid sector
actor->spr.pos.Z = sop->mid_sector->floorz - actor->user.pos.Z;
}
}
int16_t oldang = actor->spr.ang;
2021-12-26 00:30:29 +00:00
actor->spr.ang = actor->user.sang;
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_ON_SO_SECTOR))
{
if ((sop->flags & SOBJ_DONT_ROTATE))
continue;
// IS part of a sector, sprite can do things based on the
// current sector it is in
if ((actor->sector()->firstWall()->extra & WALLFX_LOOP_DONT_SPIN))
continue;
if ((actor->sector()->firstWall()->extra & WALLFX_LOOP_REVERSE_SPIN))
{
rotatepoint(sop->pmid.vec2, actor->spr.pos.vec2, -delta_ang, &actor->spr.pos.vec2);
actor->spr.ang = NORM_ANGLE(actor->spr.ang - delta_ang);
}
else
{
rotatepoint(sop->pmid.vec2, actor->spr.pos.vec2, delta_ang, &actor->spr.pos.vec2);
actor->spr.ang = NORM_ANGLE(actor->spr.ang + delta_ang);
}
}
else
{
if (!(sop->flags & SOBJ_DONT_ROTATE))
{
// NOT part of a sector - independant of any sector
rotatepoint(sop->pmid.vec2, actor->spr.pos.vec2, delta_ang, &actor->spr.pos.vec2);
actor->spr.ang = NORM_ANGLE(actor->spr.ang + delta_ang);
}
// Does not necessarily move with the sector so must accout for
// moving across sectors
if (sop->pmid.X < MAXSO) // special case for operating SO's
SetActorZ(sop->so_actors[i], &actor->spr.pos);
}
2021-12-26 00:30:29 +00:00
actor->user.oangdiff += getincangle(oldang, actor->spr.ang);
if ((actor->spr.extra & SPRX_BLADE))
{
2021-11-04 20:26:08 +00:00
DoBladeDamage(sop->so_actors[i]);
}
}
TRAVERSE_CONNECT(pnum)
{
pp = Player + pnum;
// if player was on a sector object
if (pp->sop_riding)
{
// update here AFTER sectors/player has been manipulated
// prevents you from falling into map HOLEs created by moving
// Sectors and sprites around.
//if (sop->xmid < MAXSO)
updatesector(pp->pos.X, pp->pos.Y, &pp->cursector);
// in case you are in a whirlpool
// move perfectly with the ride in the z direction
if (pp->Flags & PF_CRAWLING)
{
// move up some for really fast moving plats
//pp->posz -= PLAYER_HEIGHT + Z(12);
DoPlayerZrange(pp);
pp->pos.Z = pp->loz - PLAYER_CRAWL_HEIGHT;
pp->actor->spr.pos.Z = pp->loz;
}
else
{
// move up some for really fast moving plats
//pp->posz -= Z(24);
DoPlayerZrange(pp);
if (!(pp->Flags & (PF_JUMPING | PF_FALLING | PF_FLYING)))
{
pp->pos.Z = pp->loz - PLAYER_HEIGHT;
pp->actor->spr.pos.Z = pp->loz;
}
}
}
else
{
// if player was not on any sector object set Riding flag to false
2021-12-27 17:58:15 +00:00
pp->Flags &= ~(PF_PLAYER_RIDING);
}
}
}
2021-12-31 14:50:44 +00:00
void RefreshPoints(SECTOR_OBJECT* sop, int nx, int ny, bool dynamic)
{
2021-11-24 21:04:11 +00:00
short wallcount = 0, delta_ang_from_orig;
short ang;
int dx,dy,x,y;
// do scaling
if (dynamic && sop->PreMoveAnimator)
(*sop->PreMoveAnimator)(sop);
2021-12-31 14:43:47 +00:00
sectortype** sectp;
2021-11-24 21:04:11 +00:00
int j;
for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
{
if (!(sop->flags & SOBJ_SPRITE_OBJ))
{
// move all walls in sectors back to the original position
2021-11-24 21:04:11 +00:00
for (auto& wal : wallsofsector(*sectp))
{
if (!(wal.extra && (wal.extra & WALLFX_DONT_MOVE)))
{
dx = x = sop->pmid.X - sop->xorig[wallcount];
dy = y = sop->pmid.Y - sop->yorig[wallcount];
if (dynamic && sop->scale_type)
{
if (!(wal.extra & WALLFX_DONT_SCALE))
{
ang = NORM_ANGLE(getangle(x - sop->pmid.X, y - sop->pmid.Y));
if (sop->scale_type == SO_SCALE_RANDOM_POINT)
{
// was causing memory overwrites
//ScaleRandomPoint(sop, k, ang, x, y, &dx, &dy);
ScaleRandomPoint(sop, wallcount, ang, x, y, &dx, &dy);
}
else
{
int xmul = (sop->scale_dist * sop->scale_x_mult)>>8;
int ymul = (sop->scale_dist * sop->scale_y_mult)>>8;
dx = x + MulScale(xmul, bcos(ang), 14);
dy = y + MulScale(ymul, bsin(ang), 14);
}
}
}
if (wal.extra && (wal.extra & WALLFX_LOOP_OUTER))
{
2021-11-24 21:04:11 +00:00
dragpoint(&wal, dx, dy);
}
else
{
wal.movexy(dx, dy);
}
}
wallcount++;
}
}
}
if (sop->spin_speed)
{
// same as below - ignore the objects angle
// last_ang is the last true angle before SO started spinning
delta_ang_from_orig = NORM_ANGLE(sop->last_ang + sop->spin_ang - sop->ang_orig);
}
else
{
// angle traveling + the new spin angle all offset from the original
// angle
delta_ang_from_orig = NORM_ANGLE(sop->ang + sop->spin_ang - sop->ang_orig);
}
// Note that this delta angle is from the original angle
// nx,ny are 0 so the points are not moved, just rotated
MovePoints(sop, delta_ang_from_orig, nx, ny);
// do morphing - angle independent
if (dynamic && sop->PostMoveAnimator)
(*sop->PostMoveAnimator)(sop);
}
2021-12-31 14:50:44 +00:00
void KillSectorObjectSprites(SECTOR_OBJECT* sop)
{
int i;
2021-11-02 17:45:21 +00:00
for (i = 0; sop->so_actors[i] != nullptr; i++)
{
2021-12-07 18:22:30 +00:00
DSWActor* actor = sop->so_actors[i];
if (!actor) continue;
// not a part of the so anymore
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_SO_ATTACHED);
if (actor->spr.picnum == ST1 && actor->spr.hitag == SPAWN_SPOT)
continue;
so_stopspriteinterpolation(sop, actor);
KillActor(actor);
}
// clear the list
2021-11-02 17:45:21 +00:00
sop->so_actors[0] = nullptr;
}
2021-12-31 14:50:44 +00:00
void UpdateSectorObjectSprites(SECTOR_OBJECT* sop)
{
int i;
2021-11-02 17:45:21 +00:00
for (i = 0; sop->so_actors[i] != nullptr; i++)
{
2021-12-07 18:22:30 +00:00
DSWActor* actor = sop->so_actors[i];
if (!actor) continue;
SetActorZ(actor, &actor->spr.pos);
}
}
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* DetectSectorObject(sectortype* sectph)
{
short j;
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* sop;
// collapse the SO to a single point
// move all points to nx,ny
for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
{
if (SO_EMPTY(sop))
continue;
for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
{
if (sectph == *sectp)
return sop;
}
}
return nullptr;
}
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* DetectSectorObjectByWall(walltype* wph)
{
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* sop;
// collapse the SO to a single point
// move all points to nx,ny
for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
{
if (SO_EMPTY(sop))
continue;
2021-12-31 14:43:47 +00:00
sectortype** sectp;
2021-11-24 21:04:11 +00:00
int j;
for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
{
2021-11-24 21:04:11 +00:00
for (auto& wal : wallsofsector(*sectp))
{
// if outer wall check the NEXTWALL also
if ((wal.extra & WALLFX_LOOP_OUTER))
{
2021-11-24 21:04:11 +00:00
if (wal.twoSided() && wph == wal.nextWall())
return sop;
}
2021-11-24 21:04:11 +00:00
if (wph == &wal)
return sop;
}
}
}
return nullptr;
}
2021-12-31 14:50:44 +00:00
void CollapseSectorObject(SECTOR_OBJECT* sop, int nx, int ny)
{
2021-11-24 21:04:11 +00:00
int j;
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
// collapse the SO to a single point
// move all points to nx,ny
for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
{
if (!(sop->flags & SOBJ_SPRITE_OBJ))
{
// move all walls in sectors back to the original position
2021-11-24 21:04:11 +00:00
for (auto& wal : wallsofsector(*sectp))
{
if ((wal.extra & WALLFX_DONT_MOVE))
continue;
if (wal.extra && (wal.extra & WALLFX_LOOP_OUTER))
{
2021-11-24 21:04:11 +00:00
dragpoint(&wal, nx, ny);
}
else
{
wal.movexy(nx, ny);
}
}
}
}
}
2021-12-31 14:50:44 +00:00
void MoveZ(SECTOR_OBJECT* sop)
{
short i;
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
if (sop->bob_amt)
{
sop->bob_sine_ndx = (PlayClock << sop->bob_speed) & 2047;
sop->bob_diff = MulScale(sop->bob_amt, bsin(sop->bob_sine_ndx), 14);
// for all sectors
for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
{
if (sop->sectp[i]->hasU() && (sop->sectp[i]->flags & SECTFU_SO_DONT_BOB))
continue;
(*sectp)->setfloorz(sop->zorig_floor[i] + sop->bob_diff);
}
}
if ((sop->flags & SOBJ_MOVE_VERTICAL))
{
i = AnimGetGoal (ANIM_SopZ, int(sop - SectorObject), nullptr);
if (i < 0)
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_MOVE_VERTICAL);
}
if ((sop->flags & SOBJ_ZDIFF_MODE))
{
return;
}
// move all floors
if ((sop->flags & SOBJ_ZDOWN))
{
for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
{
2021-11-24 21:49:00 +00:00
AnimSet(ANIM_Floorz, *sectp, sop->zorig_floor[i] + sop->z_tgt, sop->z_rate);
}
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_ZDOWN);
}
else if ((sop->flags & SOBJ_ZUP))
{
for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
{
2021-11-24 21:49:00 +00:00
AnimSet(ANIM_Floorz, *sectp, sop->zorig_floor[i] + sop->z_tgt, sop->z_rate);
}
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_ZUP);
}
}
2021-12-31 14:49:07 +00:00
void CallbackSOsink(ANIM* ap, void *data)
{
2021-12-31 14:50:44 +00:00
SECTOR_OBJECT* sop;
2021-11-16 17:53:40 +00:00
int i, ndx;
bool found = false;
int tgt_depth;
sectortype* srcsect = nullptr;
sectortype* destsect = nullptr;
2021-12-31 14:50:44 +00:00
sop = (SECTOR_OBJECT*)data;
for (i = 0; sop->sectp[i] != nullptr; i++)
{
if (sop->sectp[i]->hasU() && (sop->sectp[i]->flags & SECTFU_SO_SINK_DEST))
{
srcsect = sop->sectp[i];
break;
}
}
ASSERT(srcsect != nullptr);
for (i = 0; sop->sectp[i] != nullptr; i++)
{
if (ap->animtype == ANIM_Floorz && ap->animindex == sectnum(sop->sectp[i]))
{
destsect = sop->sectp[i];
break;
}
}
ASSERT(destsect != nullptr);
2021-11-24 21:04:11 +00:00
destsect->floorpicnum = srcsect->floorpicnum;
destsect->floorshade = srcsect->floorshade;
// destsect->floorz = srcsect->floorz;
2021-12-27 18:07:39 +00:00
destsect->floorstat &= ~(CSTAT_SECTOR_ALIGN);
destsect->u_defined = true;
ASSERT(srcsect->hasU());
2021-11-24 21:04:11 +00:00
tgt_depth = FixedToInt(srcsect->depth_fixed);
for(auto& sect: sector)
{
if (&sect == destsect)
{
ndx = AnimSet(ANIM_SUdepth, destsect, IntToFixed(tgt_depth), (ap->vel << 8) >> 8);
AnimSetVelAdj(ndx, ap->vel_adj);
found = true;
break;
}
}
ASSERT(found);
SWSectIterator it(destsect);
while (auto actor = it.Next())
{
if (!actor->hasU() || actor->user.PlayerP || !(actor->user.Flags & SPR_SO_ATTACHED))
continue;
// move sprite WAY down in water
ndx = AnimSet(ANIM_Userz, 0, actor, -actor->user.pos.Z - ActorSizeZ(actor) - Z(100), ap->vel>>8);
AnimSetVelAdj(ndx, ap->vel_adj);
}
// Take out any blocking walls
2021-11-24 21:04:11 +00:00
for(auto& wal : wallsofsector(destsect))
{
2021-12-27 18:07:39 +00:00
wal.cstat &= ~(CSTAT_WALL_BLOCK);
}
return;
}
2021-12-31 14:50:44 +00:00
void MoveSectorObjects(SECTOR_OBJECT* sop, short locktics)
{
int nx, ny;
short speed;
short delta_ang;
so_setinterpolationtics(sop, locktics);
if (sop->track >= SO_OPERATE_TRACK_START)
{
if ((sop->flags & SOBJ_UPDATE_ONCE))
{
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_UPDATE_ONCE);
RefreshPoints(sop, 0, 0, false);
}
return;
}
nx = 0;
ny = 0;
// if pausing the return
if (sop->wait_tics)
{
sop->wait_tics -= locktics;
if (sop->wait_tics <= 0)
sop->wait_tics = 0;
return;
}
delta_ang = 0;
if (sop->track > -1)
DoTrack(sop, locktics, &nx, &ny);
// get delta to target angle
delta_ang = getincangle(sop->ang, sop->ang_tgt);
sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> sop->turn_speed));
delta_ang = delta_ang >> sop->turn_speed;
// move z values
MoveZ(sop);
// calculate the spin speed
speed = sop->spin_speed * locktics;
// spin_ang is incremented by the spin_speed
sop->spin_ang = NORM_ANGLE(sop->spin_ang + speed);
if (sop->spin_speed)
{
// ignore delta angle if spinning
GlobSpeedSO = speed;
}
else
{
// The actual delta from the last frame
GlobSpeedSO = speed;
GlobSpeedSO += delta_ang;
}
if ((sop->flags & SOBJ_DYNAMIC))
{
// trick tricks
RefreshPoints(sop, nx, ny, true);
}
else
{
// Update the points so there will be no warping
if ((sop->flags & (SOBJ_UPDATE|SOBJ_UPDATE_ONCE)) ||
sop->vel ||
(sop->ang != sop->ang_tgt) ||
GlobSpeedSO)
{
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_UPDATE_ONCE);
RefreshPoints(sop, nx, ny, false);
}
}
}
2021-12-31 14:50:44 +00:00
void DoTrack(SECTOR_OBJECT* sop, short locktics, int *nx, int *ny)
{
2021-12-31 14:13:05 +00:00
TRACK_POINT* tpoint;
int dx, dy, dz;
int dist;
tpoint = Track[sop->track].TrackPoint + sop->point;
// calculate an angle to the target
if (sop->vel)
sop->ang_moving = sop->ang_tgt = getangle(tpoint->x - sop->pmid.X, tpoint->y - sop->pmid.Y);
// NOTE: Jittery ride - try new value out here
// NOTE: Put a loop around this (locktics) to make it more acuruate
2021-11-05 23:37:16 +00:00
const int TRACK_POINT_SIZE = 200;
if (sop->target_dist < 100)
{
switch (tpoint->tag_low)
{
case TRACK_MATCH_EVERYTHING:
DoMatchEverything(nullptr, tpoint->tag_high, -1);
break;
case TRACK_MATCH_EVERYTHING_ONCE:
DoMatchEverything(nullptr, tpoint->tag_high, -1);
tpoint->tag_low = 0;
tpoint->tag_high = 0;
break;
case TRACK_SPIN:
if (sop->spin_speed)
break;
sop->spin_speed = tpoint->tag_high;
sop->last_ang = sop->ang;
break;
case TRACK_SPIN_REVERSE:
{
if (!sop->spin_speed)
break;
if (sop->spin_speed >= 0)
{
sop->spin_speed = -sop->spin_speed;
}
}
break;
case TRACK_SPIN_STOP:
if (!sop->spin_speed)
break;
sop->spin_speed = 0;
break;
case TRACK_BOB_START:
sop->flags |= (SOBJ_ZMID_FLOOR);
sop->bob_amt = Z(tpoint->tag_high);
sop->bob_sine_ndx = 0;
sop->bob_speed = 4;
break;
case TRACK_BOB_STOP:
sop->bob_speed = 0;
sop->bob_sine_ndx = 0;
sop->bob_amt = 0;
break;
case TRACK_BOB_SPEED:
sop->bob_speed = tpoint->tag_high;
break;
case TRACK_REVERSE:
sop->dir *= -1;
break;
case TRACK_STOP:
sop->vel = 0;
sop->wait_tics = tpoint->tag_high * 128;
break;
case TRACK_SET_SPEED:
sop->vel = tpoint->tag_high * 256;
sop->vel_tgt = sop->vel;
break;
//
// Controls the velocity
//
case TRACK_VEL_RATE:
sop->vel_rate = tpoint->tag_high;
break;
case TRACK_SPEED_UP:
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_SLOW_DOWN | SOBJ_SPEED_UP);
if (sop->dir < 0)
{
// set target to new slower target
sop->vel_tgt = sop->vel_tgt - (tpoint->tag_high * 256);
sop->flags |= (SOBJ_SLOW_DOWN);
}
else
{
sop->vel_tgt = sop->vel_tgt + (tpoint->tag_high * 256);
sop->flags |= (SOBJ_SPEED_UP);
}
break;
case TRACK_SLOW_DOWN:
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_SLOW_DOWN | SOBJ_SPEED_UP);
if (sop->dir > 0)
{
sop->vel_tgt = sop->vel_tgt - (tpoint->tag_high * 256);
sop->flags |= (SOBJ_SLOW_DOWN);
}
else
{
sop->vel_tgt = sop->vel_tgt + (tpoint->tag_high * 256);
sop->flags |= (SOBJ_SPEED_UP);
}
break;
//
// Controls z
//
case TRACK_SO_SINK:
{
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
sectortype* dest_sector = nullptr;
short i,ndx;
for (i = 0; sop->sectp[i] != nullptr; i++)
{
if (sop->sectp[i]->hasU() && (sop->sectp[i]->flags & SECTFU_SO_SINK_DEST))
{
dest_sector = sop->sectp[i];
break;
}
}
ASSERT(dest_sector != nullptr);
sop->bob_speed = 0;
sop->bob_sine_ndx = 0;
sop->bob_amt = 0;
for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
{
if (sop->sectp[i]->hasU() && (sop->sectp[i]->flags & SECTFU_SO_DONT_SINK))
continue;
ndx = AnimSet(ANIM_Floorz, *sectp, dest_sector->floorz, tpoint->tag_high);
AnimSetCallback(ndx, CallbackSOsink, sop);
AnimSetVelAdj(ndx, 6);
}
break;
}
case TRACK_SO_FORM_WHIRLPOOL:
{
// for lowering the whirlpool in level 1
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
2021-11-20 22:35:14 +00:00
int i;
for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
{
if ((*sectp)->hasU())
{
2021-11-20 22:20:43 +00:00
if ((*sectp) && (*sectp)->stag == SECT_SO_FORM_WHIRLPOOL)
{
2021-11-24 21:49:00 +00:00
AnimSet(ANIM_Floorz, *sectp, (*sectp)->floorz + Z((*sectp)->height), 128);
2021-11-20 22:20:43 +00:00
(*sectp)->floorshade += (*sectp)->height / 6;
2021-12-27 18:07:39 +00:00
(*sectp)->extra &= ~(SECTFX_NO_RIDE);
}
}
}
break;
}
case TRACK_MOVE_VERTICAL:
{
int zr;
sop->flags |= (SOBJ_MOVE_VERTICAL);
if (tpoint->tag_high > 0)
zr = tpoint->tag_high;
else
zr = 256;
// look at the next point
NextTrackPoint(sop);
tpoint = Track[sop->track].TrackPoint + sop->point;
// set anim
AnimSet(ANIM_SopZ, int(sop-SectorObject), nullptr, tpoint->z, zr);
// move back to current point by reversing direction
sop->dir *= -1;
NextTrackPoint(sop);
tpoint = Track[sop->track].TrackPoint + sop->point;
sop->dir *= -1;
break;
}
case TRACK_WAIT_FOR_EVENT:
{
if (tpoint->tag_high == -1)
break;
sop->flags |= (SOBJ_WAIT_FOR_EVENT);
sop->save_vel = sop->vel;
sop->save_spin_speed = sop->spin_speed;
sop->vel = sop->spin_speed = 0;
// only set event if non-zero
if (tpoint->tag_high)
sop->match_event = tpoint->tag_high;
tpoint->tag_high = -1;
break;
}
case TRACK_ZDIFF_MODE:
sop->flags |= (SOBJ_ZDIFF_MODE);
sop->zdelta = Z(tpoint->tag_high);
break;
case TRACK_ZRATE:
sop->z_rate = Z(tpoint->tag_high);
break;
case TRACK_ZUP:
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_ZDOWN | SOBJ_ZUP);
if (sop->dir < 0)
{
sop->z_tgt = sop->z_tgt + Z(tpoint->tag_high);
sop->flags |= (SOBJ_ZDOWN);
}
else
{
sop->z_tgt = sop->z_tgt - Z(tpoint->tag_high);
sop->flags |= (SOBJ_ZUP);
}
break;
case TRACK_ZDOWN:
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_ZDOWN | SOBJ_ZUP);
if (sop->dir > 0)
{
sop->z_tgt = sop->z_tgt + Z(tpoint->tag_high);
sop->flags |= (SOBJ_ZDOWN);
}
else
{
sop->z_tgt = sop->z_tgt - Z(tpoint->tag_high);
sop->flags |= (SOBJ_ZUP);
}
break;
}
// get the next point
NextTrackPoint(sop);
tpoint = Track[sop->track].TrackPoint + sop->point;
// calculate distance to target poing
sop->target_dist = Distance(sop->pmid.X, sop->pmid.Y, tpoint->x, tpoint->y);
// calculate a new angle to the target
sop->ang_moving = sop->ang_tgt = getangle(tpoint->x - sop->pmid.X, tpoint->y - sop->pmid.Y);
if ((sop->flags & SOBJ_ZDIFF_MODE))
{
short i;
// set dx,dy,dz up for finding the z magnitude
dx = tpoint->x;
dy = tpoint->y;
dz = tpoint->z - sop->zdelta;
// find the distance to the target (player)
dist = DIST(dx, dy, sop->pmid.X, sop->pmid.Y);
// (velocity * difference between the target and the object)
// / distance
sop->z_rate = (sop->vel * (sop->pmid.Z - dz)) / dist;
// take absolute value and convert to pixels (divide by 256)
sop->z_rate = PIXZ(labs(sop->z_rate));
if ((sop->flags & SOBJ_SPRITE_OBJ))
{
// only modify zmid for sprite_objects
AnimSet(ANIM_SopZ, int(sop - SectorObject), nullptr, dz, sop->z_rate);
}
else
{
// churn through sectors setting their new z values
for (i = 0; sop->sectp[i] != nullptr; i++)
{
AnimSet(ANIM_Floorz, sop->sectp[i], dz - (sop->mid_sector->floorz - sop->sectp[i]->floorz), sop->z_rate);
}
}
}
}
else
{
// make velocity approach the target velocity
if ((sop->flags & SOBJ_SPEED_UP))
{
if ((sop->vel += (locktics << sop->vel_rate)) >= sop->vel_tgt)
{
sop->vel = sop->vel_tgt;
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_SPEED_UP);
}
}
else if ((sop->flags & SOBJ_SLOW_DOWN))
{
if ((sop->vel -= (locktics << sop->vel_rate)) <= sop->vel_tgt)
{
sop->vel = sop->vel_tgt;
2021-12-27 17:58:15 +00:00
sop->flags &= ~(SOBJ_SLOW_DOWN);
}
}
}
// calculate a new x and y
if (sop->vel && !(sop->flags & SOBJ_MOVE_VERTICAL))
{
*nx = ((sop->vel) >> 8) * locktics * bcos(sop->ang_moving) >> 14;
*ny = ((sop->vel) >> 8) * locktics * bsin(sop->ang_moving) >> 14;
dist = Distance(sop->pmid.X, sop->pmid.Y, sop->pmid.X + *nx, sop->pmid.Y + *ny);
sop->target_dist -= dist;
}
}
2021-12-31 14:50:44 +00:00
void OperateSectorObjectForTics(SECTOR_OBJECT* sop, short newang, int newx, int newy, short locktics)
{
int i;
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
if (Prediction)
return;
if (sop->track < SO_OPERATE_TRACK_START)
return;
so_setinterpolationtics(sop, locktics);
if (sop->bob_amt)
{
sop->bob_sine_ndx = (PlayClock << sop->bob_speed) & 2047;
sop->bob_diff = MulScale(sop->bob_amt, bsin(sop->bob_sine_ndx), 14);
// for all sectors
for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
{
if (sop->sectp[i]->hasU() && (sop->sectp[i]->flags & SECTFU_SO_DONT_BOB))
continue;
(*sectp)->setfloorz(sop->zorig_floor[i] + sop->bob_diff);
}
}
GlobSpeedSO = 0;
//sop->ang_tgt = newang;
sop->ang_moving = newang;
sop->spin_ang = 0;
sop->ang = newang;
RefreshPoints(sop, newx - sop->pmid.X, newy - sop->pmid.Y, false);
}
2021-12-31 14:50:44 +00:00
void OperateSectorObject(SECTOR_OBJECT* sop, short newang, int newx, int newy)
{
OperateSectorObjectForTics(sop, newang, newx, newy, synctics);
}
2021-12-31 14:50:44 +00:00
void PlaceSectorObject(SECTOR_OBJECT* sop, int newx, int newy)
{
so_setinterpolationtics(sop, synctics);
RefreshPoints(sop, newx - sop->pmid.X, newy - sop->pmid.Y, false);
}
2021-12-31 22:09:34 +00:00
void VehicleSetSmoke(SECTOR_OBJECT* sop, ANIMATOR* animator)
{
2021-12-31 14:43:47 +00:00
sectortype* *sectp;
for (sectp = sop->sectp; *sectp; sectp++)
{
SWSectIterator it(*sectp);
2021-10-30 18:00:02 +00:00
while (auto actor = it.Next())
{
switch (actor->spr.hitag)
{
case SPAWN_SPOT:
if (actor->spr.clipdist == 3)
{
if (animator)
{
if (actor->spr.statnum == STAT_NO_STATE)
break;
2021-10-30 18:00:02 +00:00
change_actor_stat(actor, STAT_NO_STATE);
DoSoundSpotMatch(actor->spr.lotag, 1, 0);
DoSpawnSpotsForDamage(actor->spr.lotag);
}
else
{
2021-10-30 18:00:02 +00:00
change_actor_stat(actor, STAT_SPAWN_SPOT);
DoSoundSpotStopSound(actor->spr.lotag);
}
2021-12-26 00:30:29 +00:00
actor->user.ActorActionFunc = animator;
}
break;
}
}
}
}
2021-12-31 14:50:44 +00:00
void TornadoSpin(SECTOR_OBJECT* sop)
{
short delta_ang, speed;
short locktics = synctics;
// get delta to target angle
delta_ang = getincangle(sop->ang, sop->ang_tgt);
sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> sop->turn_speed));
delta_ang = delta_ang >> sop->turn_speed;
// move z values
MoveZ(sop);
// calculate the spin speed
speed = sop->spin_speed * locktics;
// spin_ang is incremented by the spin_speed
sop->spin_ang = NORM_ANGLE(sop->spin_ang + speed);
if (sop->spin_speed)
{
// ignore delta angle if spinning
GlobSpeedSO = speed;
}
else
{
// The actual delta from the last frame
GlobSpeedSO = speed;
GlobSpeedSO += delta_ang;
}
}
2021-12-31 14:50:44 +00:00
void DoTornadoObject(SECTOR_OBJECT* sop)
{
int xvect,yvect;
// this made them move together more or less - cool!
//static short ang = 1024;
int floor_dist;
vec3_t pos;
int ret;
short *ang = &sop->ang_moving;
xvect = sop->vel * bcos(*ang);
yvect = sop->vel * bcos(*ang);
2021-11-24 22:07:02 +00:00
auto cursect = sop->op_main_sector; // for sop->vel
floor_dist = (abs(cursect->ceilingz - cursect->floorz)) >> 2;
pos.X = sop->pmid.X;
pos.Y = sop->pmid.Y;
2021-12-22 09:41:47 +00:00
pos.Z = floor_dist;
PlaceSectorObject(sop, MAXSO, MAXSO);
Collision coll;
clipmove(pos, &cursect, xvect, yvect, (int)sop->clipdist, Z(0), floor_dist, CLIPMASK_ACTOR, coll);
if (coll.type != kHitNone)
{
*ang = NORM_ANGLE(*ang + 1024 + RANDOM_P2(512) - 256);
}
TornadoSpin(sop);
RefreshPoints(sop, pos.X - sop->pmid.X, pos.Y - sop->pmid.Y, true);
}
2021-12-31 14:50:44 +00:00
void DoAutoTurretObject(SECTOR_OBJECT* sop)
{
2021-12-07 18:22:30 +00:00
DSWActor* actor = sop->sp_child;
if (!actor) return;
short delta_ang;
int diff;
short i;
2021-12-26 00:32:40 +00:00
if ((sop->max_damage != -9999 && sop->max_damage <= 0) || !actor->hasU())
return;
2021-12-26 00:30:29 +00:00
actor->user.WaitTics -= synctics;
// check for new player if doesn't have a target or time limit expired
2021-12-26 00:30:29 +00:00
if (!actor->user.targetActor || actor->user.WaitTics < 0)
{
// 4 seconds
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 4*120;
2021-10-30 11:05:07 +00:00
DoActorPickClosePlayer(actor);
}
if (MoveSkip2 == 0)
{
2021-11-02 17:45:21 +00:00
for (i = 0; sop->so_actors[i] != nullptr; i++)
{
2021-12-07 18:22:30 +00:00
DSWActor* sActor = sop->so_actors[i];
if (!sActor) continue;
2021-12-25 00:35:54 +00:00
if (sActor->spr.statnum == STAT_SO_SHOOT_POINT)
2021-11-02 17:45:21 +00:00
{
if (!FAFcansee(sActor->spr.pos.X, sActor->spr.pos.Y, sActor->spr.pos.Z-Z(4), sActor->sector(),
actor->user.targetActor->spr.pos.X, actor->user.targetActor->spr.pos.Y, ActorUpperZ(actor->user.targetActor), actor->user.targetActor->sector()))
{
return;
}
}
}
// FirePausing
2021-12-26 00:30:29 +00:00
if (actor->user.Counter > 0)
{
2021-12-26 00:30:29 +00:00
actor->user.Counter -= synctics*2;
if (actor->user.Counter <= 0)
actor->user.Counter = 0;
}
2021-12-26 00:30:29 +00:00
if (actor->user.Counter == 0)
{
2021-11-02 17:45:21 +00:00
for (i = 0; sop->so_actors[i] != nullptr; i++)
{
2021-12-07 18:22:30 +00:00
DSWActor* sActor = sop->so_actors[i];
if (!sActor) continue;
if (sActor->spr.statnum == STAT_SO_SHOOT_POINT)
2021-11-02 17:45:21 +00:00
{
if (SP_TAG5(sActor))
2021-12-26 00:30:29 +00:00
actor->user.Counter = SP_TAG5(sActor);
else
2021-12-26 00:30:29 +00:00
actor->user.Counter = 12;
InitTurretMgun(sop);
}
}
}
sop->ang_tgt = getangle(actor->user.targetActor->spr.pos.X - sop->pmid.X, actor->user.targetActor->spr.pos.Y - sop->pmid.Y);
// get delta to target angle
delta_ang = getincangle(sop->ang, sop->ang_tgt);
//sop->ang += delta_ang >> 4;
sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> 3));
//sop->ang += delta_ang >> 2;
if (sop->limit_ang_center >= 0)
{
diff = getincangle(sop->limit_ang_center, sop->ang);
if (labs(diff) >= sop->limit_ang_delta)
{
if (diff < 0)
sop->ang = sop->limit_ang_center - sop->limit_ang_delta;
else
sop->ang = sop->limit_ang_center + sop->limit_ang_delta;
}
}
OperateSectorObjectForTics(sop, sop->ang, sop->pmid.X, sop->pmid.Y, 2*synctics);
}
}
2021-11-05 23:37:16 +00:00
void DoActorHitTrackEndPoint(DSWActor* actor)
{
2021-12-27 18:07:39 +00:00
Track[actor->user.track].flags &= ~(TF_TRACK_OCCUPIED);
// jump the current track & determine if you should go to another
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_RUN_AWAY))
{
// look for another track leading away from the player
2021-12-26 00:30:29 +00:00
actor->user.track = FindTrackAwayFromPlayer(actor);
2021-12-26 00:30:29 +00:00
if (actor->user.track >= 0)
{
2021-12-26 00:30:29 +00:00
actor->spr.ang = NORM_ANGLE(getangle((Track[actor->user.track].TrackPoint + actor->user.point)->x - actor->spr.pos.X, (Track[actor->user.track].TrackPoint + actor->user.point)->y - actor->spr.pos.Y));
}
else
{
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_RUN_AWAY);
2021-10-30 10:09:34 +00:00
DoActorSetSpeed(actor, NORM_SPEED);
2021-12-26 00:30:29 +00:00
actor->user.track = -1;
}
}
2021-12-27 18:34:06 +00:00
else if (actor->user.Flags & (SPR_FIND_PLAYER))
{
// look for another track leading away from the player
2021-12-26 00:30:29 +00:00
actor->user.track = FindTrackToPlayer(actor);
2021-12-26 00:30:29 +00:00
if (actor->user.track >= 0)
{
2021-12-26 00:30:29 +00:00
actor->spr.ang = NORM_ANGLE(getangle((Track[actor->user.track].TrackPoint + actor->user.point)->x - actor->spr.pos.X, (Track[actor->user.track].TrackPoint + actor->user.point)->y - actor->spr.pos.Y));
}
else
{
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_FIND_PLAYER);
2021-10-30 10:09:34 +00:00
DoActorSetSpeed(actor, NORM_SPEED);
2021-12-26 00:30:29 +00:00
actor->user.track = -1;
}
}
else
{
2021-12-26 00:30:29 +00:00
actor->user.track = -1;
}
}
2021-11-05 23:37:16 +00:00
void ActorLeaveTrack(DSWActor* actor)
{
2021-12-26 00:30:29 +00:00
if (actor->user.track == -1)
return;
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_FIND_PLAYER|SPR_RUN_AWAY|SPR_CLIMBING);
2021-12-27 18:07:39 +00:00
Track[actor->user.track].flags &= ~(TF_TRACK_OCCUPIED);
2021-12-26 00:30:29 +00:00
actor->user.track = -1;
}
2021-12-31 14:13:05 +00:00
bool ActorTrackDecide(TRACK_POINT* tpoint, DSWActor* actor)
{
switch (tpoint->tag_low)
{
case TRACK_START:
// if track has a type and actor is going the right direction jump
// the track
2021-12-26 00:30:29 +00:00
if (Track[actor->user.track].ttflags)
{
2021-12-26 00:30:29 +00:00
if (actor->user.track_dir == -1)
{
2021-11-05 23:37:16 +00:00
DoActorHitTrackEndPoint(actor);
return false;
}
}
break;
case TRACK_END:
// if track has a type and actor is going to right direction jump the
// track
2021-12-26 00:30:29 +00:00
if (Track[actor->user.track].ttflags)
{
2021-12-26 00:30:29 +00:00
if (actor->user.track_dir == 1)
{
2021-11-05 23:37:16 +00:00
DoActorHitTrackEndPoint(actor);
return false;
}
}
break;
case TRACK_ACTOR_WAIT_FOR_PLAYER:
{
actor->user.Flags |= (SPR_WAIT_FOR_PLAYER);
2021-12-26 00:30:29 +00:00
actor->user.Dist = tpoint->tag_high;
break;
}
case TRACK_ACTOR_WAIT_FOR_TRIGGER:
{
actor->user.Flags |= (SPR_WAIT_FOR_TRIGGER);
2021-12-26 00:30:29 +00:00
actor->user.Dist = tpoint->tag_high;
break;
}
//
// Controls the velocity
//
case TRACK_ACTOR_VEL_RATE:
2021-12-26 00:30:29 +00:00
actor->user.vel_rate = tpoint->tag_high;
break;
case TRACK_ACTOR_SPEED_UP:
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_SLOW_DOWN | SPR_SPEED_UP);
2021-12-26 00:30:29 +00:00
if (actor->user.track_dir < 0)
{
// set target to new slower target
2021-12-26 00:30:29 +00:00
actor->user.vel_tgt = actor->user.vel_tgt - (tpoint->tag_high * 256);
actor->user.Flags |= (SPR_SLOW_DOWN);
}
else
{
2021-12-26 00:30:29 +00:00
actor->user.vel_tgt = actor->user.vel_tgt + (tpoint->tag_high * 256);
actor->user.Flags |= (SPR_SPEED_UP);
}
break;
case TRACK_ACTOR_SLOW_DOWN:
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_SLOW_DOWN | SPR_SPEED_UP);
2021-12-26 00:30:29 +00:00
if (actor->user.track_dir > 0)
{
2021-12-26 00:30:29 +00:00
actor->user.vel_tgt = actor->user.vel_tgt - (tpoint->tag_high * 256);
actor->user.Flags |= (SPR_SLOW_DOWN);
}
else
{
2021-12-26 00:30:29 +00:00
actor->user.vel_tgt = actor->user.vel_tgt + (tpoint->tag_high * 256);
actor->user.Flags |= (SPR_SPEED_UP);
}
break;
// Reverse it
case TRACK_ACTOR_REVERSE:
2021-12-26 00:30:29 +00:00
actor->user.track_dir *= -1;
break;
case TRACK_ACTOR_STAND:
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Stand);
break;
case TRACK_ACTOR_JUMP:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Jump)
{
actor->spr.ang = tpoint->ang;
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = ACTOR_STD_JUMP;
else
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -tpoint->tag_high;
DoActorBeginJump(actor);
2021-12-26 00:30:29 +00:00
actor->user.ActorActionFunc = DoActorMoveJump;
}
break;
case TRACK_ACTOR_QUICK_JUMP:
case TRACK_ACTOR_QUICK_SUPER_JUMP:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Jump)
{
int zdiff;
HitInfo hit{};
actor->spr.ang = tpoint->ang;
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
if (tpoint->tag_high)
{
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -tpoint->tag_high;
}
else
{
2021-12-27 17:58:15 +00:00
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
FAFhitscan(actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z - Z(24), actor->sector(), // Start position
bcos(actor->spr.ang), // X vector of 3D ang
bsin(actor->spr.ang), // Y vector of 3D ang
0, // Z vector of 3D ang
hit, CLIPMASK_MISSILE);
actor->spr.cstat |= (CSTAT_SPRITE_BLOCK);
ASSERT(hit.hitSector != nullptr);
if (hit.actor() != nullptr)
return false;
if (hit.hitWall == nullptr)
return false;
2021-12-05 20:31:19 +00:00
if (!hit.hitWall->twoSided())
return false;
zdiff = labs(actor->spr.pos.Z - hit.hitWall->nextSector()->floorz) >> 8;
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = PickJumpSpeed(actor, zdiff);
}
DoActorBeginJump(actor);
2021-12-26 00:30:29 +00:00
actor->user.ActorActionFunc = DoActorMoveJump;
return false;
}
break;
case TRACK_ACTOR_QUICK_JUMP_DOWN:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Jump)
{
actor->spr.ang = tpoint->ang;
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
if (tpoint->tag_high)
{
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -tpoint->tag_high;
}
else
{
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -350;
}
DoActorBeginJump(actor);
2021-12-26 00:30:29 +00:00
actor->user.ActorActionFunc = DoActorMoveJump;
return false;
}
break;
case TRACK_ACTOR_QUICK_SCAN:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Jump)
{
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
return false;
}
break;
case TRACK_ACTOR_QUICK_DUCK:
2021-12-26 00:30:29 +00:00
if (actor->user.Rot != actor->user.ActorActionSet->Duck)
{
actor->spr.ang = tpoint->ang;
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 4 * 120;
else
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = tpoint->tag_high * 128;
InitActorDuck(actor);
2021-12-26 00:30:29 +00:00
actor->user.ActorActionFunc = DoActorDuck;
return false;
}
break;
case TRACK_ACTOR_OPERATE:
case TRACK_ACTOR_QUICK_OPERATE:
{
2021-11-26 18:30:32 +00:00
HitInfo near;
int z[2];
int i;
2021-12-26 00:30:29 +00:00
if (actor->user.Rot == actor->user.ActorActionSet->Sit || actor->user.Rot == actor->user.ActorActionSet->Stand)
return false;
actor->spr.ang = tpoint->ang;
z[0] = actor->spr.pos.Z - ActorSizeZ(actor) + Z(5);
2021-12-27 17:19:30 +00:00
z[1] = actor->spr.pos.Z - (ActorSizeZ(actor) >> 1);
for (i = 0; i < (int)SIZ(z); i++)
{
neartag({ actor->spr.pos.X, actor->spr.pos.Y, z[i] }, actor->sector(), actor->spr.ang, near, 1024, NTAG_SEARCH_LO_HI);
2021-12-22 09:36:09 +00:00
if (near.actor() != nullptr && near.hitpos.X < 1024)
{
2021-11-26 18:30:32 +00:00
if (OperateSprite(near.actor(), false))
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 2 * 120;
else
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = tpoint->tag_high * 128;
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Stand);
}
}
}
2021-12-22 09:36:09 +00:00
if (near.hitSector != nullptr && near.hitpos.X < 1024)
{
2021-11-26 18:30:32 +00:00
if (OperateSector(near.hitSector, false))
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 2 * 120;
else
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = tpoint->tag_high * 128;
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Sit);
}
}
break;
}
case TRACK_ACTOR_JUMP_IF_FORWARD:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Jump && actor->user.track_dir == 1)
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = ACTOR_STD_JUMP;
else
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -tpoint->tag_high;
DoActorBeginJump(actor);
}
break;
case TRACK_ACTOR_JUMP_IF_REVERSE:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Jump && actor->user.track_dir == -1)
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = ACTOR_STD_JUMP;
else
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -tpoint->tag_high;
DoActorBeginJump(actor);
}
break;
case TRACK_ACTOR_CRAWL:
2021-12-26 00:30:29 +00:00
if (actor->user.Rot != actor->user.ActorActionSet->Crawl)
NewStateGroup(actor, actor->user.ActorActionSet->Crawl);
else
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Rise);
break;
case TRACK_ACTOR_SWIM:
2021-12-26 00:30:29 +00:00
if (actor->user.Rot != actor->user.ActorActionSet->Swim)
NewStateGroup(actor, actor->user.ActorActionSet->Swim);
else
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Rise);
break;
case TRACK_ACTOR_FLY:
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Fly);
break;
case TRACK_ACTOR_SIT:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Sit)
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 3 * 120;
else
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = tpoint->tag_high * 128;
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Sit);
}
break;
case TRACK_ACTOR_DEATH1:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Death2)
{
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 4 * 120;
NewStateGroup(actor, actor->user.ActorActionSet->Death1);
}
break;
case TRACK_ACTOR_DEATH2:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Death2)
{
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 4 * 120;
NewStateGroup(actor, actor->user.ActorActionSet->Death2);
}
break;
case TRACK_ACTOR_DEATH_JUMP:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->DeathJump)
{
actor->user.Flags |= (SPR_DEAD);
actor->spr.xvel <<= 1;
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -495;
DoActorBeginJump(actor);
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->DeathJump);
}
break;
case TRACK_ACTOR_CLOSE_ATTACK1:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->CloseAttack[0])
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 2 * 120;
else
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = tpoint->tag_high * 128;
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->CloseAttack[0]);
}
break;
case TRACK_ACTOR_CLOSE_ATTACK2:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->CloseAttack[1])
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 4 * 120;
else
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = tpoint->tag_high * 128;
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->CloseAttack[1]);
}
break;
case TRACK_ACTOR_ATTACK1:
case TRACK_ACTOR_ATTACK2:
case TRACK_ACTOR_ATTACK3:
case TRACK_ACTOR_ATTACK4:
case TRACK_ACTOR_ATTACK5:
case TRACK_ACTOR_ATTACK6:
{
2021-12-31 15:00:14 +00:00
STATE* **ap = &actor->user.ActorActionSet->Attack[0] + (tpoint->tag_low - TRACK_ACTOR_ATTACK1);
if (*ap)
{
if (!tpoint->tag_high)
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = 4 * 120;
else
2021-12-26 00:30:29 +00:00
actor->user.WaitTics = tpoint->tag_high * 128;
NewStateGroup(actor, *ap);
}
break;
}
case TRACK_ACTOR_ZDIFF_MODE:
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_ZDIFF_MODE))
{
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_ZDIFF_MODE);
actor->spr.pos.Z = actor->sector()->floorz;
actor->spr.zvel = 0;
}
else
{
actor->user.Flags |= (SPR_ZDIFF_MODE);
}
break;
case TRACK_ACTOR_CLIMB_LADDER:
2021-12-26 00:30:29 +00:00
if (actor->user.ActorActionSet->Jump)
{
int bos_z,nx,ny;
2021-11-26 18:30:32 +00:00
HitInfo near;
//
// Get angle and x,y pos from CLIMB_MARKER
//
auto lActor = FindNearSprite(actor, STAT_CLIMB_MARKER);
if (!lActor)
{
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
return false;
}
// determine where the player is supposed to be in relation to the ladder
// move out in front of the ladder
2021-12-25 00:35:54 +00:00
nx = MOVEx(100, lActor->spr.ang);
ny = MOVEy(100, lActor->spr.ang);
2022-02-01 18:41:58 +00:00
actor->set_int_xy(lActor->spr.pos.X + nx, lActor->spr.pos.Y + ny);
2021-12-25 00:35:54 +00:00
actor->spr.ang = NORM_ANGLE(lActor->spr.ang + 1024);
//
// Get the z height to climb
//
neartag({ actor->spr.pos.X, actor->spr.pos.Y, ActorZOfTop(actor) - (ActorSizeZ(actor) >> 1) }, actor->sector(), actor->spr.ang, near, 600, NTAG_SEARCH_LO_HI);
2021-11-26 18:30:32 +00:00
if (near.hitWall == nullptr)
{
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
return false;
}
2021-11-26 18:30:32 +00:00
auto wal = near.hitWall;
#if 0
2021-11-24 20:39:35 +00:00
if (!wal->twoSided())
{
I_Error("Take out white wall ladder x = %d, y = %d",wal->x, wal->y);
}
#endif
// destination z for climbing
if (wal->twoSided())
actor->user.pos.Z = wal->nextSector()->floorz;
else
actor->user.pos.Z = wal->sectorp()->ceilingz; // don't crash on bad setups.
2021-11-03 16:47:13 +00:00
DoActorZrange(actor);
//
// Adjust for YCENTERING
//
actor->spr.cstat |= (CSTAT_SPRITE_YCENTER);
bos_z = ActorZOfBottom(actor);
2021-12-26 00:30:29 +00:00
if (bos_z > actor->user.loz)
{
actor->user.pos.Y = (bos_z - actor->spr.pos.Z);
actor->spr.pos.Z -= actor->user.pos.Y;
}
//
// Misc climb setup
//
actor->user.Flags |= (SPR_CLIMBING);
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Climb);
actor->spr.zvel = -Z(1);
}
break;
case TRACK_ACTOR_SET_JUMP:
2021-12-26 00:30:29 +00:00
actor->user.jump_speed = -tpoint->tag_high;
break;
}
return true;
}
/*
!AIC - This is where actors follow tracks. Its massy, hard to read, and more
complex than it needs to be. It was taken from sector object track movement
code. The routine above ActorTrackDecide() is where a track tag is recognized
and acted upon. There are quite a few of these that are not useful to us at
present time.
*/
2021-11-01 13:36:46 +00:00
int ActorFollowTrack(DSWActor* actor, short locktics)
{
2021-12-31 14:59:11 +00:00
PLAYER* pp;
2021-12-31 14:13:05 +00:00
TRACK_POINT* tpoint;
short pnum;
int nx = 0, ny = 0, nz = 0, dx, dy, dz;
int dist;
// if not on a track then better not go here
2021-12-26 00:30:29 +00:00
if (actor->user.track == -1)
return true;
// if lying in wait for player
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_WAIT_FOR_PLAYER | SPR_WAIT_FOR_TRIGGER))
{
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_WAIT_FOR_PLAYER))
{
TRAVERSE_CONNECT(pnum)
{
pp = &Player[pnum];
2021-12-26 00:30:29 +00:00
if (Distance(actor->spr.pos.X, actor->spr.pos.Y, pp->pos.X, pp->pos.Y) < actor->user.Dist)
{
actor->user.targetActor = pp->actor;
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_WAIT_FOR_PLAYER);
return true;
}
}
}
2021-12-26 00:30:29 +00:00
actor->user.Tics = 0;
return true;
}
// if pausing the return
2021-12-26 00:30:29 +00:00
if (actor->user.WaitTics)
{
2021-12-26 00:30:29 +00:00
actor->user.WaitTics -= locktics;
if (actor->user.WaitTics <= 0)
{
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_DONT_UPDATE_ANG);
2021-12-26 00:30:29 +00:00
NewStateGroup(actor, actor->user.ActorActionSet->Run);
actor->user.WaitTics = 0;
}
return true;
}
2021-12-26 00:30:29 +00:00
tpoint = Track[actor->user.track].TrackPoint + actor->user.point;
2021-12-27 18:34:06 +00:00
if (!(actor->user.Flags & (SPR_CLIMBING | SPR_DONT_UPDATE_ANG)))
{
actor->spr.ang = getangle(tpoint->x - actor->spr.pos.X, tpoint->y - actor->spr.pos.Y);
}
if ((dist = Distance(actor->spr.pos.X, actor->spr.pos.Y, tpoint->x, tpoint->y)) < 200) // 64
{
2021-11-05 23:37:16 +00:00
if (!ActorTrackDecide(tpoint, actor))
return true;
// get the next point
2021-11-05 23:37:16 +00:00
NextActorTrackPoint(actor);
2021-12-26 00:30:29 +00:00
tpoint = Track[actor->user.track].TrackPoint + actor->user.point;
2021-12-27 18:34:06 +00:00
if (!(actor->user.Flags & (SPR_CLIMBING | SPR_DONT_UPDATE_ANG)))
{
// calculate a new angle to the target
actor->spr.ang = getangle(tpoint->x - actor->spr.pos.X, tpoint->y - actor->spr.pos.Y);
}
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_ZDIFF_MODE))
{
// set dx,dy,dz up for finding the z magnitude
dx = tpoint->x;
dy = tpoint->y;
dz = tpoint->z;
// find the distance to the target (player)
dist = DIST(dx, dy, actor->spr.pos.X, actor->spr.pos.Y);
// (velocity * difference between the target and the object) /
// distance
actor->spr.zvel = -((actor->spr.xvel * (actor->spr.pos.Z - dz)) / dist);
}
}
else
{
// make velocity approach the target velocity
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_SPEED_UP))
{
2021-12-26 00:30:29 +00:00
if ((actor->user.track_vel += (locktics << actor->user.vel_rate)) >= actor->user.vel_tgt)
{
2021-12-26 00:30:29 +00:00
actor->user.track_vel = actor->user.vel_tgt;
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_SPEED_UP);
}
// update the real velocity
2021-12-26 00:30:29 +00:00
actor->spr.xvel = (actor->user.track_vel) >> 8;
}
2021-12-27 18:34:06 +00:00
else if (actor->user.Flags & (SPR_SLOW_DOWN))
{
2021-12-26 00:30:29 +00:00
if ((actor->user.track_vel -= (locktics << actor->user.vel_rate)) <= actor->user.vel_tgt)
{
2021-12-26 00:30:29 +00:00
actor->user.track_vel = actor->user.vel_tgt;
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SOBJ_SLOW_DOWN);
}
2021-12-26 00:30:29 +00:00
actor->spr.xvel = (actor->user.track_vel) >> 8;
}
nx = 0;
ny = 0;
2021-12-27 18:34:06 +00:00
if (actor->user.Flags & (SPR_CLIMBING))
{
if (ActorZOfTop(actor) + (ActorSizeZ(actor) >> 2) < actor->user.pos.Z)
{
2021-12-27 17:58:15 +00:00
actor->user.Flags &= ~(SPR_CLIMBING);
actor->spr.zvel = 0;
actor->spr.ang = getangle(tpoint->x - actor->spr.pos.X, tpoint->y - actor->spr.pos.Y);
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
2021-12-27 17:58:15 +00:00
actor->spr.cstat &= ~(CSTAT_SPRITE_YCENTER);
actor->spr.pos.Z += actor->user.pos.Y;
2021-10-30 10:09:34 +00:00
DoActorSetSpeed(actor, SLOW_SPEED);
2021-12-26 00:30:29 +00:00
actor->user.ActorActionFunc = NinjaJumpActionFunc;
actor->user.jump_speed = -650;
DoActorBeginJump(actor);
return true;
}
}
else
{
// calculate a new x and y
nx = MulScale(actor->spr.xvel, bcos(actor->spr.ang), 14);
ny = MulScale(actor->spr.xvel, bsin(actor->spr.ang), 14);
}
nz = 0;
if (actor->spr.zvel)
nz = actor->spr.zvel * locktics;
}
2021-12-26 00:30:29 +00:00
actor->user.coll = move_sprite(actor, nx, ny, nz, actor->user.ceiling_dist, actor->user.floor_dist, 0, locktics);
2021-12-26 00:30:29 +00:00
if (actor->user.coll.type != kHitNone)
{
if (!(actor->user.Flags & (SPR_JUMPING|SPR_FALLING)))
2021-11-05 23:37:16 +00:00
ActorLeaveTrack(actor);
}
return true;
}
#include "saveable.h"
static saveable_code saveable_track_code[] =
{
SAVE_CODE(DoTornadoObject),
SAVE_CODE(DoAutoTurretObject),
};
saveable_module saveable_track =
{
// code
saveable_track_code,
SIZ(saveable_track_code),
// data
nullptr,0
};
END_SW_NS