5810 lines
142 KiB
C++
5810 lines
142 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Actor Engine
|
|
|
|
Primary Author : *****
|
|
Review Status: Reviewed
|
|
|
|
Actors contain:
|
|
1. A path, pglrpt, which is a gl of route points, the nodes of the path.
|
|
2. An event list, pggaev, which is a list of variable size events
|
|
occurring on or between nodes on the path.
|
|
3. A motion match list of sounds applicable during the current action.
|
|
4. State variables
|
|
|
|
See Movie.doc for a detailed description of the functionality.
|
|
|
|
Routes consists of one or more subroutes which can be separately
|
|
translated, deleted, etc.
|
|
|
|
This is a *where* based model of editing. Events occur at specified
|
|
locations along the path. The path is linear between nodes. Each
|
|
node represents the point at which the actor was located in the original
|
|
path recording session. It is not redefined on resizing or motion fill.
|
|
Instead, the event list is edited. The mouse input is however, smoothed
|
|
in movie before actor is called.
|
|
|
|
The one exception to events being executed based on location is the
|
|
aetAdd event, which is executed at its specified time.
|
|
Due to motion fill inheriting the static or non-static path property,
|
|
note that it is (no longer) possible for motion fill to cause an
|
|
actor to not reach the end of a subroute.
|
|
|
|
Misc comments:
|
|
|
|
Actors begin each subroute with the default costume, reimposed
|
|
automatically by the code at every new subroute. Any
|
|
initialization events are then CHANGE events, post applied via
|
|
events in the event stream. When a new subroute is created, the
|
|
new initialization events are copied from the previous subroute,
|
|
if one exists, and from the following subroute if no previous subroute
|
|
exists.
|
|
|
|
Subroutes can be pushed in time by record, rerecord, & sooner/later.
|
|
An actor is represented at at most one location at any given time.
|
|
|
|
All events located <= _rtelCur on the path are executed at the end
|
|
of any given frame. (See FGotoFrame()).
|
|
|
|
Each subroute has an aetAdd event as its first event in the event list.
|
|
|
|
Rotation transformations are absolute, not cumulative. The transformation
|
|
effective at the current frame is stored in _xfrm. There are now two
|
|
fundamentally different types of rotation. The aetRotF represent forward
|
|
rotation and have path orientation applied concurrently. The aetRotH
|
|
apply to static segments only and do not have path orientation post
|
|
applied. _xfrm represents both, and _fUseBmat34 flags (for each frame)
|
|
which transformation is to be applied when the body is positioned.
|
|
The aetRotH events take precedence over aetRotF events if both exist in the
|
|
same frame. This is necessary to allow the aetRotF events to control future
|
|
path rotation while still allowing single frame orientation. The aetRotF
|
|
events nuke the aetRotH events, however, in order to allow the user to
|
|
visibly see the forward rotation currently being applied.
|
|
|
|
Costume changes are per body part set.
|
|
|
|
Freeze inhibits cel advancement.
|
|
Step size events control floating as well as cel advancement in place.
|
|
Each subpath is terminated with a step=0 and a freeze event.
|
|
|
|
Each actor is visible (not hidden, though possibly out of view) between
|
|
aetAdd and aetRem events.
|
|
|
|
The stretch events, aetPull, do not alter step size. Uniform stretching,
|
|
aetSize events, do.
|
|
|
|
Move events are used to translate the path forward of the event by the
|
|
delta-xyz value in the event. These are therefore cumulative, so that
|
|
(unlike rotations), a move at frame 50 followed by a move at frame 20
|
|
will result in frame 50 -to- end-of-subroute being translated by the sum
|
|
of the translations at frames 20 and 50. Tweak events do not translate
|
|
the path, but instead change the current display location only.
|
|
|
|
This code carefully maintains wysiwyg. There are two parts to this.
|
|
One part is the maintenance of exact location, so that if a recorded
|
|
actor follows a specific path (eg, through rather than into doorways)
|
|
that path is retained exactly. A second part is that by <calculating>
|
|
exact locations (unaltered by numerical roundoff), a carefully edited
|
|
and synchronized actor will not end up one frame off in time on replay.
|
|
Both were considered high priority be design.
|
|
|
|
Actors currently proceed to the end of their path, taking a potentially
|
|
partial step at the end. As a result, to maintain wysiwyg, actors
|
|
display at the location of aetActn and aetStep events.
|
|
NOTE: This UI decision probably added more complexity to the
|
|
code than it gained in UI functionality. Had actors always
|
|
displayed only at full step increments, these scenarios would not
|
|
include wysiwyg issues.
|
|
|
|
Frame numbers are stored with the events, but are not guaranteed to be
|
|
correct for frame numbers larger than current frame _nfrmCur.
|
|
FComputeLifetime() guarantees these for all frames, but FComputeLifetime()
|
|
can fail -> the code should not rely on future nfrm values.
|
|
|
|
The route is translated by _dxyzFullRte in class ACTR.
|
|
Each subroute is additionally translated by _dxyzSubRte.
|
|
_dxyzRte combines the overall actor translation for efficiency only.
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
#include "soc.h"
|
|
|
|
ASSERTNAME
|
|
|
|
RTCLASS(ACTR)
|
|
|
|
/***************************************************************************
|
|
|
|
Constructor for ACTR - private.
|
|
|
|
***************************************************************************/
|
|
ACTR::ACTR(void)
|
|
{
|
|
_arid = aridNil;
|
|
_tagTmpl.sid = ksidInvalid;
|
|
_tagSnd.sid = ksidInvalid;
|
|
_nfrmCur = _nfrmFirst = knfrmInvalid;
|
|
_nfrmLast = klwMin;
|
|
_fLifeDirty = fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Destructor for an ACTR
|
|
|
|
***************************************************************************/
|
|
ACTR::~ACTR(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
_CloseTags();
|
|
ReleasePpo(&_pbody);
|
|
ReleasePpo(&_pggaev);
|
|
ReleasePpo(&_pglrpt);
|
|
ReleasePpo(&_pglsmm);
|
|
ReleasePpo(&_ptmpl);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Initialize the actor
|
|
The actor will not yet be grounded to any initial scene frame.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FInit(TAG *ptagTmpl)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(ptagTmpl);
|
|
|
|
_ptmpl = (PTMPL)vptagm->PbacoFetch(ptagTmpl, TMPL::FReadTmpl);
|
|
if (pvNil == _ptmpl)
|
|
return fFalse;
|
|
|
|
AssertPo(_ptmpl, 0);
|
|
_tagTmpl = *ptagTmpl;
|
|
TAGM::DupTag(&_tagTmpl);
|
|
|
|
if (!_FCreateGroups())
|
|
return fFalse;
|
|
|
|
_SetStateRewound();
|
|
if (!_ptmpl->FGetGrfactn(0, &_grfactn))
|
|
return fFalse;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Initialize the transformation matrix & factors
|
|
(Sets the absolute scale = 1, and use the rest orientation)
|
|
|
|
***************************************************************************/
|
|
void ACTR::_InitXfrm(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_InitXfrmRot(&_xfrm.bmat34Fwd);
|
|
_InitXfrmRot(&_xfrm.bmat34Cur);
|
|
_xfrm.aevpull.rScaleX = rOne;
|
|
_xfrm.aevpull.rScaleY = rOne;
|
|
_xfrm.aevpull.rScaleZ = rOne;
|
|
_xfrm.rScaleStep = rOne;
|
|
_xfrm.xaPath = aZero;
|
|
_xfrm.yaPath = aZero;
|
|
_xfrm.zaPath = aZero;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Initialize the rotation part of the transformation
|
|
The rest orientation is now applied post user-rotations so that
|
|
user rotations can be with respect to the actor's coordinate system
|
|
|
|
***************************************************************************/
|
|
void ACTR::_InitXfrmRot(BMAT34 *pbmat34)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pbmat34);
|
|
BrMatrix34Identity(pbmat34);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Allocate a new actor
|
|
The actor is not attached to a scene until SetPscen is called.
|
|
The actor will not have an identifiable ID until SetArid is called.
|
|
Note that an actor which is not yet added to a scene is neither
|
|
grounded in time (_nfrmFirst) nor space (_dxyzFullRte).
|
|
|
|
***************************************************************************/
|
|
PACTR ACTR::PactrNew(TAG *ptagTmpl)
|
|
{
|
|
AssertVarMem(ptagTmpl);
|
|
|
|
PACTR pactr;
|
|
|
|
if ((pactr = NewObj ACTR()) == pvNil)
|
|
return pvNil;
|
|
|
|
if (!pactr->_FInit(ptagTmpl))
|
|
{
|
|
ReleasePpo(&pactr);
|
|
return pvNil;
|
|
}
|
|
|
|
return pactr;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Create the groups _pggaev, _pglrpt and _pglsmm
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FCreateGroups(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (pvNil == (_pggaev = GG::PggNew(size(AEV), kcaevInit, kcbVarAdd)))
|
|
return fFalse;
|
|
|
|
if (pvNil == (_pglrpt = GL::PglNew(size(RPT), kcrptGrow)))
|
|
return fFalse;
|
|
_pglrpt->SetMinGrow(kcrptGrow);
|
|
|
|
if (pvNil == (_pglsmm = GL::PglNew(size(SMM), kcsmmGrow)))
|
|
return fFalse;
|
|
_pglsmm->SetMinGrow(kcsmmGrow);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Set the owning scene and the brender world for the body.
|
|
|
|
***************************************************************************/
|
|
void ACTR::SetPscen(SCEN *pscen)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
AssertVarMem(pscen);
|
|
Assert(_pscen == pvNil || _pscen == pscen, "SetPscen logic error");
|
|
|
|
if (_pscen != pvNil)
|
|
{
|
|
Assert(_pbody != pvNil, "Bad body pointer");
|
|
return;
|
|
}
|
|
|
|
Assert(_pbody == pvNil, "Bad body pointer");
|
|
_pscen = pscen;
|
|
|
|
// Create the body parts
|
|
if (pvNil == (_pbody = _ptmpl->PbodyCreate()))
|
|
return;
|
|
|
|
_pbody->SetBwld(pscen->Pmvie()->Pbwld());
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Hide the Actor and initialize the Actor State Variables
|
|
|
|
***************************************************************************/
|
|
void ACTR::_InitState(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
_Hide();
|
|
_fLifeDirty = fTrue;
|
|
if (_ptmpl != pvNil && _pbody != pvNil)
|
|
_ptmpl->FSetDefaultCost(_pbody);
|
|
_SetStateRewound();
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Set the actor state variables to the rewound position
|
|
|
|
***************************************************************************/
|
|
void ACTR::_SetStateRewound(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
_fFrozen = fFalse;
|
|
_fModeRecord = fFalse;
|
|
_anidCur = _celnCur = 0;
|
|
_rtelCur.irpt = 0;
|
|
_rtelCur.dwrOffset = rZero;
|
|
_rtelCur.dnfrm = -1;
|
|
if (_pglrpt->IvMac() > 0)
|
|
{
|
|
RPT rpt;
|
|
_pglrpt->Get(0, &rpt);
|
|
_xyzCur = rpt.xyz;
|
|
}
|
|
|
|
_iaevCur = 0;
|
|
_iaevActnCur = _iaevAddCur = ivNil;
|
|
_dwrStep = rZero;
|
|
_iaevFrmMin = 0;
|
|
_InitXfrm();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Prepare the actor for display in frame nfrm.
|
|
|
|
nfrm can be any frame number in the movie - it is actor independent.
|
|
NOTE: The state of this actor is expected to be current for frame _nfrmCur
|
|
at the time this routine is called.
|
|
FGotoFrame exits with _nfrmCur == nfrm.
|
|
(A scene level interrogation of all actors should find
|
|
that each actor's _nfrmCur is the same).
|
|
|
|
Note: The scope of fPosition dirty spans multiple calls to _FDoFrm
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FGotoFrame(long nfrm, bool *pfSoundInFrame)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(nfrm, klwMin, klwMax);
|
|
|
|
bool fPositionBody;
|
|
bool fSuccess = fTrue;
|
|
bool fPositionDirty = fFalse;
|
|
bool fQuickMethodValid;
|
|
long iaevT = -1;
|
|
long iaev;
|
|
AEV *paev;
|
|
|
|
if (nfrm == _nfrmCur)
|
|
return fTrue;
|
|
|
|
// Initialization
|
|
if (nfrm < _nfrmCur || _nfrmCur == knfrmInvalid)
|
|
{
|
|
bool fMidPath = FPure(_pggaev->IvMac() > 0 && _iaevCur > 0 &&
|
|
nfrm > _nfrmFirst);
|
|
|
|
// Note: If nfrm < _nfrmFirst, we may be adding
|
|
// a new earliest add event.
|
|
if (nfrm < _nfrmFirst && _fOnStage)
|
|
_Hide();
|
|
|
|
if (nfrm > _nfrmFirst && _nfrmCur != knfrmInvalid )
|
|
{
|
|
Assert(0 < _iaevCur, "Invalid state variables in FGotoFrame()");
|
|
// Optimize if there are no events in the current frame
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevCur - 1);
|
|
if (paev->nfrm < nfrm)
|
|
{
|
|
if (!_FQuickBackupToFrm(nfrm, &fQuickMethodValid))
|
|
return fFalse;
|
|
// Vertical segments can prevent _FQuickBackupToFrm()
|
|
// from being valid. If so, use default method.
|
|
if (fQuickMethodValid)
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
// Will be walking forward from the nearest earlier add event
|
|
// Search backward to find it.
|
|
if (fMidPath)
|
|
{
|
|
for (iaev = _iaevCur - 1; iaev >= 0; iaev--)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
if (paev->aet == aetAdd && paev->nfrm <= nfrm)
|
|
{
|
|
iaevT = iaev;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
_SetStateRewound();
|
|
if (fMidPath)
|
|
{
|
|
AssertIn(iaevT, 0, _pggaev->IvMac());
|
|
_iaevFrmMin = _iaevCur = _iaevAddCur = iaevT;
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevCur);
|
|
_rtelCur = paev->rtel;
|
|
_nfrmCur = paev->nfrm;
|
|
Assert(paev->aet == aetAdd, "Illegal _iaevAddCur state var");
|
|
_rtelCur.dnfrm--;
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
}
|
|
else
|
|
{
|
|
_nfrmCur = LwMin(nfrm, _nfrmFirst);
|
|
}
|
|
_ptmpl->FSetDefaultCost(_pbody);
|
|
}
|
|
else
|
|
{
|
|
_nfrmCur++;
|
|
_iaevFrmMin = _iaevCur; // Save 1st event of this frame
|
|
}
|
|
|
|
if (nfrm < _nfrmFirst)
|
|
return fTrue;
|
|
|
|
// Trivial case: no events for this actor
|
|
// _nfrmCur always reflects the movie's current frame
|
|
if (_pggaev->IvMac() == 0)
|
|
{
|
|
_nfrmCur = nfrm;
|
|
return fTrue;
|
|
}
|
|
|
|
while (_nfrmCur <= nfrm)
|
|
{
|
|
fPositionBody = (_nfrmCur == nfrm);
|
|
if (!_FDoFrm(fPositionBody, &fPositionDirty, pfSoundInFrame))
|
|
fSuccess = fFalse;
|
|
|
|
if (nfrm == _nfrmCur)
|
|
break;
|
|
|
|
_nfrmCur++;
|
|
_iaevFrmMin = _iaevCur;
|
|
AssertIn(_iaevActnCur, -1, _pggaev->IvMac());
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Backup To a smaller frame. (Optimization)
|
|
Return *pfQuickMethodValid fTrue on success.
|
|
Return *pfQuickMethodValid fFalse to if this method invalid here.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FQuickBackupToFrm(long nfrm, bool *pfQuickMethodValid)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pfQuickMethodValid);
|
|
Assert(nfrm < _nfrmCur, "Illegal call to _FBackupToFrm");
|
|
|
|
long ifrm;
|
|
RTEL rtelT;
|
|
XYZ xyzOld = _xyzCur;
|
|
XYZ xyzT;
|
|
long dnfrm = _nfrmCur - nfrm;
|
|
|
|
#ifdef DEBUG
|
|
AEV *paev;
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevCur - 1);
|
|
Assert(paev->nfrm < nfrm, "Invalid Call to _FQuickBackupToFrm()") ;
|
|
#endif //DEBUG
|
|
|
|
// Don't try to back up during frames beyond the actor's lifetime
|
|
if (nfrm >= _nfrmLast)
|
|
{
|
|
// GotoFrame() must always exit with _nfrmCur being valid
|
|
// Otherwise, edits will be located at incorrect frames
|
|
_nfrmCur = nfrm;
|
|
#ifdef BUG1906
|
|
_rtelCur.dnfrm -= dnfrm;
|
|
#else //!BUG1906
|
|
_rtelCur.dnfrm--;
|
|
#endif //!BUG1906
|
|
return fTrue;
|
|
}
|
|
|
|
// There are no events between here and the destination frame
|
|
// _iaevFrmMin need not change
|
|
|
|
// Walk to the destination location
|
|
// Set the cel of the action
|
|
for (ifrm = _nfrmCur - 1; ifrm >= nfrm; ifrm--)
|
|
{
|
|
if (!_FGetRtelBack(&_rtelCur, fTrue))
|
|
goto LFail;
|
|
}
|
|
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
_nfrmCur = nfrm;
|
|
|
|
if (dnfrm > 1 || xyzOld.dxr != _xyzCur.dxr || xyzOld.dzr != _xyzCur.dzr)
|
|
{
|
|
// Check for transitions to vertical motion
|
|
if (!_FGetRtelBack(&rtelT, fFalse))
|
|
goto LFail;
|
|
|
|
_GetXyzFromRtel(&rtelT, &xyzT);
|
|
|
|
if (_xyzCur.dxr == xyzT.dxr && _xyzCur.dzr == xyzT.dzr)
|
|
{
|
|
// Vertical motion next (backing up)
|
|
// -> Require _fUseBmat34Cur == fTrue, but bmat34Cur is not
|
|
// yet computed.
|
|
// -> Quick backup insufficient.
|
|
*pfQuickMethodValid = fFalse;
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
// Send motion match sounds to Msq to play
|
|
if (!(_pscen->GrfScen() & fscenSounds) && (nfrm <= _nfrmLast) && _fOnStage)
|
|
_FEnqueueSmmInMsq(); // Ignore failure
|
|
|
|
// Position the actor
|
|
_PositionBody(&_xyzCur);
|
|
if (_fOnStage)
|
|
{
|
|
if (!_ptmpl->FSetActnCel(_pbody, _anidCur, _celnCur, pvNil))
|
|
goto LFail;
|
|
}
|
|
*pfQuickMethodValid = fTrue;
|
|
return fTrue;
|
|
|
|
LFail:
|
|
*pfQuickMethodValid = fFalse;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Compute xyz for one step backwards from the current location, as per
|
|
state variables
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FGetRtelBack(RTEL *prtel, bool fUpdateStateVar)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(prtel);
|
|
|
|
long celnSav = _celnCur;
|
|
long nfrmSav = _nfrmCur;
|
|
RTEL rtelSav = _rtelCur;
|
|
AEV *paev;
|
|
BRS dwrStep;
|
|
RTEL rtelAdd;
|
|
BRS dwrT;
|
|
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevAddCur);
|
|
rtelAdd = paev->rtel;
|
|
|
|
if (!_fFrozen)
|
|
_celnCur--;
|
|
if (rZero == _dwrStep ||
|
|
(rtelAdd.irpt == _rtelCur.irpt && rtelAdd.dwrOffset == _rtelCur.dwrOffset))
|
|
{
|
|
_nfrmCur--;
|
|
_rtelCur.dnfrm--;
|
|
goto LEnd;
|
|
}
|
|
|
|
// Set the location to display this actor
|
|
if (!_FGetDwrPlay(&dwrStep))
|
|
goto LFail;
|
|
|
|
dwrT = BrsSub(_rtelCur.dwrOffset, dwrStep);
|
|
|
|
if (dwrT >= rZero)
|
|
_rtelCur.dwrOffset = dwrT;
|
|
else
|
|
{
|
|
RPT rpt;
|
|
while(dwrT < rZero)
|
|
{
|
|
if (_rtelCur.irpt <= 0)
|
|
{
|
|
Bug("Corrupted event list");
|
|
_rtelCur.irpt = 0;
|
|
dwrT = rZero;
|
|
break;
|
|
}
|
|
_rtelCur.irpt--;
|
|
_pglrpt->Get(_rtelCur.irpt, &rpt);
|
|
dwrT = BrsAdd(rpt.dwr, dwrT);
|
|
}
|
|
_rtelCur.dwrOffset = dwrT;
|
|
}
|
|
_nfrmCur--;
|
|
|
|
LEnd:
|
|
*prtel = _rtelCur;
|
|
if (!fUpdateStateVar)
|
|
{
|
|
_celnCur = celnSav;
|
|
_nfrmCur = nfrmSav;
|
|
_rtelCur = rtelSav;
|
|
}
|
|
return fTrue;
|
|
|
|
LFail:
|
|
_celnCur = celnSav;
|
|
_nfrmCur = nfrmSav;
|
|
_rtelCur = rtelSav;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Update the internal actor state variables to frame _nfrmCur.
|
|
Ready the actor to display in frame _nfrmCur if fPositionBody is fTrue.
|
|
Sets *pfPositionDirty if a change is encountered.
|
|
Clears *pfPositionDirty after updating Brender.
|
|
|
|
Assumes previous frame correctly rendered.
|
|
Note: PositionBody is delayed until after Events are processed - Tweak,
|
|
Actn, Step and Transform events all can alter the location or orientation.
|
|
Also, new Action Events precede Cel Positioning
|
|
|
|
Note: The scope of *pfPositionDirty spans multiple _FDoFrm calls
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FDoFrm(bool fPositionBody, bool *pfPositionDirty, bool *pfSoundInFrame)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pfPositionDirty);
|
|
|
|
AEV aev;
|
|
BRS dwr;
|
|
long iaev;
|
|
long iaevAdd;
|
|
bool fEndRoute;
|
|
XYZ xyzOld = _xyzCur;
|
|
bool fFreezeThisCel = _fFrozen;
|
|
bool fEndSubEvents = fFalse;
|
|
bool fSuccess = fTrue;
|
|
bool fAdvanceCel;
|
|
|
|
// Obtain distance to move. This may be shortened on encountering
|
|
// a aetActn event later in this same frame.
|
|
fSuccess = _FGetDwrPlay(&dwr);
|
|
_AdvanceRtel(dwr, &_rtelCur, _iaevCur, _nfrmCur, &fEndRoute);
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
|
|
// Use the pre-path rotation matrix if the actor moves
|
|
// so that the path orientation can later be post applied.
|
|
_fUseBmat34Cur = (dwr == rZero ||
|
|
(xyzOld.dxr == _xyzCur.dxr && xyzOld.dzr == _xyzCur.dzr));
|
|
|
|
// Locate the next Add event before entering the next loop
|
|
// Add events are executed when their absolute frame number == _nfrmCur
|
|
if (!_fModeRecord)
|
|
{
|
|
iaevAdd = _iaevCur - 1;
|
|
while(_FFindNextAevAet(aetAdd, iaevAdd + 1, &iaevAdd))
|
|
{
|
|
if (!_FIsAddNow(iaevAdd))
|
|
break;
|
|
|
|
// Add is now
|
|
_iaevCur = iaevAdd;
|
|
if (!_FDoAevCur())
|
|
return fFalse;
|
|
|
|
*pfPositionDirty = fTrue;
|
|
}
|
|
|
|
// Process any events through a dwr step size, unless an aetActn event
|
|
// shortens that distance.
|
|
// An aetActn Event will change _rtelCur at the time it is executed
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet == aetAdd)
|
|
{
|
|
if (!_FIsAddNow(iaev))
|
|
break;
|
|
|
|
// Add is now
|
|
if (!_FDoAevCur())
|
|
return fFalse;
|
|
|
|
*pfPositionDirty = fTrue;
|
|
continue;
|
|
}
|
|
|
|
if (aev.rtel > _rtelCur)
|
|
break;
|
|
|
|
if (aetRotF == aev.aet || aetSize == aev.aet ||
|
|
aetPull == aev.aet || aetRotH == aev.aet || aetMove == aev.aet)
|
|
{
|
|
// The xyz position is not necessarily changing.
|
|
// Specifically enforce Brender updating
|
|
*pfPositionDirty = fTrue;
|
|
}
|
|
else if ((aetActn == aev.aet) || (aetAdd == aev.aet))
|
|
{
|
|
//Do not increment _celnCur before displaying actor on new entrance/action
|
|
fFreezeThisCel = fTrue;
|
|
*pfPositionDirty = fTrue;
|
|
}
|
|
|
|
if (pfSoundInFrame != pvNil && aev.aet == aetSnd && fPositionBody)
|
|
*pfSoundInFrame = fTrue;
|
|
|
|
// Non-motion match sounds cannot depend on playing here as
|
|
// there might not be a sound event at the ending frame.
|
|
// Therefore _FDoAevCur() does not enqueue motion match sounds.
|
|
// It enters mm-snds in the smm. _FDoAevCur() enqueues non-mm snds.
|
|
if (aev.aet != aetSnd ||
|
|
(fPositionBody && !(_pscen->GrfScen() & fscenSounds)))
|
|
{
|
|
// Play non sounds
|
|
// Play sounds if this is the final frame (mm or non mm)
|
|
if (!_FDoAevCur())
|
|
fSuccess = fFalse;
|
|
}
|
|
else
|
|
{
|
|
// Motion match sounds must be entered in the smm
|
|
// whether this is the final frame or not
|
|
AEVSND aevsnd;
|
|
_pggaev->Get(iaev, &aevsnd);
|
|
if (aevsnd.celn != smmNil)
|
|
{
|
|
if (!_FDoAevCur())
|
|
fSuccess = fFalse;
|
|
}
|
|
else
|
|
{
|
|
// Skip the current sound event
|
|
_iaevCur++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fEndSubEvents = _FIsDoneAevSub(_iaevCur, _rtelCur);
|
|
fAdvanceCel = (!fFreezeThisCel && !(fEndRoute && fEndSubEvents));
|
|
|
|
if (fAdvanceCel)
|
|
{
|
|
_celnCur++;
|
|
if (_ccelCur > 1)
|
|
*pfPositionDirty = fTrue;
|
|
}
|
|
|
|
// Force Brender to update if the xyz position has changed
|
|
if (xyzOld != _xyzCur)
|
|
*pfPositionDirty = fTrue;
|
|
|
|
// Position even if hidden for clipping region detection
|
|
// fPositionBody avoids extraneous positioning on intermed frames
|
|
if (fPositionBody)
|
|
{
|
|
// Enqueue the motion match sounds from the smm. Ie, enter in the msq
|
|
if (!(_pscen->GrfScen() & fscenSounds) && (_nfrmCur <= _nfrmLast) && _fOnStage)
|
|
_FEnqueueSmmInMsq();
|
|
|
|
if (*pfPositionDirty)
|
|
{
|
|
_PositionBody(&_xyzCur);
|
|
*pfPositionDirty = fFalse;
|
|
}
|
|
|
|
// Position the actor in the next cel
|
|
// Do not do so if at the "end of route" & "end of events"
|
|
if (_fOnStage)
|
|
{
|
|
if (!_ptmpl->FSetActnCel(_pbody, _anidCur, _celnCur, pvNil))
|
|
fSuccess = fFalse;
|
|
}
|
|
}
|
|
else if (*pfPositionDirty)
|
|
{
|
|
BMAT34 bmat34;
|
|
_MatrixRotUpdate(&_xyzCur, &bmat34);
|
|
}
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
FReplayFrame : Replays the sound for the current frame
|
|
-> Re-enqueues sounds for the current frame
|
|
grfscen - Things that are supposed to be played right now.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FReplayFrame(long grfscen)
|
|
{
|
|
AssertThis(0);
|
|
|
|
AEV aev;
|
|
long iaev;
|
|
|
|
// Check if there is anything to do
|
|
if (!(grfscen & fscenSounds) || !_fOnStage)
|
|
return fTrue;
|
|
|
|
for (iaev = _iaevFrmMin; iaev < _iaevCur; iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet != aetSnd)
|
|
continue;
|
|
|
|
if (!_FDoAevCore(iaev))
|
|
return fFalse;
|
|
}
|
|
|
|
// Also send any motion match sounds to msq to play
|
|
return _FEnqueueSmmInMsq();
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
_FGetStatic : Returns true/false on success/failure
|
|
Returns the bool value in *pfStatic
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FGetStatic(long anid, bool *pfStatic)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, klwMax);
|
|
AssertVarMem(pfStatic);
|
|
|
|
ulong grfactn;
|
|
|
|
if (!_ptmpl->FGetGrfactn(anid, &grfactn))
|
|
return fFalse;
|
|
|
|
if (ivNil == _iaevActnCur)
|
|
{
|
|
*pfStatic = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
*pfStatic = FPure(grfactn & factnStatic);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Is the actor in the last active frame of the subroute?
|
|
|
|
***************************************************************************/
|
|
bool ACTR:: _FIsDoneAevSub(long iaev, RTEL rtel)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaev, 0, _pggaev->IvMac() + 1);
|
|
|
|
AEV aev;
|
|
if (iaev == _pggaev->IvMac())
|
|
return fTrue;
|
|
|
|
for ( ; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet == aetAdd)
|
|
return fTrue;
|
|
if (aev.rtel > rtel)
|
|
return fFalse;
|
|
// Event at current frame. Keep looking.
|
|
}
|
|
|
|
// No further events exist at future frames for this subpath
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Is the actor at an AddOnStage event which can be applied now?
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FIsAddNow(long iaev)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaev, 0, _pggaev->IvMac());
|
|
|
|
AEV *paev;
|
|
|
|
if (_fLifeDirty)
|
|
{
|
|
// Update the nfrm values
|
|
if (!_FComputeLifetime())
|
|
return fFalse;
|
|
}
|
|
|
|
Assert(_pggaev->Cb(iaev) == size(AEVADD), "Corrupt event list");
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
|
|
if (paev->nfrm <= _nfrmCur)
|
|
return fTrue;
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Return functional (sized) step size when playing
|
|
Zero is a valid return value
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FGetDwrPlay(BRS *pdwr)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pdwr);
|
|
|
|
*pdwr = rZero;
|
|
|
|
if (kdwrNil == _dwrStep)
|
|
{
|
|
if (!_ptmpl->FGetDwrActnCel(_anidCur, _celnCur, pdwr))
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
*pdwr = _dwrStep;
|
|
}
|
|
|
|
*pdwr = BrsMul(*pdwr, _xfrm.rScaleStep);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Return functional (sized) step size when recording.
|
|
The returned size is expected to be authored > 0
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FGetDwrRecord(BRS *pdwr)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pdwr);
|
|
|
|
*pdwr = rZero;
|
|
|
|
if (!_ptmpl->FGetDwrActnCel(_anidCur, _celnCur, pdwr))
|
|
return fFalse;
|
|
|
|
*pdwr = BrsMul(*pdwr, _xfrm.rScaleStep);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Execute the current event.
|
|
State Variables are updated.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FDoAevCur(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (!_FDoAevCore(_iaevCur))
|
|
return fFalse;
|
|
|
|
_iaevCur++;
|
|
Assert(_iaevCur <= _pggaev->IvMac(), "_iaevCur bug");
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Execute the event iaev.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FDoAevCore(long iaev)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaev, 0, _pggaev->IvMac());
|
|
|
|
AEV aev;
|
|
COST cost;
|
|
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.nfrm != _nfrmCur)
|
|
{
|
|
aev.nfrm = _nfrmCur;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
AssertIn(aev.aet, 0, aetLim);
|
|
|
|
switch(aev.aet)
|
|
{
|
|
case aetActn:
|
|
AEVACTN aevactn;
|
|
ulong grfactn;
|
|
|
|
_pggaev->Get(iaev, &aevactn);
|
|
|
|
//Empty the motion match sound list if the action <changed>
|
|
if (aevactn.anid != _anidCur)
|
|
_pglsmm->FSetIvMac(0);
|
|
|
|
if (!_ptmpl->FSetActnCel(_pbody, aevactn.anid, aevactn.celn))
|
|
{
|
|
return fFalse;
|
|
}
|
|
_celnCur = aevactn.celn;
|
|
_anidCur = aevactn.anid;
|
|
if (!_ptmpl->FGetCcelActn(_anidCur, &_ccelCur)) // Cache the cel count
|
|
return fFalse;
|
|
_iaevActnCur = iaev;
|
|
|
|
// Force the location to the step event
|
|
// Avoids incorrect event ordering on static segments
|
|
_rtelCur = aev.rtel;
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
if (!_ptmpl->FGetGrfactn(_anidCur, &grfactn))
|
|
{
|
|
return fFalse;
|
|
}
|
|
_grfactn = grfactn;
|
|
break;
|
|
|
|
case aetAdd:
|
|
AEVADD aevadd;
|
|
RPT rpt;
|
|
// Save old costume in case of error
|
|
if (!cost.FGet(_pbody))
|
|
return fFalse;
|
|
// Invoke the default costume/orientation
|
|
if (!_ptmpl->FSetDefaultCost(_pbody))
|
|
return fFalse;
|
|
// Put the actor on stage. Set up models.
|
|
if (!_ptmpl->FSetActnCel(_pbody, _anidCur, _celnCur))
|
|
{
|
|
cost.Set(_pbody); // restore old costume
|
|
return fFalse;
|
|
}
|
|
|
|
//Empty the motion match sound list
|
|
_pglsmm->FSetIvMac(0);
|
|
|
|
// Set the translation for the subroute
|
|
_pggaev->Get(iaev, &aevadd);
|
|
_dxyzSubRte.dxr = aevadd.dxr;
|
|
_dxyzSubRte.dyr = aevadd.dyr;
|
|
_dxyzSubRte.dzr = aevadd.dzr;
|
|
_UpdateXyzRte();
|
|
|
|
// Load the initial orientation
|
|
_InitXfrm();
|
|
_LoadAddOrien(&aevadd);
|
|
|
|
// Set state variables
|
|
_iaevFrmMin = _iaevAddCur = iaev;
|
|
_rtelCur = aev.rtel;
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
_pglrpt->Get(aev.rtel.irpt, &rpt);
|
|
|
|
// Show the actor
|
|
if (!_fOnStage)
|
|
_pbody->Show();
|
|
_fOnStage = fTrue;
|
|
break;
|
|
|
|
case aetRem:
|
|
// Exit the actor from the stage
|
|
_Hide();
|
|
break;
|
|
|
|
case aetCost:
|
|
AEVCOST aevcost;
|
|
_pggaev->Get(iaev, &aevcost);
|
|
if (aevcost.fCmtl)
|
|
{
|
|
PCMTL pcmtl = _ptmpl->PcmtlFetch(aevcost.cmid);
|
|
if (pvNil == pcmtl)
|
|
return fFalse;
|
|
_pbody->SetPartSetCmtl(pcmtl);
|
|
ReleasePpo(&pcmtl);
|
|
}
|
|
else
|
|
{
|
|
PMTRL pmtrl;
|
|
pmtrl = (PMTRL)vptagm->PbacoFetch(&aevcost.tag, MTRL::FReadMtrl);
|
|
if (pvNil == pmtrl)
|
|
return fFalse;
|
|
_pbody->SetPartSetMtrl(aevcost.ibset, pmtrl);
|
|
ReleasePpo(&pmtrl);
|
|
}
|
|
break;
|
|
|
|
case aetRotF:
|
|
// Actors are xformed in _FDoFrm, Rotate or Scale
|
|
_pggaev->Get(iaev, &_xfrm.bmat34Fwd);
|
|
_fUseBmat34Cur = fFalse;
|
|
break;
|
|
|
|
case aetRotH:
|
|
// Actors are xformed in _FDoFrm, Rotate or Scale
|
|
_pggaev->Get(iaev, &_xfrm.bmat34Cur);
|
|
_fUseBmat34Cur = fTrue;
|
|
break;
|
|
|
|
case aetPull:
|
|
// Actors are xformed in _FDoFrm, Rotate or Scale
|
|
_pggaev->Get(iaev, &_xfrm.aevpull);
|
|
break;
|
|
|
|
case aetSize:
|
|
// Actors are xformed in _FDoFrm, Rotate or Scale
|
|
_pggaev->Get(iaev, &_xfrm.rScaleStep);
|
|
break;
|
|
|
|
case aetStep: //Exists for timing control (eg walk in place)
|
|
_pggaev->Get(iaev, &_dwrStep);
|
|
// Force the location to the step event
|
|
// Avoids incorrect event ordering on static segments
|
|
if (rZero == _dwrStep)
|
|
{
|
|
_rtelCur = aev.rtel;
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
}
|
|
break;
|
|
|
|
case aetFreeze:
|
|
long fFrozen; //_fFrozen is a bit
|
|
_pggaev->Get(iaev, &fFrozen);
|
|
_fFrozen = FPure(fFrozen);
|
|
break;
|
|
|
|
case aetTweak:
|
|
if (aev.rtel.dnfrm == _rtelCur.dnfrm)
|
|
_pggaev->Get(iaev, &_xyzCur);
|
|
// The actual locating of the actor is done in _FDoFrm or FTweakRoute
|
|
break;
|
|
|
|
case aetSnd:
|
|
// Enqueue non-mm sounds
|
|
if (!_FEnqueueSnd(iaev)) // Ignore failure
|
|
return fFalse;
|
|
break;
|
|
|
|
case aetMove:
|
|
XYZ dxyz;
|
|
_pggaev->Get(iaev, &dxyz);
|
|
_dxyzSubRte.dxr = BrsAdd(dxyz.dxr, _dxyzSubRte.dxr);
|
|
_dxyzSubRte.dyr = BrsAdd(dxyz.dyr, _dxyzSubRte.dyr);
|
|
_dxyzSubRte.dzr = BrsAdd(dxyz.dzr, _dxyzSubRte.dzr);
|
|
_UpdateXyzRte();
|
|
break;
|
|
|
|
default:
|
|
Bug("Unimplemented actor event");
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Add the specified event to the event list at the current frame, and
|
|
execute the event
|
|
|
|
NOTE: Overwrites events of the same type if appropriate
|
|
NOTE: If the new action event is the same as the most recent
|
|
previous action, the new action is still inserted. This avoids
|
|
repositioning the actor based on a past Set Action.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FAddDoAev(long aetNew, long cbNew, void *pvVar)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(aetNew, 0, aetLim);
|
|
AssertIn(cbNew, 0, 100); //approximate upper bound
|
|
AssertPvCb(pvVar, cbNew);
|
|
Assert(_fOnStage || aetNew == aetAdd,
|
|
"Error! Beginning subroute with no Add Onstage event");
|
|
|
|
AEV aev;
|
|
long iaevNew;
|
|
// Setup fixed part of the gg
|
|
aev.aet = aetNew;
|
|
aev.rtel = _rtelCur;
|
|
aev.nfrm = _nfrmCur;
|
|
|
|
if (!_FInsertAev(_iaevCur, cbNew, pvVar, &aev))
|
|
return fFalse;
|
|
_MergeAev(_iaevFrmMin, _iaevCur, &iaevNew);
|
|
|
|
#ifdef BUG1870
|
|
// REVIEW *****: V2.0
|
|
// Though the only situation in which this arises is aetMove,
|
|
// it might be that the FDoAevCore() should be called before
|
|
// merging events (so that any cumulative changes get executed
|
|
// only once, without requiring this special casing of aetMove.
|
|
if (aetMove == aetNew)
|
|
{
|
|
// Skip the "do" of the FAddDoAev().
|
|
// The 'Do' part currently merges events before
|
|
// executing them, which in this case would cause
|
|
// any existing same frame translaton to be added
|
|
// to the state variables twice.
|
|
// Instead, adjust state var translation here.
|
|
_dxyzSubRte.dxr = BrsAdd(((XYZ *)pvVar)->dxr, _dxyzSubRte.dxr);
|
|
_dxyzSubRte.dyr = BrsAdd(((XYZ *)pvVar)->dyr, _dxyzSubRte.dyr);
|
|
_dxyzSubRte.dzr = BrsAdd(((XYZ *)pvVar)->dzr, _dxyzSubRte.dzr);
|
|
}
|
|
else if (!_FDoAevCore(iaevNew))
|
|
return fFalse;
|
|
#else //!BUG1870
|
|
if (!_FDoAevCore(iaevNew))
|
|
return fFalse;
|
|
#endif //!BUG1870
|
|
|
|
if (_iaevCur == iaevNew)
|
|
_iaevCur++;
|
|
|
|
_pscen->MarkDirty();
|
|
|
|
Assert(aetNew != aetActn || _iaevActnCur == iaevNew,
|
|
"_iaevActnCur not up to date");
|
|
AssertIn(_iaevCur, 0, _pggaev->IvMac() + 1);
|
|
Assert(!(_pggaev->IvMac() == 1 && _nfrmFirst != _nfrmCur), "check case");
|
|
|
|
if (_nfrmCur < _nfrmFirst)
|
|
{
|
|
_pscen->InvalFrmRange();
|
|
_nfrmFirst = _nfrmCur;
|
|
}
|
|
|
|
if (_nfrmCur > _nfrmLast)
|
|
{
|
|
_pscen->InvalFrmRange();
|
|
_nfrmLast = _nfrmCur;
|
|
}
|
|
|
|
AssertThis(fobjAssertFull);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Merge event iaevNew among preceding events, beginning with
|
|
event iaevFirst (which is usually the first event in the frame).
|
|
Unless equal to pvNil, Return *piaevRtn to be the final index of the
|
|
merged event
|
|
NOTE: Add events (only) are not merged.
|
|
|
|
***************************************************************************/
|
|
void ACTR::_MergeAev(long iaevFirst, long iaevNew, long *piaevRtn)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaevFirst, 0, _pggaev->IvMac());
|
|
AssertIn(iaevNew, iaevFirst, _pggaev->IvMac());
|
|
AssertNilOrVarMem(piaevRtn);
|
|
|
|
AEV aev;
|
|
AEV aevNew;
|
|
void *pvVar;
|
|
long cbVar;
|
|
long iaev;
|
|
BMAT34 bmat34;
|
|
|
|
if ((_pggaev->IvMac() < iaevFirst) || (iaevFirst == iaevNew))
|
|
{
|
|
if (pvNil != piaevRtn)
|
|
*piaevRtn = iaevNew;
|
|
return;
|
|
}
|
|
|
|
_pggaev->GetFixed(iaevNew, &aevNew);
|
|
|
|
//
|
|
// Check if Aev is in the list already
|
|
//
|
|
for (iaev = iaevFirst; iaev < iaevNew; iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
|
|
if ((aev.aet != aevNew.aet) && (aev.aet != aetRem) && (aev.aet != aetRotH))
|
|
continue;
|
|
|
|
switch(aev.aet)
|
|
{
|
|
case aetRem:
|
|
if (aevNew.aet == aetAdd && aevNew.nfrm != aev.nfrm)
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaevNew--;
|
|
iaev--;
|
|
}
|
|
break;
|
|
|
|
case aetAdd:
|
|
// We cannot remove events associated with a same-frame Add event
|
|
// or we will not retain costume etc information to propogate
|
|
// forward (eg drag single pt actor offstage & roll call back)
|
|
continue;
|
|
break;
|
|
|
|
case aetMove: // Move is accumulation of previous moves
|
|
XYZ dxyz;
|
|
XYZ dxyzNew;
|
|
_pggaev->Get(iaev, &dxyz);
|
|
_pggaev->Get(iaevNew, &dxyzNew);
|
|
// Note: Merging moves should not alter state variables!
|
|
#ifndef BUG1870
|
|
// Remove these lines of code
|
|
_dxyzSubRte.dxr = BrsSub(_dxyzSubRte.dxr, dxyz.dxr);
|
|
_dxyzSubRte.dyr = BrsSub(_dxyzSubRte.dyr, dxyz.dyr);
|
|
_dxyzSubRte.dzr = BrsSub(_dxyzSubRte.dzr, dxyz.dzr);
|
|
#endif //!BUG1870
|
|
dxyz.dxr = BrsAdd(dxyz.dxr, dxyzNew.dxr);
|
|
dxyz.dyr = BrsAdd(dxyz.dyr, dxyzNew.dyr);
|
|
dxyz.dzr = BrsAdd(dxyz.dzr, dxyzNew.dzr);
|
|
_pggaev->Put(iaev, &dxyz);
|
|
goto LDeleteNew;
|
|
break;
|
|
|
|
case aetCost:
|
|
AEVCOST aevcost;
|
|
AEVCOST aevcostNew;
|
|
|
|
// Check that the body parts match
|
|
_pggaev->Get(iaev, &aevcost);
|
|
_pggaev->Get(iaevNew, &aevcostNew);
|
|
|
|
if (aevcost.ibset != aevcostNew.ibset)
|
|
continue;
|
|
|
|
if (aev.rtel != aevNew.rtel)
|
|
goto LDeleteOld;
|
|
_pggaev->Put(iaev, &aevcostNew);
|
|
goto LDeleteNew;
|
|
break;
|
|
|
|
case aetSnd:
|
|
AEVSND aevsnd;
|
|
AEVSND aevsndNew;
|
|
long ismm;
|
|
SMM *psmm;
|
|
// Check that the sound types match
|
|
_pggaev->Get(iaev, &aevsnd);
|
|
_pggaev->Get(iaevNew, &aevsndNew);
|
|
if (MSND::SqnActr(aevsnd.sty, _arid) != MSND::SqnActr(aevsndNew.sty, _arid))
|
|
continue;
|
|
// Queued sounds need to have multiple events reside in a single frame
|
|
if (aevsndNew.fQueue)
|
|
continue;
|
|
// Non queued sounds need to replace queued sounds of the same type
|
|
// First, the _pggsmm needs to be updated
|
|
for (ismm = 0; ismm < _pglsmm->IvMac(); ismm++)
|
|
{
|
|
psmm = (SMM *)_pglsmm->QvGet(ismm);
|
|
if (psmm->aevsnd.sty == aevsnd.sty && aevsnd.celn == psmm->aevsnd.celn)
|
|
{
|
|
_pglsmm->Delete(ismm);
|
|
break;
|
|
}
|
|
}
|
|
_RemoveAev(iaev);
|
|
iaevNew--;
|
|
iaev--;
|
|
continue;
|
|
break;
|
|
|
|
case aetRotH:
|
|
if (aevNew.aet == aetRotF && aevNew.nfrm == aev.nfrm)
|
|
{
|
|
// Forward rotations must get rid of tweak rotations in the current frame
|
|
_RemoveAev(iaev);
|
|
iaevNew--;
|
|
iaev--;
|
|
}
|
|
if (aevNew.aet != aetRotH)
|
|
continue;
|
|
if (aev.rtel != aevNew.rtel)
|
|
goto LDeleteOld;
|
|
_pggaev->Get(iaevNew, &bmat34);
|
|
_pggaev->Put(iaev, &bmat34);
|
|
goto LDeleteNew;
|
|
break;
|
|
|
|
case aetRotF:
|
|
// New == old == forward-rotate
|
|
// Need to replace the old rotation, but continue on to remove tweak-rotations
|
|
_RemoveAev(iaev);
|
|
iaevNew--;
|
|
iaev--;
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
if (aev.rtel != aevNew.rtel)
|
|
{
|
|
goto LDeleteOld;
|
|
}
|
|
pvVar = _pggaev->QvGet(iaevNew, &cbVar);
|
|
Assert(cbVar == _pggaev->Cb(iaev), "Wrong Var size");
|
|
_pggaev->Put(iaev, pvVar);
|
|
goto LDeleteNew;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Leave it where it is. No match found.
|
|
//
|
|
if (pvNil != piaevRtn)
|
|
*piaevRtn = iaevNew;
|
|
|
|
return;
|
|
|
|
LDeleteNew:
|
|
if (pvNil != piaevRtn)
|
|
{
|
|
*piaevRtn = iaev;
|
|
AssertIn(iaev, iaevFirst, iaevNew);
|
|
}
|
|
_RemoveAev(iaevNew);
|
|
return;
|
|
|
|
LDeleteOld:
|
|
_RemoveAev(iaev);
|
|
iaevNew--;
|
|
if (pvNil != piaevRtn)
|
|
*piaevRtn = iaevNew;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Add (or replace) an action
|
|
Add the event to the event list
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FSetActionCore(long anid, long celn, bool fFreeze)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, klwMax);
|
|
AssertIn(celn, klwMin, klwMax);
|
|
|
|
bool fStatic;
|
|
bool fNewAction = fFalse;
|
|
AEVACTN aevactn;
|
|
long anidPrev = _anidCur;
|
|
long iaevMin;
|
|
|
|
long cbVar = kcbVarActn + kcbVarFreeze + (kcbVarStep * 2);
|
|
if (!_pggaev->FEnsureSpace( 4, cbVar, fgrpNil))
|
|
return fFalse;
|
|
|
|
if (!_FGetStatic(anid, &fStatic))
|
|
return fFalse;
|
|
|
|
// If the action is changing:
|
|
// Remove all motion match sounds of the previous action
|
|
// Query the template and insert new sound events
|
|
if (_anidCur != anid)
|
|
{
|
|
fNewAction = fTrue;
|
|
if (!_FRemoveAevMm(anid))
|
|
return fFalse;
|
|
}
|
|
|
|
// var part of gg
|
|
aevactn.anid = anid;
|
|
aevactn.celn = celn;
|
|
|
|
// Add this action to the event list
|
|
iaevMin = (_iaevFrmMin == _iaevAddCur) ? _iaevFrmMin + 1 : _iaevFrmMin;
|
|
_PrepActnFill(iaevMin, _anidCur, anid,
|
|
faetTweak | faetFreeze | faetActn);
|
|
|
|
if (!_FAddDoAev(aetActn, kcbVarActn, &aevactn))
|
|
return fFalse;
|
|
|
|
// If the action is changing:
|
|
// Query the template and insert new default motion match sound events
|
|
if (fNewAction)
|
|
{
|
|
if (!_FAddAevDefMm(anid))
|
|
return fFalse;
|
|
}
|
|
|
|
if (_nfrmCur != _nfrmLast)
|
|
{
|
|
_fLifeDirty = fTrue;
|
|
_pscen->InvalFrmRange();
|
|
}
|
|
|
|
if (!fFreeze && !_FIsDoneAevSub(_iaevCur, _rtelCur))
|
|
{
|
|
if (!_FUnfreeze())
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
if (!_FFreeze())
|
|
return fFalse;
|
|
}
|
|
|
|
// If the actor is in the middle of a static segment, the application of a
|
|
// non-static action is supposed to make the actor start moving forward along
|
|
// the remaining path.
|
|
// The last frame in the subpath must retain its final step=0 event, however.
|
|
if (!fStatic && !_ptmpl->FIsTdt() && !_FIsDoneAevSub(_iaevCur, _rtelCur))
|
|
{
|
|
if (!FSetStep(kdwrNil))
|
|
return fFalse;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Remove an actor from the stage
|
|
NOTE: This is a low level API and has no effect on the event list
|
|
|
|
***************************************************************************/
|
|
void ACTR::_Hide(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (_fOnStage && _pbody != pvNil)
|
|
_pbody->Hide();
|
|
_fOnStage = fFalse;
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Insert a node point *prpt in the route.
|
|
Update *prpt.dwr, and the previous node's dwr.
|
|
On input:
|
|
*prpt.dwr == 0 iff the new node is a subpath terminating node
|
|
*prpt.xyz has the new node coordinates
|
|
dwrPrior is the distance from the previous node to the new one.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FInsertGgRpt(long irpt, RPT *prpt, BRS dwrPrior)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(irpt, 0, _pglrpt->IvMac() + 1);
|
|
AssertNilOrVarMem(prpt);
|
|
|
|
RPT rpt;
|
|
BRS dwrTotal = rZero; // Dist from prev node to node after the inserted node
|
|
|
|
if (!_pglrpt->FInsert(irpt, prpt))
|
|
return fFalse;
|
|
|
|
if (0 < irpt)
|
|
{
|
|
_pglrpt->Get(irpt-1, &rpt);
|
|
dwrTotal = rpt.dwr;
|
|
|
|
// Do not alter end of route dwr's
|
|
if (rZero != rpt.dwr)
|
|
{
|
|
// Adjust the distance from the previous point here
|
|
rpt.dwr = dwrPrior;
|
|
if (rZero == rpt.dwr)
|
|
rpt.dwr = rEps; //Epsilon. Prevent pathological incorrect end-of-path
|
|
_pglrpt->Put(irpt-1, &rpt);
|
|
}
|
|
else Assert(dwrPrior == rZero, "Illegal distance");
|
|
}
|
|
|
|
if (irpt < _pglrpt->IvMac() - 1)
|
|
{
|
|
_pglrpt->Get(irpt+1, &rpt);
|
|
if (rZero != prpt->dwr) // If not at end of subroute
|
|
{
|
|
// Set the distance to the next point in this subroute
|
|
Assert(rZero != dwrTotal, "Overwriting end of route");
|
|
prpt->dwr = BrsSub(dwrTotal, dwrPrior);
|
|
if (rZero >= prpt->dwr)
|
|
prpt->dwr = rEps; //Epsilon. Prevent pathological incorrect end-of-path
|
|
_pglrpt->Put(irpt, prpt);
|
|
}
|
|
}
|
|
else Assert(rZero == prpt->dwr, "Invalid end sub-node dwr");
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Set a Stepsize Event
|
|
Add it to the event list
|
|
|
|
dwrStep == 0 -> Stop the actor from forward motion.
|
|
dwrStep == kdwrNil -> Use the scaled template step size
|
|
Note that this is orthogonal and independent of freezing an actor
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FSetStep(BRS dwrStep)
|
|
{
|
|
AssertThis(0);
|
|
Assert(kdwrNil == dwrStep || rZero <= dwrStep, "Invalid dwrStep argument");
|
|
|
|
if (!_pggaev->FEnsureSpace( 1, kcbVarStep, fgrpNil))
|
|
return fFalse;
|
|
|
|
return _FAddDoAev(aetStep, kcbVarStep, &dwrStep);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Get the new origin for the to-be positioned actor. Always place
|
|
actors on the "floor" (Y = 0)...except 3-D Text actors, which are
|
|
at Y = 1.0 meters.
|
|
|
|
***************************************************************************/
|
|
void ACTR::_GetNewOrigin(BRS *pxr, BRS *pyr, BRS *pzr)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pxr);
|
|
AssertVarMem(pyr);
|
|
AssertVarMem(pzr);
|
|
|
|
BRS xrCam = rZero;
|
|
BRS yrCam = rZero;
|
|
BRS zrCam = kzrDefault;
|
|
BRS zrYon, zrHither;
|
|
BMAT34 bmat34Cam;
|
|
|
|
_pscen->Pbkgd()->GetActorPlacePoint(pxr, pyr, pzr);
|
|
// A (0, 0, 0) place point means that one hasn't been authored yet,
|
|
// so use the old system.
|
|
if (*pxr == rZero && *pyr == rZero && *pzr == rZero)
|
|
{
|
|
_pscen->Pmvie()->Pbwld()->GetCamera(&bmat34Cam, &zrHither, &zrYon);
|
|
|
|
Assert(zrCam < 0, "Non-negative placement value");
|
|
Assert(zrHither >= 0 && zrYon >= 0, "Negative camera values");
|
|
if (BR_ABS(zrCam) > zrYon)
|
|
zrCam = BR_CONST_DIV(BR_ADD(zrYon, zrHither), -2);
|
|
|
|
*pxr = BR_MAC3(xrCam, bmat34Cam.m[0][0], yrCam, bmat34Cam.m[1][0],
|
|
zrCam, bmat34Cam.m[2][0]) + bmat34Cam.m[3][0];
|
|
*pyr = rZero;
|
|
*pzr = BR_MAC3(xrCam, bmat34Cam.m[0][2], yrCam, bmat34Cam.m[1][2],
|
|
zrCam, bmat34Cam.m[2][2]) + bmat34Cam.m[3][2];
|
|
}
|
|
if (_ptmpl->FIsTdt())
|
|
*pyr += BR_SCALAR(10.0); // 1.0 meters
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Add actor on the stage - ie, create a new subroute.
|
|
Add the Add event to the event list
|
|
Nukes existing subroute if obsoleted by current Add.
|
|
|
|
Add the initialization events for costumes, xforms, etc from the
|
|
preceding or subsequent subroute.
|
|
***************************************************************************/
|
|
bool ACTR::FAddOnStageCore(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
BRS xr;
|
|
BRS yr;
|
|
BRS zr;
|
|
long cbVar;
|
|
AEVADD aevadd;
|
|
bool fUpdateFrmRange = fFalse;
|
|
|
|
cbVar = kcbVarAdd + kcbVarActn + kcbVarStep + kcbVarFreeze;
|
|
if (_pggaev->IvMac() == 0)
|
|
{
|
|
if (!_pggaev->FEnsureSpace( 4, cbVar, fgrpNil) ||
|
|
!_pglrpt->FEnsureSpace(1, fgrpNil))
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
cbVar += kcbVarCost + kcbVarRot + kcbVarSize + kcbVarPull;
|
|
if (!_pggaev->FEnsureSpace( 8, cbVar, fgrpNil) ||
|
|
!_pglrpt->FEnsureSpace(1, fgrpNil))
|
|
{
|
|
return fFalse;
|
|
}
|
|
}
|
|
|
|
_GetNewOrigin(&xr, &yr, &zr);
|
|
aevadd.dxr = BrsSub(xr, _dxyzFullRte.dxr);
|
|
aevadd.dyr = BrsSub(yr, _dxyzFullRte.dyr);
|
|
aevadd.dzr = BrsSub(zr, _dxyzFullRte.dzr);
|
|
aevadd.xa = aevadd.za = aZero;
|
|
|
|
// Rotate the actor to be facing the camera
|
|
// NOTE: 3D spletter code uses this also.
|
|
aevadd.ya = _pscen->Pbkgd()->BraRotYCamera();
|
|
|
|
if (_nfrmCur < _nfrmFirst)
|
|
{
|
|
fUpdateFrmRange = fTrue;
|
|
_nfrmFirst = _nfrmCur;
|
|
}
|
|
if (_nfrmCur > _nfrmLast)
|
|
{
|
|
fUpdateFrmRange = fTrue;
|
|
_nfrmLast = _nfrmCur;
|
|
}
|
|
|
|
RPT rptNil = {rZero, rZero, rZero, rZero};
|
|
|
|
if (_fOnStage) // May have walked offstage
|
|
{
|
|
// Delete the remnant subroute (non-inclusive of current frame)
|
|
_DeleteFwdCore(fFalse);
|
|
}
|
|
|
|
_rtelCur.dnfrm = 0;
|
|
_rtelCur.dwrOffset = rZero;
|
|
if (_iaevAddCur < 0)
|
|
_rtelCur.irpt = 0;
|
|
else
|
|
_rtelCur.irpt++;
|
|
|
|
AssertDo(_FInsertGgRpt(_rtelCur.irpt, &rptNil), "Logic error");
|
|
_AdjustAevForRteIns(_rtelCur.irpt, 0);
|
|
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
|
|
if (!_FAddDoAev(aetAdd, kcbVarAdd, &aevadd))
|
|
return fFalse;
|
|
|
|
|
|
// Copy costume and transform events forward if this is
|
|
// the earliest Add event : each subroute needs all
|
|
// initialization events.
|
|
// Note: _iaevCur is already incremented at this point
|
|
if (1 == _iaevCur)
|
|
{
|
|
// The earliest Add. Gather later events and insert them
|
|
if (!_FAddAevFromLater())
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
// Not the earliest Add. Gather earlier events and insert them
|
|
ulong grfaet = faetActn | faetCost | faetPull | faetSize | faetRotF;
|
|
if (!_FAddAevFromPrev(_iaevCur - 1, grfaet))
|
|
return fFalse;
|
|
}
|
|
|
|
_PositionBody(&_xyzCur);
|
|
|
|
if (!FSetStep(rZero))
|
|
return fFalse;
|
|
if (!_FFreeze())
|
|
return fFalse;
|
|
|
|
if (fUpdateFrmRange)
|
|
{
|
|
_pscen->InvalFrmRange();
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Collect events < iaevLim & insert them beginning at event number
|
|
_iaevCur.
|
|
NOTE: The primary complication is to OMIT copying events which can
|
|
never be reached due to static actions (stalling) in the previous
|
|
subroute. Due to initialization now spec'd to occur in every subroute,
|
|
note that only the immediately preceding subroute is of concern.
|
|
NOTE: This is optimized to add only the latest event of each given type.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FAddAevFromPrev(long iaevLim, ulong grfaet)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaevLim, 0, _pggaev->IvMac() + 1);
|
|
|
|
AEV aev;
|
|
AEV *paev;
|
|
AEV aevCur;
|
|
long cb;
|
|
long iaev;
|
|
long iaevAdd;
|
|
long iaevNew;
|
|
long iaevLast;
|
|
bool fPrunedPrevSubrte = fFalse;
|
|
|
|
// Locate the next active (not stalled) region of the subroute
|
|
// Note: Not finding a previous aev is not a failure
|
|
_FFindPrevAevAet(aetAdd, iaevLim, &iaevAdd);
|
|
_FindAevLastSub(iaevAdd, iaevLim, &iaevLast);
|
|
|
|
_pggaev->GetFixed(_iaevCur - 1, &aevCur);
|
|
|
|
// It is more efficient to insert non-costume events backwards
|
|
for (iaev = iaevLast; iaev > iaevAdd; iaev--)
|
|
{
|
|
if (0 == grfaet)
|
|
break;
|
|
|
|
Assert(iaev >= 0, "Logic error");
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (!(grfaet & (1 << aev.aet)) || aev.aet == aetCost)
|
|
continue;
|
|
|
|
// Non-costume events are added only once
|
|
grfaet ^= (1 << aev.aet);
|
|
|
|
// Allocate space
|
|
cb = _pggaev->Cb(iaev);
|
|
aev.rtel = aevCur.rtel;
|
|
// aev.nfrm is set by _FDoAevCore()
|
|
|
|
if (!_FInsertAev(_iaevCur, cb, pvNil, &aev))
|
|
return fFalse;
|
|
|
|
//Insert event
|
|
_pggaev->Put(_iaevCur, _pggaev->QvGet(iaev));
|
|
|
|
// Merge events to avoid duplicates
|
|
_MergeAev(_iaevFrmMin, _iaevCur, &iaevNew);
|
|
|
|
if (!_FDoAevCore(iaevNew))
|
|
return fFalse;
|
|
|
|
if (iaevNew == _iaevCur)
|
|
_iaevCur++;
|
|
}
|
|
|
|
if (!(grfaet & (1 << aetCost)))
|
|
return fTrue;
|
|
|
|
// Costumes needed to be gathered forward
|
|
for (iaev = iaevAdd + 1; iaev <= iaevLast; iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet != aetCost)
|
|
continue;
|
|
|
|
aev.rtel = aevCur.rtel;
|
|
// aev.nfrm is set by _FDoAevCore()
|
|
|
|
// Allocate space
|
|
cb = _pggaev->Cb(iaev);
|
|
if (!_FInsertAev(_iaevCur, cb, pvNil, &aev))
|
|
return fFalse;
|
|
|
|
//Insert event
|
|
_pggaev->Put(_iaevCur, _pggaev->QvGet(iaev));
|
|
|
|
// Merge events to avoid duplicates
|
|
_MergeAev(_iaevFrmMin, _iaevCur, &iaevNew);
|
|
|
|
if (!_FDoAevCore(iaevNew))
|
|
return fFalse;
|
|
|
|
if (iaevNew == _iaevCur)
|
|
_iaevCur++;
|
|
}
|
|
|
|
// Remove redundant (same frame) events from previous Add
|
|
if (iaevAdd < 0)
|
|
return fTrue;
|
|
|
|
// May need to delete entire previous subroute
|
|
// if the prev Add occurred at the same frame
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaevAdd);
|
|
if (_nfrmCur == paev->nfrm)
|
|
{
|
|
// Deleting the entire subroute
|
|
_DelAddFrame(iaevAdd, _iaevAddCur);
|
|
return fTrue;
|
|
}
|
|
|
|
// May need to prune out same-frame events from prev subrte
|
|
for (iaev = _iaevAddCur - 1; iaev >= iaevAdd; iaev--)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
if (_nfrmCur == paev->nfrm)
|
|
{
|
|
fPrunedPrevSubrte = fTrue;
|
|
_RemoveAev(iaev);
|
|
}
|
|
}
|
|
|
|
if (fPrunedPrevSubrte)
|
|
{
|
|
// The previous path requires pruning
|
|
// Terminating stop & freeze events need to be inserted
|
|
long nfrm = _nfrmCur;
|
|
if (!FGotoFrame(_nfrmCur - 1))
|
|
return fFalse;
|
|
DeleteFwdCore(fFalse, pvNil, _iaevCur);
|
|
return FGotoFrame(nfrm);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Gather the initialization events of the subsequent subroute and insert
|
|
them at the current event index, _iaevCur.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FAddAevFromLater(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
AEV aev;
|
|
long iaev;
|
|
long iaevStart = -1;
|
|
RTEL rtelAdd;
|
|
bool fPositionBody = fFalse;
|
|
|
|
Assert(1 == _iaevCur, "_FAddAevFromLater logic error");
|
|
// Find the next Add event
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet == aetAdd)
|
|
{
|
|
iaevStart = iaev;
|
|
rtelAdd = aev.rtel;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iaevStart < 0)
|
|
goto LEnd;
|
|
|
|
// Stuff in the events from this next Add event
|
|
for (iaev = iaevStart; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
|
|
if (aev.rtel != rtelAdd)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (aetCost == aev.aet || aetPull == aev.aet ||
|
|
aetSize == aev.aet || aetRotF == aev.aet)
|
|
{
|
|
aev.rtel = _rtelCur;
|
|
// aev.nfrm is set by _FDoAevCore()
|
|
|
|
// Allocate space
|
|
long cbNew = _pggaev->Cb(iaev);
|
|
if (!_FInsertAev(_iaevCur, cbNew, pvNil, &aev))
|
|
return fFalse;
|
|
iaev++;
|
|
|
|
//Insert event
|
|
_pggaev->Put(_iaevCur, _pggaev->QvGet(iaev));
|
|
|
|
if (!_FDoAevCur())
|
|
return fFalse;
|
|
|
|
if (aev.aet != aetCost)
|
|
fPositionBody = fTrue;
|
|
}
|
|
}
|
|
|
|
if (fPositionBody)
|
|
{
|
|
_PositionBody(&_xyzCur);
|
|
}
|
|
|
|
Assert(_pggaev->IvMac() > 0, "Logic Error");
|
|
|
|
LEnd:
|
|
return FSetActionCore(0, 0, 0);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Locate the event of type aet with index closest to but smaller than iaevCur
|
|
Return true if found, with its index in *piaevAdd
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FFindPrevAevAet(long aet, long iaevCur, long *piaevAdd)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(aet, 0, aetLim);
|
|
AssertIn(iaevCur, 0, _pggaev->IvMac());
|
|
AssertVarMem(piaevAdd);
|
|
|
|
AEV aev;
|
|
|
|
iaevCur--;
|
|
for (; iaevCur >= 0; iaevCur--)
|
|
{
|
|
_pggaev->GetFixed(iaevCur, &aev);
|
|
if (aev.aet == aet)
|
|
{
|
|
*piaevAdd = iaevCur;
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
*piaevAdd = ivNil;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Locate the event of type aet with index closest to but >= iaevCur.
|
|
Return true if one is found, with its index in *piaevAdd
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FFindNextAevAet(long aet, long iaevCur, long *piaevAdd)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(aet, 0, aetLim);
|
|
AssertIn(iaevCur, 0, _pggaev->IvMac() + 1);
|
|
AssertVarMem(piaevAdd);
|
|
|
|
AEV *paev;
|
|
|
|
for (; iaevCur < _pggaev->IvMac(); iaevCur++)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaevCur);
|
|
if (paev->aet == aet)
|
|
{
|
|
*piaevAdd = iaevCur;
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
*piaevAdd = ivNil;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Locate the last reachable event in the specified subroute
|
|
Note: This is complicated by static actions.
|
|
Input:
|
|
Begin the search at iaevAdd.
|
|
The limit of the search is iaevLim, which, if == ivNil, is
|
|
the extent of the ggaev.
|
|
Return its index in *piaevAdd
|
|
|
|
***************************************************************************/
|
|
void ACTR::_FindAevLastSub(long iaevAdd, long iaevLim, long *piaevLast)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaevAdd, 0, _pggaev->IvMac());
|
|
if (iaevLim != ivNil)
|
|
AssertIn(iaevLim, 0, _pggaev->IvMac() + 1);
|
|
AssertVarMem(piaevLast);
|
|
|
|
AEV aev;
|
|
|
|
if (iaevLim == ivNil)
|
|
iaevLim = _pggaev->IvMac();
|
|
|
|
_pggaev->GetFixed(iaevLim - 1, &aev);
|
|
|
|
if (_FIsStalled(iaevAdd, &aev.rtel, piaevLast))
|
|
{
|
|
// Last active event stored by _FIsStalled
|
|
return;
|
|
}
|
|
|
|
*piaevLast = iaevLim - 1;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Remove the actor from the stage
|
|
Add the event to the event list
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FRemFromStageCore(void)
|
|
{
|
|
AssertThis(0);
|
|
AEV *paev;
|
|
|
|
if (!_fOnStage)
|
|
return fTrue;
|
|
|
|
if (!_pggaev->FEnsureSpace( 1, kcbVarStep, fgrpNil)) //step
|
|
return fFalse;
|
|
|
|
AssertIn(_iaevAddCur, 0, _pggaev->IvMac());
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevAddCur);
|
|
if (_nfrmCur == paev->nfrm)
|
|
{
|
|
if (!_FDeleteEntireSubrte())
|
|
return fFalse;
|
|
}
|
|
|
|
if (_fOnStage)
|
|
return _FAddDoAev(aetRem, kcbVarZero, pvNil);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Delete the current entire subroute & reposition actor accordingly.
|
|
Based on current state variables
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FDeleteEntireSubrte(void)
|
|
{
|
|
AssertThis(0);
|
|
AEV *paev;
|
|
long nfrmSav;
|
|
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevAddCur);
|
|
Assert(paev->nfrm == _nfrmCur,
|
|
"Logic error: trying to delete whole route from the middle");
|
|
|
|
// Delete forward from here (exclusive of current point)
|
|
_DeleteFwdCore(fFalse, pvNil, _iaevCur);
|
|
|
|
// Delete events & path for the sole remaining Add frame
|
|
_DelAddFrame(_iaevAddCur, _iaevCur);
|
|
|
|
// A hide should be done separately from event execution
|
|
// because no subroute exists in this case
|
|
_Hide();
|
|
if (_ptmpl != pvNil && _pbody != pvNil)
|
|
_ptmpl->FSetDefaultCost(_pbody);
|
|
_SetStateRewound();
|
|
nfrmSav = _nfrmCur;
|
|
_nfrmCur = knfrmInvalid;
|
|
if (_pggaev->IvMac() > 0)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(0);
|
|
Assert(paev->aet == aetAdd, "Corrupt event list");
|
|
_nfrmFirst = paev->nfrm;
|
|
return FGotoFrame(nfrmSav);
|
|
}
|
|
_nfrmFirst = knfrmInvalid;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Delete the events and point corresponding to a single point subroute
|
|
|
|
***************************************************************************/
|
|
void ACTR::_DelAddFrame(long iaevAdd, long iaevLim)
|
|
{
|
|
AssertThis(0);
|
|
long iaev;
|
|
long irptAdd;
|
|
AEV *paev;
|
|
|
|
AssertIn(iaevAdd, 0, iaevLim);
|
|
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaevAdd);
|
|
irptAdd = paev->rtel.irpt;
|
|
|
|
// Delete events in current frame of subroute
|
|
for (iaev = iaevLim - 1; iaev >= iaevAdd; iaev--)
|
|
{
|
|
_RemoveAev(iaev);
|
|
}
|
|
|
|
// Delete current point if unused
|
|
if (iaevAdd > 0)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaevAdd - 1);
|
|
if (paev->rtel.irpt == irptAdd ||
|
|
(paev->rtel.irpt == (irptAdd - 1) && paev->rtel.dwrOffset > rZero))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
_AdjustAevForRteDel(irptAdd, iaevAdd);
|
|
_pglrpt->Delete(irptAdd);
|
|
if (_rtelCur.irpt > irptAdd)
|
|
_rtelCur.irpt--;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Set the Costume for a body part
|
|
Add the event to the event list
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FSetCostumeCore(long ibsetClicked, TAG *ptag, long cmid,
|
|
bool fCmtl)
|
|
{
|
|
AssertThis(0);
|
|
Assert(fCmtl || ibsetClicked >= 0, "Invalid ibsetClicked argument");
|
|
AssertVarMem(ptag);
|
|
|
|
AEVCOST aevcost;
|
|
PCMTL pcmtl;
|
|
long ibsetApply;
|
|
|
|
// For custom materials, the ibset is a property of the CMTL itself,
|
|
// so read it from the CMTL rather than using the clicked ibset.
|
|
if (fCmtl)
|
|
{
|
|
pcmtl = _ptmpl->PcmtlFetch(cmid);
|
|
if (pvNil == pcmtl)
|
|
return fFalse;
|
|
ibsetApply = pcmtl->Ibset();
|
|
ReleasePpo(&pcmtl);
|
|
}
|
|
else
|
|
{
|
|
ibsetApply = ibsetClicked;
|
|
}
|
|
|
|
aevcost.ibset = ibsetApply;
|
|
aevcost.tag = *ptag;
|
|
aevcost.cmid = cmid;
|
|
aevcost.fCmtl = fCmtl;
|
|
|
|
if (!_pggaev->FEnsureSpace( 1, kcbVarCost, fgrpNil))
|
|
return fFalse;
|
|
|
|
_PrepCostFill(_iaevCur, &aevcost);
|
|
if (!_FAddDoAev(aetCost, kcbVarCost, &aevcost))
|
|
return fFalse;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Freeze the actor (don't advance cels)
|
|
Add the event to the event list
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FFreeze(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
long faevfrz = (long)fTrue;
|
|
return _FAddDoAev(aetFreeze, kcbVarFreeze, &faevfrz);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Unfreeze the actor
|
|
Add the event to the event list
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FUnfreeze(void)
|
|
{
|
|
AssertThis(0);
|
|
long faevfrz = (long)fFalse;
|
|
return _FAddDoAev(aetFreeze, kcbVarFreeze, &faevfrz);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Modify the add event to re-orient the actor
|
|
The add event retains the single point orientation.
|
|
|
|
On Input: If pxyz == pvNil, no averaging is done in orientation
|
|
grfbra are the flags indicating angles of rotation. In directions
|
|
in which no rotation has taken place, the wa are invalid.
|
|
|
|
WARNING: Rotations are non-abelian. Ordering is x then y then z.
|
|
If ordering other than this is desired, separate calls must be made.
|
|
|
|
***************************************************************************/
|
|
void ACTR::SetAddOrient(BRA xa, BRA ya, BRA za, ulong grfbra, XYZ *pdxyz)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(_iaevAddCur, 0, _pggaev->IvMac());
|
|
|
|
AEVADD aevadd;
|
|
|
|
_pggaev->Get(_iaevAddCur, &aevadd);
|
|
|
|
if (pvNil == pdxyz)
|
|
{
|
|
aevadd.xa = xa;
|
|
aevadd.ya = ya;
|
|
aevadd.za = za;
|
|
}
|
|
else
|
|
{
|
|
BRS rWeight;
|
|
BRS dwrNew = BR_LENGTH3(pdxyz->dxr, pdxyz->dyr, pdxyz->dzr);
|
|
rWeight = BrsDiv(dwrNew, kdwrFast);
|
|
rWeight = LwBound(rWeight, krOriWeightMin, rOne);
|
|
|
|
if (grfbra & fbraRotateX)
|
|
aevadd.xa = _BraAvgAngle(xa, aevadd.xa, rWeight);
|
|
|
|
if (grfbra & fbraRotateY)
|
|
aevadd.ya = _BraAvgAngle(ya, aevadd.ya, rWeight);
|
|
|
|
if (grfbra & fbraRotateZ)
|
|
aevadd.za = _BraAvgAngle(za, aevadd.za, rWeight);
|
|
}
|
|
|
|
//Modify the event
|
|
_pggaev->Put(_iaevAddCur, &aevadd);
|
|
_xfrm.xaPath = aevadd.xa;
|
|
_xfrm.yaPath = aevadd.ya;
|
|
_xfrm.zaPath = aevadd.za;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Average two angles by (rw1 * a1 + (1-rw1)*a2)
|
|
rw1 is the weighting for a1
|
|
|
|
***************************************************************************/
|
|
BRA ACTR::_BraAvgAngle(BRA a1, BRA a2, BRS rw1)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(rw1, rZero, rOne + rEps);
|
|
|
|
BRS rT = rZero;
|
|
BRS r1 = BrAngleToScalar(a1);
|
|
BRS r2 = BrAngleToScalar(a2);
|
|
BRS rw2;
|
|
|
|
rw2 = BrsSub(rOne, rw1);
|
|
|
|
// Compensate for averaging across 0 degrees
|
|
if (BrsAbs(BrsSub(r1, r2)) > rOneHalf)
|
|
{
|
|
if (r2 > r1)
|
|
{
|
|
// Add equiv of 360 degrees for each weight of r1
|
|
rT = BrsAdd(rT, rw1);
|
|
}
|
|
else
|
|
{
|
|
// Add equiv of 360 degrees for each weight of r1
|
|
rT = BrsAdd(rT, rw2);
|
|
}
|
|
}
|
|
|
|
rT = BrsAdd(rT, BrsMul(r1, rw1));
|
|
rT = BrsAdd(rT, BrsMul(r2, rw2));
|
|
|
|
if (rT > rOne)
|
|
rT = rT & rFractMax;
|
|
|
|
return(BrScalarToAngle(rT));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Rotate the actor
|
|
Add the event to the event list
|
|
If !fFromHereFwd, the rotation is to apply to the current frame only
|
|
|
|
WARNING: Rotations are non-abelian. Ordering is x then y then z.
|
|
If ordering other than this is desired, separate calls must be made.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FRotate(BRA xa, BRA ya, BRA za, bool fFromHereFwd)
|
|
{
|
|
AssertThis(0);
|
|
|
|
BMAT34 *pbmat34;
|
|
long aet;
|
|
|
|
if (!_pggaev->FEnsureSpace(1, kcbVarRot, fgrpNil))
|
|
return fFalse;
|
|
|
|
if (fFromHereFwd)
|
|
{
|
|
aet = aetRotF;
|
|
pbmat34 = &_xfrm.bmat34Fwd;
|
|
if (_fUseBmat34Cur) // _xfrm.bmat34Cur has last been used
|
|
{
|
|
// _xfrm.bmat34Cur stores the complete (no path added) orientation
|
|
BrMatrix34Copy(pbmat34, &_xfrm.bmat34Cur);
|
|
// Back the path orientation out from this matrix.
|
|
// Otherwise the actor will jump in angle
|
|
// This MUST be done in z then y then x order
|
|
// Note: matrix inversion unnecessary
|
|
if (_xfrm.zaPath != aZero)
|
|
BrMatrix34PostRotateZ(pbmat34, -_xfrm.zaPath);
|
|
if (_xfrm.yaPath != aZero)
|
|
BrMatrix34PostRotateY(pbmat34, -_xfrm.yaPath);
|
|
if (_xfrm.xaPath != aZero)
|
|
BrMatrix34PostRotateX(pbmat34, -_xfrm.xaPath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// _xfrm.bmat34Cur is kept current at <all> frames so that
|
|
// rotations can be post applied to it
|
|
aet = aetRotH;
|
|
pbmat34 = &_xfrm.bmat34Cur;
|
|
}
|
|
|
|
if (aZero != xa)
|
|
{
|
|
BrMatrix34PreRotateX(pbmat34, xa);
|
|
}
|
|
if (aZero != ya)
|
|
{
|
|
BrMatrix34PreRotateY(pbmat34, ya);
|
|
}
|
|
if (aZero != za)
|
|
{
|
|
BrMatrix34PreRotateZ(pbmat34, za);
|
|
}
|
|
|
|
Assert(_iaevCur <= _pggaev->IvMac(), "_iaevCur bug");
|
|
|
|
//Add the event
|
|
if (fFromHereFwd)
|
|
_PrepXfrmFill(aetRotF, pbmat34, kcbVarRot, _iaevCur, ivNil, faetNil);
|
|
AssertDo(_FAddDoAev(aet, kcbVarRot, pbmat34),
|
|
"Ensure space insufficient");
|
|
|
|
_PositionBody(&_xyzCur);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
"Normalize" the actor
|
|
The revert tool
|
|
Insert normalize transformation events into the event list
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FNormalizeCore(ulong grfnorm)
|
|
{
|
|
AssertThis(0);
|
|
long rScaleStepOld = _xfrm.rScaleStep;
|
|
ulong faet;
|
|
long cbVar = 0;
|
|
|
|
if (grfnorm & fnormRotate)
|
|
cbVar += kcbVarRot;
|
|
if (grfnorm & fnormSize)
|
|
cbVar += kcbVarSize + kcbVarPull;
|
|
|
|
if (!_pggaev->FEnsureSpace( 3, cbVar, fgrpNil))
|
|
return fFalse;
|
|
|
|
if (grfnorm & fnormRotate)
|
|
{
|
|
_InitXfrmRot(&_xfrm.bmat34Fwd);
|
|
_InitXfrmRot(&_xfrm.bmat34Cur);
|
|
_PrepXfrmFill(aetRotF, &_xfrm.bmat34Fwd, kcbVarRot, _iaevCur);
|
|
AssertDo(_FAddDoAev(aetRotF, kcbVarRot, &_xfrm.bmat34Fwd),
|
|
"EnsureSpace insufficient");
|
|
}
|
|
|
|
if (grfnorm & fnormSize)
|
|
{
|
|
_xfrm.aevpull.rScaleX = rOne;
|
|
_xfrm.aevpull.rScaleY = rOne;
|
|
_xfrm.aevpull.rScaleZ = rOne;
|
|
faet = (_xfrm.rScaleStep != rOne) ? faetTweak : faetNil;
|
|
_xfrm.rScaleStep = rOne;
|
|
_PrepXfrmFill(aetPull, &_xfrm.aevpull, kcbVarPull, _iaevCur);
|
|
_PrepXfrmFill(aetSize, &_xfrm.rScaleStep, kcbVarSize, _iaevCur, ivNil, faet);
|
|
|
|
AssertDo(_FAddDoAev(aetPull, kcbVarPull, &_xfrm.aevpull),
|
|
"EnsureSpace insufficient");
|
|
AssertDo(_FAddDoAev(aetSize, kcbVarSize, &_xfrm.rScaleStep),
|
|
"EnsureSpace insufficient");
|
|
|
|
// Possibly extend life of actor if shrunk
|
|
if (rScaleStepOld > rOne &&
|
|
pvNil != _pscen &&
|
|
_rtelCur.irpt < _pglrpt->IvMac() - 1) //optimization
|
|
{
|
|
_fLifeDirty = fTrue;
|
|
_pscen->InvalFrmRange();
|
|
}
|
|
}
|
|
|
|
Assert(_iaevCur <= _pggaev->IvMac(), "_iaevCur bug");
|
|
|
|
_PositionBody(&_xyzCur);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Scale the actor.
|
|
Add the event to the event list
|
|
Bound the allowable scaling.
|
|
|
|
Note that the stepsize and the actor lifetime is then subject to change,
|
|
with the attendent specified tweak etc modifications.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FScale(BRS rScale)
|
|
{
|
|
AssertThis(0);
|
|
|
|
ulong faet = faetNil;
|
|
long rScaleStep;
|
|
long rScaleStepOld = _xfrm.rScaleStep;
|
|
|
|
if (!_pggaev->FEnsureSpace( 1, kcbVarSize, fgrpNil))
|
|
return fFalse;
|
|
|
|
rScale = LwBound(rScale, krScaleMinFactor, krScaleMaxFactor);
|
|
|
|
rScaleStep = BrsMul(_xfrm.rScaleStep, rScale);
|
|
_xfrm.rScaleStep = LwBound(rScaleStep, krScaleMin, krScaleMax);
|
|
|
|
// Remove tweaks when a transformation that changes stepsize occurs
|
|
faet = (_xfrm.rScaleStep != rScale) ? faetTweak : faetNil;
|
|
_PrepXfrmFill(aetSize, &_xfrm.rScaleStep, kcbVarSize, _iaevCur, ivNil, faet);
|
|
|
|
if (!_FAddDoAev(aetSize, kcbVarSize, &_xfrm.rScaleStep))
|
|
return fFalse;
|
|
|
|
Assert(_iaevCur <= _pggaev->IvMac(), "_iaevCur bug");
|
|
|
|
if (_xfrm.rScaleStep != rScale)
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
_PositionBody(&_xyzCur);
|
|
|
|
// Possibly extend life of actor if shrunk
|
|
if (rScaleStepOld != _xfrm.rScaleStep &&
|
|
pvNil != _pscen &&
|
|
_rtelCur.irpt < _pglrpt->IvMac() - 1) //optimization
|
|
{
|
|
_fLifeDirty = fTrue;
|
|
_pscen->InvalFrmRange();
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Drag the actor forwards or backwards in time
|
|
NOTE** Drag this subroute only -> sliding other subroutes in time
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FSoonerLater(long dnfrm)
|
|
{
|
|
AssertThis(0);
|
|
|
|
AEV aev;
|
|
AEV *paevPrev;
|
|
long nfrmSav;
|
|
long iaev;
|
|
long fSuccess;
|
|
long dnfrmSub;
|
|
long dnfrmT;
|
|
|
|
if (0 == dnfrm)
|
|
return fTrue;
|
|
|
|
if (_fLifeDirty)
|
|
{
|
|
if (!_FComputeLifetime(pvNil))
|
|
PushErc(ercSocBadFrameSlider);
|
|
}
|
|
|
|
Assert(_iaevAddCur >= 0, "Invalid value for _iaevAddCur");
|
|
|
|
// On a Sooner operation, slide the earlier subroutes
|
|
// backward in time
|
|
if (dnfrm < 0)
|
|
{
|
|
dnfrmT = (-dnfrm);
|
|
// The times of past events need to be correct.
|
|
for (iaev = _iaevCur - 1; dnfrmT > 0 && iaev >= 0; iaev--)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet != aetAdd || iaev == 0)
|
|
{
|
|
aev.nfrm -= dnfrmT;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
continue;
|
|
}
|
|
|
|
// Account for subroute gaps
|
|
paevPrev = (AEV *)_pggaev->QvFixedGet(iaev - 1);
|
|
dnfrmSub = aev.nfrm - (paevPrev->nfrm);
|
|
aev.nfrm -= dnfrmT;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
dnfrmT -= (dnfrmSub - 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Later
|
|
dnfrmT = dnfrm;
|
|
for (iaev = _iaevAddCur; dnfrmT > 0 && iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet != aetAdd || iaev == _iaevAddCur)
|
|
{
|
|
nfrmSav = aev.nfrm;
|
|
aev.nfrm += dnfrmT;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
continue;
|
|
}
|
|
// Adjust for the gap between subroutes
|
|
dnfrmSub = aev.nfrm - nfrmSav;
|
|
dnfrmT -= (dnfrmSub - 1);
|
|
if (dnfrmT <= 0)
|
|
break;
|
|
aev.nfrm += dnfrmT;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
}
|
|
_pggaev->GetFixed(0, &aev);
|
|
_nfrmFirst = aev.nfrm;
|
|
_pggaev->GetFixed(_pggaev->IvMac() - 1, &aev);
|
|
_nfrmLast = aev.nfrm;
|
|
|
|
_nfrmCur += dnfrm;
|
|
|
|
// Invalidate, but do not recompute the range
|
|
_pscen->InvalFrmRange();
|
|
|
|
if (fSuccess = _pscen->FGotoFrm(_pscen->Nfrm() + dnfrm))
|
|
{
|
|
_pscen->Pmvie()->Pmcc()->UpdateScrollbars();
|
|
}
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Pull, stretch, squash the actor.
|
|
Add the event to the event list
|
|
|
|
Bound the allowable scaling.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FPull(BRS rScaleX, BRS rScaleY, BRS rScaleZ)
|
|
{
|
|
AssertThis(0);
|
|
|
|
BRS rScaleXT;
|
|
BRS rScaleYT;
|
|
BRS rScaleZT;
|
|
|
|
if (!_pggaev->FEnsureSpace( 1, kcbVarPull, fgrpNil))
|
|
return fFalse;
|
|
|
|
rScaleX = LwBound(rScaleX, krScaleMinFactor, krScaleMaxFactor);
|
|
rScaleY = LwBound(rScaleY, krScaleMinFactor, krScaleMaxFactor);
|
|
rScaleZ = LwBound(rScaleZ, krScaleMinFactor, krScaleMaxFactor);
|
|
|
|
rScaleXT = BrsMul(_xfrm.aevpull.rScaleX, rScaleX);
|
|
rScaleYT = BrsMul(_xfrm.aevpull.rScaleY, rScaleY);
|
|
rScaleZT = BrsMul(_xfrm.aevpull.rScaleZ, rScaleZ);
|
|
|
|
_xfrm.aevpull.rScaleX = LwBound(rScaleXT, krPullMin, krPullMax);
|
|
_xfrm.aevpull.rScaleY = LwBound(rScaleYT, krPullMin, krPullMax);
|
|
_xfrm.aevpull.rScaleZ = LwBound(rScaleZT, krPullMin, krPullMax);
|
|
|
|
_PrepXfrmFill(aetPull, &_xfrm.aevpull, kcbVarPull, _iaevCur);
|
|
|
|
if (!_FAddDoAev(aetPull, kcbVarPull, &_xfrm.aevpull))
|
|
return fFalse;
|
|
|
|
Assert(_iaevCur <= _pggaev->IvMac(), "_iaevCur bug");
|
|
|
|
_PositionBody(&_xyzCur);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Translate one point of the actor's path
|
|
Add an event to the event list
|
|
|
|
The (dxr,dyr,dzr) passed in is a delta distance from the previous
|
|
location (set by GoToFrame, ie, determined by _rtelCur).
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FTweakRoute(BRS dxr, BRS dyr, BRS dzr, ulong grfmaf)
|
|
{
|
|
AssertThis(0);
|
|
|
|
XYZ xyz;
|
|
|
|
if (!_pggaev->FEnsureSpace( 1, kcbVarTweak, fgrpNil))
|
|
return fFalse;
|
|
|
|
xyz.dxr = BrsAdd(_xyzCur.dxr, dxr);
|
|
xyz.dyr = BrsAdd(_xyzCur.dyr, dyr);
|
|
xyz.dzr = BrsAdd(_xyzCur.dzr, dzr);
|
|
|
|
if ((grfmaf & fmafGround) &&
|
|
(BrsAdd(_xyzCur.dyr, _dxyzRte.dyr) >= rZero) &&
|
|
(BrsAdd(xyz.dyr, _dxyzRte.dyr) < rZero))
|
|
{
|
|
xyz.dyr = -_dxyzRte.dyr;
|
|
}
|
|
|
|
if (!_FAddDoAev(aetTweak, kcbVarTweak, &xyz))
|
|
return fFalse;
|
|
|
|
_PositionBody(&xyz);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Update xyzRte from the overall origin & the subroute translation
|
|
|
|
***************************************************************************/
|
|
void ACTR::_UpdateXyzRte(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
_dxyzRte.dxr = BrsAdd(_dxyzFullRte.dxr, _dxyzSubRte.dxr);
|
|
_dxyzRte.dyr = BrsAdd(_dxyzFullRte.dyr, _dxyzSubRte.dyr);
|
|
_dxyzRte.dzr = BrsAdd(_dxyzFullRte.dzr, _dxyzSubRte.dzr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Move the current subroute. (Compose tool)
|
|
|
|
If fmafOrient is set, the actor is oriented and moved.
|
|
|
|
If fmafEntireSubrte is set, this does not add an event to the aev list.
|
|
It alters the data in the Add event & _dxyzSubRte. Return fTrue.
|
|
|
|
If fmafEntireScene is set, _dxyzFullRte is modified. Return fTrue.
|
|
|
|
If !fmafEntireSubrte && !fmafEntireScene, a move event is added
|
|
to the event list. This can therefore fail.
|
|
|
|
Return success or failure
|
|
Return *pfmoved indicating whether or not the path moved.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FMoveRoute(BRS dxr, BRS dyr, BRS dzr, bool *pfMoved, ulong grfmaf)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(pfMoved);
|
|
|
|
AEVADD aevadd;
|
|
AEV *paev;
|
|
long iaev;
|
|
bool fMoved;
|
|
ulong grfbra;
|
|
XYZ dxyz;
|
|
BRS yrCurOld;
|
|
BRA xa;
|
|
BRA ya;
|
|
BRA za;
|
|
|
|
fMoved = (rZero != dxr || rZero != dyr || rZero != dzr);
|
|
if (pvNil != pfMoved)
|
|
*pfMoved = fMoved;
|
|
|
|
if (!fMoved)
|
|
return fTrue;
|
|
|
|
if (!_pggaev->FEnsureSpace( 2, kcbVarMove + kcbVarRot, fgrpNil))
|
|
return fFalse;
|
|
|
|
// Edit dyr to respect ground
|
|
yrCurOld = BrsAdd(_xyzCur.dyr, _dxyzRte.dyr);
|
|
if (FPure(grfmaf & fmafGround) &&
|
|
(yrCurOld >= rZero) &&
|
|
(BrsAdd(dyr, yrCurOld) < rZero))
|
|
{
|
|
dyr = BrsSub(rZero, yrCurOld);
|
|
}
|
|
|
|
// Update Actor's orientation
|
|
if (FPure(grfmaf & fmafOrient) && !_ptmpl->FIsTdt())
|
|
{
|
|
dxyz.dxr = dxr;
|
|
dxyz.dyr = dyr;
|
|
dxyz.dzr = dzr;
|
|
|
|
// Back the path orientation out from this matrix & replace
|
|
// with new orientation
|
|
if (_xfrm.zaPath != aZero)
|
|
BrMatrix34PostRotateZ(&_xfrm.bmat34Cur, -_xfrm.zaPath);
|
|
if (_xfrm.yaPath != aZero)
|
|
BrMatrix34PostRotateY(&_xfrm.bmat34Cur, -_xfrm.yaPath);
|
|
if (_xfrm.xaPath != aZero)
|
|
BrMatrix34PostRotateX(&_xfrm.bmat34Cur, -_xfrm.xaPath);
|
|
|
|
_ApplyRotFromVec(&dxyz, pvNil, &xa, &ya, &za, &grfbra);
|
|
SetAddOrient(xa, ya, za, grfbra, &dxyz);
|
|
|
|
// Force speed to be inversely proportional to angular rotation
|
|
if (_iaevAddCur != ivNil)
|
|
{
|
|
BRS dwra, drxa, drya, drza;
|
|
BRS ra1, ra2;
|
|
#ifdef DEBUG
|
|
{
|
|
AEV *paev;
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevAddCur);
|
|
Assert(_nfrmCur == paev->nfrm, "Unsupported use of fmafOrient");
|
|
}
|
|
#endif //DEBUG
|
|
// Set dwra = angular change (in scalar form)
|
|
_pggaev->Get(_iaevAddCur, &aevadd);
|
|
ra1 = BrAngleToScalar(xa);
|
|
ra2 = BrAngleToScalar(aevadd.xa);
|
|
drxa = BrsAbs(BrsSub(ra1, ra2));
|
|
if (drxa > rOneHalf)
|
|
{
|
|
if (ra2 > ra1)
|
|
ra1 = BrsAdd(rOne, ra1);
|
|
else
|
|
ra2 = BrsAdd(rOne, ra2);
|
|
drxa = BrsAbs(BrsSub(ra1, ra2));
|
|
}
|
|
|
|
ra1 = BrAngleToScalar(ya);
|
|
ra2 = BrAngleToScalar(aevadd.ya);
|
|
drya = BrsAbs(BrsSub(ra1, ra2));
|
|
if (drya > rOneHalf)
|
|
{
|
|
if (ra2 > ra1)
|
|
ra1 = BrsAdd(rOne, ra1);
|
|
else
|
|
ra2 = BrsAdd(rOne, ra2);
|
|
drya = BrsAbs(BrsSub(ra1, ra2));
|
|
}
|
|
|
|
ra1 = BrAngleToScalar(za);
|
|
ra2 = BrAngleToScalar(aevadd.za);
|
|
drza = BrsAbs(BrsSub(ra1, ra2));
|
|
if (drza > rOneHalf)
|
|
{
|
|
if (ra2 > ra1)
|
|
ra1 = BrsAdd(rOne, ra1);
|
|
else
|
|
ra2 = BrsAdd(rOne, ra2);
|
|
drza = BrsAbs(BrsSub(ra1, ra2));
|
|
}
|
|
|
|
dwra = BR_LENGTH3(drxa, drya, drza);
|
|
|
|
// Compute a bounded inverse of the angular change
|
|
dwra = LwBound(dwra, krAngleMin, krAngleMax);
|
|
dwra = BrsDiv(BrsRcp(dwra), krAngleMinRcp);
|
|
AssertIn(dwra, rZero, BrsAdd(rOne, rEps));
|
|
|
|
// Adjust the distances
|
|
dxr = BrsMul(dxr, dwra);
|
|
dyr = BrsMul(dyr, dwra);
|
|
dzr = BrsMul(dzr, dwra);
|
|
}
|
|
|
|
// _xfrm.bmat34Cur is to hold the current full rotation
|
|
_LoadAddOrien(&aevadd, fTrue);
|
|
|
|
// Update any type of rotate event in this frame
|
|
for (iaev = _iaevAddCur + 1; iaev < _iaevCur; iaev++)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
if (aetRotH == paev->aet)
|
|
_pggaev->Put(iaev, &_xfrm.bmat34Cur);
|
|
if (aetRotF == paev->aet)
|
|
{
|
|
// Insert orientation-rotation event
|
|
if (!_FAddDoAev(aetRotH, kcbVarRot, &_xfrm.bmat34Cur))
|
|
{
|
|
Bug("Should have ensured space");
|
|
return fFalse;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update actor's position
|
|
if (FPure(grfmaf & fmafEntireScene))
|
|
{
|
|
_dxyzFullRte.dxr = BrsAdd(dxr, _dxyzFullRte.dxr);
|
|
_dxyzFullRte.dyr = BrsAdd(dyr, _dxyzFullRte.dyr);
|
|
_dxyzFullRte.dzr = BrsAdd(dzr, _dxyzFullRte.dzr);
|
|
}
|
|
else
|
|
{
|
|
if (_iaevAddCur == ivNil)
|
|
return fFalse;
|
|
|
|
if (!FPure(grfmaf & fmafEntireSubrte))
|
|
{
|
|
// Move actor just from this frame on (this subpath only)
|
|
// Note: FAddDoAev will update _dxyzSubRte
|
|
dxyz.dxr = dxr;
|
|
dxyz.dyr = dyr;
|
|
dxyz.dzr = dzr;
|
|
if (!_FAddDoAev(aetMove, kcbVarMove, &dxyz))
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
// Translating whole subroute
|
|
#ifdef DEBUG
|
|
long cbVar = _pggaev->Cb(_iaevAddCur);
|
|
Assert(cbVar == kcbVarAdd, "Corrupt aev");
|
|
#endif //DEBUG
|
|
// Adjust the translation state variables
|
|
_dxyzSubRte.dxr = BrsAdd(dxr, _dxyzSubRte.dxr);
|
|
_dxyzSubRte.dyr = BrsAdd(dyr, _dxyzSubRte.dyr);
|
|
_dxyzSubRte.dzr = BrsAdd(dzr, _dxyzSubRte.dzr);
|
|
|
|
// Adjust the position in the add event
|
|
_pggaev->Get(_iaevAddCur, &aevadd);
|
|
aevadd.dxr = BrsAdd(aevadd.dxr, dxr);
|
|
aevadd.dyr = BrsAdd(aevadd.dyr, dyr);
|
|
aevadd.dzr = BrsAdd(aevadd.dzr, dzr);
|
|
_pggaev->Put(_iaevAddCur, &aevadd);
|
|
}
|
|
}
|
|
_UpdateXyzRte();
|
|
|
|
_pscen->MarkDirty();
|
|
|
|
// Update Brender model
|
|
fMoved = FPure(rZero != dxr || rZero != dyr || rZero != dzr);
|
|
|
|
if (fMoved)
|
|
_PositionBody(&_xyzCur);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Load starting point orientation into state variable _xfrm
|
|
|
|
***************************************************************************/
|
|
void ACTR::_LoadAddOrien(AEVADD *paevadd, bool fNoReset)
|
|
{
|
|
AssertThis(0);
|
|
|
|
// Set _xfrm.bmat34Cur always holds the current full rotation
|
|
if (!fNoReset)
|
|
_InitXfrmRot(&_xfrm.bmat34Cur);
|
|
BrMatrix34PostRotateX(&_xfrm.bmat34Cur, paevadd->xa);
|
|
BrMatrix34PostRotateY(&_xfrm.bmat34Cur, paevadd->ya);
|
|
BrMatrix34PostRotateZ(&_xfrm.bmat34Cur, paevadd->za);
|
|
_xfrm.xaPath = paevadd->xa;
|
|
_xfrm.yaPath = paevadd->ya;
|
|
_xfrm.zaPath = paevadd->za;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Remove redundant events of a targeted type. (Actor uses this for
|
|
Xfrm editing)
|
|
NOTE***: This is not valid for Actn or Cost fill. Special case routines
|
|
exist for those event types.
|
|
|
|
This is called _before_ inserting the new event.
|
|
|
|
Note: For transforms, this code will rarely have effect unless the client
|
|
uses discretized increments of observable size, ie, it is statistically
|
|
unlikely that a match of the same size will be found. This code was
|
|
requested in anticipation of discretized increments.
|
|
|
|
Input: aet == the event type
|
|
*pvVar == the variable part of the new event being inserted
|
|
cbVar == the size of the variable part
|
|
iaevMin == the first possible event to delete
|
|
iaevCmp == ivNil or the event being replaced
|
|
grfaet == faetNil or types of events to nuke along the way
|
|
|
|
***************************************************************************/
|
|
void ACTR::_PrepXfrmFill(long aet, void *pvVar, long cbVar,
|
|
long iaevMin, long iaevCmp, ulong grfaet)
|
|
{
|
|
AssertBaseThis(0);
|
|
Assert(aet == aetSize || aet == aetRotF || aet == aetPull,
|
|
"Illegal argument aet");
|
|
AssertPvCb(pvVar, cbVar);
|
|
AssertIn(cbVar, 0, 100); // Approximate bound
|
|
AssertIn(iaevMin, 0, _pggaev->IvMac() + 1);
|
|
AssertIn(iaevCmp, -1, _pggaev->IvMac() + 1);
|
|
Assert(aet != aetActn && aet != aetCost, "Illegal aet argument");
|
|
|
|
long iaev;
|
|
AEV aev;
|
|
bool fReplacePrev = fTrue;
|
|
void *pvVarCmp = pvNil;
|
|
long cb;
|
|
|
|
_pggaev->Lock();
|
|
|
|
if (ivNil != iaevCmp)
|
|
{
|
|
pvVarCmp = _pggaev->QvGet(iaevCmp, &cb);
|
|
Assert(cb == cbVar, "Logic error");
|
|
}
|
|
else
|
|
{
|
|
// Locate the most current event of this type
|
|
// and store the ptr in pvVarCmp
|
|
for (iaev = 0; iaev < iaevMin - 1; iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aet != aev.aet)
|
|
continue;
|
|
|
|
pvVarCmp = _pggaev->QvGet(iaev, &cb);
|
|
Assert(cb == cbVar, "Logic error");
|
|
}
|
|
}
|
|
|
|
if (pvNil == pvVarCmp)
|
|
pvVarCmp = pvVar;
|
|
|
|
for (iaev = iaevMin; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
bool fDelete = fFalse;
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
|
|
// Stage entrance events are boundaries to edits
|
|
if (aetAdd == aev.aet)
|
|
goto LEnd;
|
|
|
|
switch(aev.aet)
|
|
{
|
|
case aetSize:
|
|
case aetPull:
|
|
case aetRotF:
|
|
void *pv1;
|
|
void *pv2;
|
|
if (aev.aet != aet)
|
|
continue;
|
|
|
|
if (fReplacePrev)
|
|
{
|
|
pv1 = pvVarCmp;
|
|
pv2 = _pggaev->QvGet(iaev);
|
|
if (fcmpEq != FcmpCompareRgb(pv1, pv2, cbVar))
|
|
{
|
|
//Prepare to test for a match with new event
|
|
fReplacePrev = fFalse;
|
|
pvVarCmp = pvVar;
|
|
fDelete = fFalse;
|
|
}
|
|
else
|
|
{
|
|
fDelete = fTrue;
|
|
goto LDelete;
|
|
}
|
|
}
|
|
|
|
Assert(!fReplacePrev, "Logic Error");
|
|
pv1 = pvVarCmp;
|
|
pv2 = _pggaev->QvGet(iaev);
|
|
if (fcmpEq != FcmpCompareRgb(pv1, pv2, cbVar))
|
|
goto LEnd;
|
|
|
|
//Events equal : delete event at iaev
|
|
fDelete = fTrue;
|
|
break;
|
|
|
|
case aetTweak:
|
|
if ((grfaet & faetTweak) && (aev.rtel >= _rtelCur))
|
|
fDelete = fTrue;
|
|
break;
|
|
case aetRotH:
|
|
if ((grfaet & faetRotF) && (aev.rtel == _rtelCur))
|
|
fDelete = fTrue;
|
|
break;
|
|
case aetFreeze:
|
|
if ((grfaet & faetFreeze) && (aev.rtel >= _rtelCur))
|
|
fDelete = fTrue;
|
|
break;
|
|
case aetStep:
|
|
if ((grfaet & faetStep) && (aev.rtel >= _rtelCur))
|
|
fDelete = fTrue;;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
LDelete:
|
|
if (fDelete)
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
}
|
|
}
|
|
|
|
LEnd:
|
|
_pggaev->Unlock();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Insert Aev. Update state variables
|
|
Since _pggaev is getting a copy of a tag, a call to DupTag is required
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FInsertAev(long iaev, long cbNew, void *pvVar, void *paev, bool fUpdateState)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaev, 0, _pggaev->IvMac() + 1);
|
|
AssertIn(cbNew, 0, 100); // approximate bound
|
|
if (pvNil != pvVar)
|
|
AssertPvCb(pvVar, cbNew);
|
|
AssertPvCb(paev, size(AEV));
|
|
|
|
PTAG ptag;
|
|
|
|
if (!_pggaev->FInsert(iaev, cbNew, pvVar, paev))
|
|
return fFalse;
|
|
|
|
// If not simply allocating space
|
|
if (pvVar != pvNil)
|
|
{
|
|
// Increment tag count
|
|
_pggaev->Lock();
|
|
if (_FIsIaevTag(_pggaev, iaev, &ptag))
|
|
TAGM::DupTag(ptag);
|
|
_pggaev->Unlock();
|
|
|
|
if (fUpdateState)
|
|
{
|
|
if (iaev < _iaevCur)
|
|
_iaevCur++;
|
|
if (iaev <= _iaevActnCur)
|
|
_iaevActnCur++;
|
|
if (iaev <= _iaevAddCur)
|
|
_iaevAddCur++;
|
|
if (iaev < _iaevFrmMin)
|
|
_iaevFrmMin++;
|
|
}
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Remove Aev. Update state variables
|
|
|
|
***************************************************************************/
|
|
void ACTR::_RemoveAev(long iaev, bool fUpdateState)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaev, 0, _pggaev->IvMac());
|
|
|
|
bool fUpdateSndFrame;
|
|
PTAG ptag;
|
|
PAEV qaev;
|
|
|
|
// First, close tags
|
|
_pggaev->Lock();
|
|
if (_FIsIaevTag(_pggaev, iaev, &ptag, &qaev))
|
|
TAGM::CloseTag(ptag);
|
|
|
|
/* Don't bother updating the frame sound indicator if we didn't change
|
|
an event in the scene's current frame */
|
|
fUpdateSndFrame = (qaev->aet == aetSnd) && (_pscen != pvNil) &&
|
|
(qaev->nfrm == _pscen->Nfrm());
|
|
|
|
_pggaev->Unlock(); // qaev is invalid past here!
|
|
TrashVar(&qaev);
|
|
|
|
_pggaev->Delete(iaev);
|
|
|
|
if (fUpdateState)
|
|
{
|
|
if (iaev < _iaevCur)
|
|
_iaevCur--;
|
|
if (iaev < _iaevActnCur)
|
|
_iaevActnCur--;
|
|
if (iaev < _iaevAddCur)
|
|
_iaevAddCur--;
|
|
if (iaev < _iaevFrmMin)
|
|
_iaevFrmMin--;
|
|
}
|
|
|
|
if (fUpdateSndFrame)
|
|
_pscen->UpdateSndFrame();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Remove specified (eg, tweak, step, freeze) events for the current action
|
|
If faetActn is set in the grfaet, also remove action events
|
|
matching the replaced action followed by action events matching
|
|
the new (current) action _anidCur.
|
|
|
|
Note: This routine exists in addition to the more general PrepXfrmFill
|
|
because actn events are considered to match if the anid's match
|
|
regardless of the value of the celn.
|
|
|
|
***************************************************************************/
|
|
void ACTR::_PrepActnFill(long iaevMin, long anidPrev, long anidNew, ulong grfaet)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaevMin, 0, _pggaev->IvMac() + 1);
|
|
|
|
AEV aev;
|
|
long iaev;
|
|
long anid = anidPrev;
|
|
|
|
for (iaev = iaevMin; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
|
|
// Stage entrance events are boundaries to edits
|
|
if (aetAdd == aev.aet)
|
|
return;
|
|
|
|
if (aetActn == aev.aet)
|
|
{
|
|
AEVACTN aevactn;
|
|
_pggaev->Get(iaev, &aevactn);
|
|
|
|
if (!(grfaet & faetActn))
|
|
{
|
|
if (aevactn.anid == anidNew)
|
|
continue;
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (aevactn.anid == anid)
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
}
|
|
else if (aevactn.anid == anidNew)
|
|
{
|
|
anid = anidNew;
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (aev.rtel < _rtelCur)
|
|
continue;
|
|
|
|
if (aev.aet == aetTweak && (grfaet & faetTweak))
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
}
|
|
|
|
if (aev.aet == aetFreeze && (grfaet & faetFreeze))
|
|
{
|
|
// Do not remove end-of-subroute freeze events
|
|
if (!_FIsDoneAevSub(iaev, aev.rtel))
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Remove redundant costume changes
|
|
Note: Delete all costume events that match the costume being replaced
|
|
Then delete all subsequent costume events that match the
|
|
costume being inserted
|
|
|
|
***************************************************************************/
|
|
void ACTR::_PrepCostFill(long iaevMin, AEVCOST *paevcost)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaevMin, 0, _pggaev->IvMac() + 1);
|
|
AssertVarMem(paevcost);
|
|
|
|
long iaev;
|
|
AEV aev;
|
|
AEVCOST aevcost;
|
|
bool fMtrl;
|
|
bool fCmtl;
|
|
MTRL *pmtrlCmp;
|
|
CMTL *pcmtlCmp;
|
|
MTRL *pmtrl = pvNil;
|
|
CMTL *pcmtl = pvNil;
|
|
bool fReplacePrev = fTrue;
|
|
|
|
// Locate the most current costume for this body part
|
|
_pbody->GetPartSetMaterial(paevcost->ibset, &fMtrl,
|
|
&pmtrlCmp, &pcmtlCmp);
|
|
fCmtl = !fMtrl;
|
|
|
|
for (iaev = iaevMin; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
|
|
// Stage entrance events are boundaries to edits
|
|
if (aetAdd == aev.aet)
|
|
goto LEnd;
|
|
|
|
if (aetCost != aev.aet)
|
|
continue;
|
|
|
|
_pggaev->Get(iaev, &aevcost);
|
|
if (aevcost.ibset != paevcost->ibset)
|
|
continue;
|
|
|
|
if (fReplacePrev)
|
|
{
|
|
// Delete the event if the costumes are the same
|
|
if ((fCmtl == aevcost.fCmtl) &&
|
|
((!fCmtl &&
|
|
(pmtrlCmp == (pmtrl = (PMTRL)vptagm->PbacoFetch(&aevcost.tag, MTRL::FReadMtrl))))
|
|
||
|
|
(fCmtl &&
|
|
(pcmtlCmp == (pcmtl = _ptmpl->PcmtlFetch(aevcost.cmid))))))
|
|
{
|
|
goto LDelete;
|
|
}
|
|
|
|
// This event does not match the one being replaced
|
|
// Prepare to test for a match with the event being inserted
|
|
fReplacePrev = fFalse;
|
|
if (!paevcost->fCmtl)
|
|
{
|
|
pmtrlCmp = (PMTRL)vptagm->PbacoFetch(&paevcost->tag, MTRL::FReadMtrl);
|
|
pcmtlCmp = pvNil;
|
|
}
|
|
else
|
|
{
|
|
pcmtlCmp = _ptmpl->PcmtlFetch(paevcost->cmid);
|
|
pmtrlCmp = pvNil;
|
|
}
|
|
|
|
}
|
|
|
|
ReleasePpo(&pcmtl);
|
|
ReleasePpo(&pmtrl);
|
|
Assert(!fReplacePrev, "Logic error");
|
|
if (paevcost->fCmtl != aevcost.fCmtl ||
|
|
(!aevcost.fCmtl &&
|
|
(pmtrlCmp != (pmtrl = (PMTRL)vptagm->PbacoFetch(&aevcost.tag, MTRL::FReadMtrl))))
|
|
||
|
|
(aevcost.fCmtl && (pcmtlCmp != (pcmtl = _ptmpl->PcmtlFetch(aevcost.cmid)))))
|
|
{
|
|
// If the costumes differ
|
|
goto LEnd;
|
|
}
|
|
LDelete:
|
|
ReleasePpo(&pcmtl);
|
|
ReleasePpo(&pmtrl);
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
}
|
|
LEnd:
|
|
if (!fReplacePrev)
|
|
{
|
|
ReleasePpo(&pmtrlCmp);
|
|
ReleasePpo(&pcmtlCmp);
|
|
}
|
|
ReleasePpo(&pmtrl);
|
|
ReleasePpo(&pcmtl);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
AdjustAevForRte for an already inserted point at irptAdjust
|
|
adjust the rtel's of the subsequent affected events
|
|
|
|
***************************************************************************/
|
|
void ACTR::_AdjustAevForRteIns(long irptAdjust, long iaevMin)
|
|
{
|
|
AssertBaseThis(0);
|
|
long irptMac = _pglrpt->IvMac();
|
|
AssertIn(irptAdjust, 0, irptMac);
|
|
AssertIn(iaevMin, 0, _pggaev->IvMac() + 1);
|
|
|
|
long iaev;
|
|
AEV aev;
|
|
RPT rptBack, rptAdjust;
|
|
BRS dwrBack;
|
|
|
|
if (irptAdjust > 0)
|
|
{
|
|
_pglrpt->Get(irptAdjust - 1, &rptBack);
|
|
dwrBack = rptBack.dwr;
|
|
}
|
|
else
|
|
dwrBack = rZero;
|
|
|
|
_pglrpt->Get(irptAdjust, &rptAdjust);
|
|
|
|
for (iaev = iaevMin; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.rtel.irpt >= irptAdjust) //offset from later point
|
|
{
|
|
aev.rtel.irpt++;
|
|
}
|
|
else if (aev.rtel.irpt < irptAdjust - 1)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if ((irptAdjust == irptMac - 1) || (rZero == dwrBack))
|
|
continue;
|
|
|
|
if (aev.rtel.dwrOffset >= dwrBack)
|
|
{
|
|
aev.rtel.dwrOffset = BrsSub(aev.rtel.dwrOffset, dwrBack);
|
|
aev.rtel.irpt++;
|
|
}
|
|
}
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
}
|
|
/***************************************************************************
|
|
|
|
AdjustAevForRte for a to-be deleted point at irptAdjust
|
|
adjust the rtel's of the affected events
|
|
|
|
***************************************************************************/
|
|
void ACTR::_AdjustAevForRteDel(long irptAdjust, long iaevMin)
|
|
{
|
|
AssertBaseThis(0);
|
|
long irptMac = _pglrpt->IvMac();
|
|
AssertIn(irptAdjust, 0, irptMac);
|
|
AssertIn(iaevMin, 0, _pggaev->IvMac() + 1);
|
|
|
|
long iaev;
|
|
AEV aev;
|
|
RPT rptBack, rptAdjust;
|
|
BRS dwrBack, dwrFwd;
|
|
|
|
if (0 == irptAdjust)
|
|
dwrBack = rZero;
|
|
else
|
|
{
|
|
_pglrpt->Get(irptAdjust-1, &rptBack);
|
|
dwrBack = rptBack.dwr;
|
|
}
|
|
|
|
_pglrpt->Get(irptAdjust, &rptAdjust);
|
|
dwrFwd = rptAdjust.dwr;
|
|
|
|
for (iaev = iaevMin; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.rtel.irpt > irptAdjust) //offset from later point
|
|
{
|
|
aev.rtel.irpt--;
|
|
}
|
|
else if (aev.rtel.irpt == irptAdjust)
|
|
{
|
|
if (rptAdjust.dwr == rZero)
|
|
{
|
|
// Point will be deleted. Event must be also
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
continue;
|
|
}
|
|
aev.rtel.dwrOffset = BrsAdd(dwrBack, aev.rtel.dwrOffset);
|
|
aev.rtel.irpt--;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Locate a point on the path dwrStep further along the route from *prtel
|
|
Events which potentially modify this step (Action, Add) begin at iaevCur.
|
|
*pfEndRoute is set only when an attempt is made to move past the last
|
|
node of the subroute.
|
|
|
|
NOTE: This routine is designed to be independent of all frame dependent
|
|
state variables.
|
|
Spec: The end of a subroute is <always> reached in a moving action.
|
|
***************************************************************************/
|
|
void ACTR::_AdvanceRtel(BRS dwrStep, RTEL *prtel, long iaevCur,
|
|
long nfrmCur, bool *pfEndRoute)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(prtel);
|
|
AssertNilOrVarMem(pfEndRoute);
|
|
|
|
BRS dwrT;
|
|
bool fEndRoute = fFalse;
|
|
long iaev;
|
|
AEV aev;
|
|
|
|
// If at start of path
|
|
// Note: _nfrmFirst is independent with respect to the current frame
|
|
if (nfrmCur <= _nfrmFirst)
|
|
{
|
|
prtel->irpt = 0;
|
|
prtel->dwrOffset = rZero;
|
|
prtel->dnfrm = 0;
|
|
goto LDone;
|
|
}
|
|
|
|
// End of route means trying to move beyond the last point.
|
|
if (rZero == dwrStep) //Not an "end-of-route"
|
|
{
|
|
prtel->dnfrm++;
|
|
goto LDone;
|
|
}
|
|
|
|
// Move to the correct path segment
|
|
dwrT = ((RPT *)_pglrpt->QvGet(prtel->irpt))->dwr;
|
|
dwrT = BrsSub(dwrT, prtel->dwrOffset);
|
|
if (rZero == dwrT)
|
|
{
|
|
Assert(prtel->dwrOffset == rZero, "Expected zero offset");
|
|
prtel->dnfrm++;
|
|
fEndRoute = fTrue;
|
|
goto LDone;
|
|
}
|
|
|
|
while (dwrT <= dwrStep && rZero < dwrStep)
|
|
{
|
|
// Spec: Move the partial step
|
|
// Not an "end-of-route" - didn't try to move beyond
|
|
if (rZero == dwrT)
|
|
{
|
|
goto LDoneMove;
|
|
}
|
|
prtel->irpt++;
|
|
AssertIn(prtel->irpt, 1, _pglrpt->IvMac());
|
|
prtel->dwrOffset = rZero;
|
|
dwrStep = BrsSub(dwrStep, dwrT);
|
|
dwrT = ((RPT *)_pglrpt->QvGet(prtel->irpt))->dwr;
|
|
}
|
|
|
|
dwrT = ((RPT *)_pglrpt->QvGet(prtel->irpt))->dwr;
|
|
prtel->dwrOffset = BrsAdd(prtel->dwrOffset, dwrStep);
|
|
|
|
LDoneMove:
|
|
prtel->dnfrm = 0;
|
|
|
|
if (!_fModeRecord && !_fRejoin)
|
|
{
|
|
// Spec: Ordinarily, the actor will display at the end of this step.
|
|
// If Actn or Step events exist, the the actor is to display
|
|
// at the location of the event.
|
|
for (iaev = iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if ((aev.rtel <= *prtel) &&
|
|
(aev.aet == aetStep || aev.aet == aetActn))
|
|
{
|
|
*prtel = aev.rtel;
|
|
break;
|
|
}
|
|
if (aev.rtel > *prtel)
|
|
break;
|
|
}
|
|
}
|
|
|
|
LDone:
|
|
if (pvNil != pfEndRoute)
|
|
*pfEndRoute = fEndRoute;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Convert a route location (rtel) to an xyz point (in *pxyz)
|
|
|
|
***************************************************************************/
|
|
void ACTR::_GetXyzFromRtel(RTEL *prtel, PXYZ pxyz)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(prtel);
|
|
AssertVarMem(pxyz);
|
|
|
|
RPT rpt;
|
|
RPT rptFirst;
|
|
RPT rptSecond;
|
|
BRS rFract;
|
|
|
|
if (rZero == prtel->dwrOffset ||
|
|
(prtel->irpt) + 1 == _pglrpt->IvMac())
|
|
{
|
|
_pglrpt->Get(prtel->irpt, &rpt);
|
|
*pxyz = rpt.xyz;
|
|
return;
|
|
}
|
|
|
|
_pglrpt->Get(prtel->irpt, &rptFirst);
|
|
_pglrpt->Get(1 + prtel->irpt, &rptSecond);
|
|
Assert(rptFirst.dwr >= prtel->dwrOffset,
|
|
"Possible offset bug in _GetXyzFromRtel");
|
|
|
|
rFract = BrsDiv(prtel->dwrOffset, rptFirst.dwr);
|
|
_GetXyzOnLine(&rptFirst.xyz, &rptSecond.xyz, rFract, pxyz);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Find a point rFract fractional distance between two points.
|
|
Store in *pxyz
|
|
|
|
***************************************************************************/
|
|
void ACTR::_GetXyzOnLine(PXYZ pxyzFirst, PXYZ pxyzSecond, BRS rFract,
|
|
PXYZ pxyz)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pxyzFirst);
|
|
AssertVarMem(pxyzSecond);
|
|
AssertVarMem(pxyz);
|
|
|
|
// New pt = first + (second - first) * fractoffset;
|
|
pxyz->dxr = BrsSub(pxyzSecond->dxr, pxyzFirst->dxr);
|
|
pxyz->dyr = BrsSub(pxyzSecond->dyr, pxyzFirst->dyr);
|
|
pxyz->dzr = BrsSub(pxyzSecond->dzr, pxyzFirst->dzr);
|
|
|
|
pxyz->dxr = BrsMul(rFract, pxyz->dxr);
|
|
pxyz->dyr = BrsMul(rFract, pxyz->dyr);
|
|
pxyz->dzr = BrsMul(rFract, pxyz->dzr);
|
|
|
|
pxyz->dxr = BrsAdd(pxyz->dxr, pxyzFirst->dxr);
|
|
pxyz->dyr = BrsAdd(pxyz->dyr, pxyzFirst->dyr);
|
|
pxyz->dzr = BrsAdd(pxyz->dzr, pxyzFirst->dzr);
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Locate the actor (in Brender terms), first adjusting the location.
|
|
Post-impose a rotation looking forward along the route
|
|
|
|
***************************************************************************/
|
|
void ACTR::_PositionBody(XYZ *pxyz)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pxyz);
|
|
XYZ xyz;
|
|
BMAT34 bmat34; // Final orientation matrix
|
|
|
|
_MatrixRotUpdate(pxyz, &bmat34);
|
|
|
|
xyz.dxr = BrsAdd(_dxyzRte.dxr, pxyz->dxr);
|
|
xyz.dyr = BrsAdd(_dxyzRte.dyr, pxyz->dyr);
|
|
xyz.dzr = BrsAdd(_dxyzRte.dzr, pxyz->dzr);
|
|
_pbody->LocateOrient(xyz.dxr, xyz.dyr, xyz.dzr, &bmat34);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Update the orientation matrices
|
|
_xfrm.bmat34Cur must be kept current
|
|
|
|
***************************************************************************/
|
|
void ACTR::_MatrixRotUpdate(XYZ *pxyz, BMAT34 *pbmat34)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pxyz);
|
|
AssertVarMem(pbmat34);
|
|
|
|
RPT rpt;
|
|
BRA xa, ya, za;
|
|
BMAT34 bmat34TS; // Scaling matrix
|
|
BMAT34 bmat34TR; // Rotation matrix
|
|
bool fStretchSize;
|
|
AEV *paev;
|
|
|
|
_xyzCur = *pxyz;
|
|
fStretchSize = (rOne != _xfrm.aevpull.rScaleX ||
|
|
rOne != _xfrm.aevpull.rScaleY ||
|
|
rOne != _xfrm.aevpull.rScaleZ ||
|
|
rOne != _xfrm.rScaleStep);
|
|
|
|
// Set the orientation & rotation to zero-change
|
|
BrMatrix34Identity(&bmat34TS);
|
|
|
|
// Post apply the rest (face the camera) orientation
|
|
_ptmpl->GetRestOrien(&xa, &ya, &za);
|
|
BrMatrix34PostRotateX(&bmat34TS, xa);
|
|
BrMatrix34PostRotateY(&bmat34TS, ya);
|
|
BrMatrix34PostRotateZ(&bmat34TS, za);
|
|
|
|
if (fStretchSize)
|
|
{
|
|
// Apply any current stretching/squashing
|
|
// This must be applied BEFORE rotation to avoid stretching
|
|
// the actor along skewed axes
|
|
BrMatrix34PostScale(&bmat34TS, _xfrm.aevpull.rScaleX,
|
|
_xfrm.aevpull.rScaleY, _xfrm.aevpull.rScaleZ);
|
|
|
|
// Apply any current uniform sizing
|
|
if (_xfrm.rScaleStep != rOne)
|
|
{
|
|
BrMatrix34PostScale(&bmat34TS, _xfrm.rScaleStep,
|
|
_xfrm.rScaleStep, _xfrm.rScaleStep);
|
|
}
|
|
}
|
|
|
|
// bmat34Cur is the all inclusive orientation matrix
|
|
if (_fUseBmat34Cur)
|
|
{
|
|
// Single frame rotate events or static segments
|
|
BrMatrix34Mul(pbmat34, &bmat34TS, &_xfrm.bmat34Cur); // A = B * C
|
|
}
|
|
else
|
|
{
|
|
// Forward-rotate events or non-static segments
|
|
BrMatrix34Copy(&bmat34TR, &_xfrm.bmat34Fwd); //copy to bmat34TR
|
|
|
|
// Post apply the path orientation
|
|
AssertIn(_iaevAddCur, 0, _pggaev->IvMac());
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevAddCur);
|
|
_pglrpt->Get(paev->rtel.irpt, &rpt);
|
|
#ifdef BUG1899
|
|
if (_ptmpl->FIsTdt() ||
|
|
(_rtelCur.irpt == paev->rtel.irpt && _rtelCur.dwrOffset == rZero))
|
|
#else //!BUG1899
|
|
if (_ptmpl->FIsTdt() || paev->nfrm == _nfrmCur || rpt.dwr == rZero)
|
|
#endif //!BUG1899
|
|
{
|
|
// Single point subroute ->
|
|
// Post apply single point orientation to event rotations
|
|
AEVADD aevadd;
|
|
_pggaev->Get(_iaevAddCur, &aevadd);
|
|
BrMatrix34PostRotateX(&bmat34TR, aevadd.xa);
|
|
BrMatrix34PostRotateY(&bmat34TR, aevadd.ya);
|
|
BrMatrix34PostRotateZ(&bmat34TR, aevadd.za);
|
|
// Save the path part of the orientation
|
|
_xfrm.xaPath = aevadd.xa;
|
|
_xfrm.yaPath = aevadd.ya;
|
|
_xfrm.zaPath = aevadd.za;
|
|
}
|
|
else
|
|
{
|
|
// Orient along the route
|
|
_CalcRteOrient(&bmat34TR, &_xfrm.xaPath, &_xfrm.yaPath, &_xfrm.zaPath);
|
|
}
|
|
|
|
// Now combine rotation with stretching
|
|
BrMatrix34Mul(pbmat34, &bmat34TS, &bmat34TR);
|
|
|
|
// Note: The result of the entire rotation (including path) needs to
|
|
// be saved - otherwise, the actor will jump when the user first tries
|
|
// to do a tweak-rotate edit.
|
|
BrMatrix34Copy(&_xfrm.bmat34Cur, &bmat34TR); // Save final all-included rotation matrix
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Calculate the Post-imposed rotation tangent to the route
|
|
Calculation based on _iaevCur, _anidCur
|
|
NOTE: *pbmat34 is not pre-initialized
|
|
|
|
***************************************************************************/
|
|
void ACTR::_CalcRteOrient(BMAT34 *pbmat34,
|
|
BRA *pxa, BRA *pya, BRA *pza, ulong *pgrfbra)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertNilOrVarMem(pbmat34);
|
|
AssertNilOrVarMem(pxa);
|
|
AssertNilOrVarMem(pya);
|
|
AssertNilOrVarMem(pza);
|
|
|
|
long irptPrev;
|
|
long irptAdd;
|
|
XYZ xyz;
|
|
AEV aev;
|
|
RPT rpt;
|
|
|
|
if (pvNil != pxa)
|
|
*pxa = aZero;
|
|
if (pvNil != pya)
|
|
*pya = aZero;
|
|
if (pvNil != pza)
|
|
*pza = aZero;
|
|
|
|
AssertIn(_iaevAddCur, 0, _pggaev->IvMac());
|
|
_pggaev->GetFixed(_iaevAddCur, &aev);
|
|
irptAdd = aev.rtel.irpt;
|
|
irptPrev = (rZero < _rtelCur.dwrOffset || _rtelCur.irpt == aev.rtel.irpt) ?
|
|
_rtelCur.irpt : _rtelCur.irpt - 1;
|
|
|
|
Assert(rZero == _rtelCur.dwrOffset || irptPrev < _pglrpt->IvMac(),
|
|
"Incorrect offset in path");
|
|
|
|
// If at end of subpath, or if on a static segment,
|
|
// retain the orientation the actor last had
|
|
_pglrpt->Get(irptPrev, &rpt);
|
|
if (rZero == rpt.dwr)
|
|
irptPrev--;
|
|
|
|
if (irptPrev < irptAdd)
|
|
return; // Single point path
|
|
|
|
//
|
|
// Compute vector xyz as a weighted average of 3 vectors
|
|
//
|
|
xyz.dxr = rZero;
|
|
xyz.dyr = rZero;
|
|
xyz.dzr = rZero;
|
|
|
|
_UpdateXyzTan(&xyz, irptPrev, rFour);
|
|
|
|
if (irptPrev > irptAdd)
|
|
{
|
|
_UpdateXyzTan(&xyz, irptPrev - 1, rTwo);
|
|
if (irptPrev - 1 > irptAdd)
|
|
{
|
|
_UpdateXyzTan(&xyz, irptPrev - 2, rOne);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Apply the rotation determined by vector xyz
|
|
//
|
|
_ApplyRotFromVec(&xyz, pbmat34, pxa, pya, pza, pgrfbra);
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Update vector *pxyz by adding to it the normalized vector from nodes
|
|
(irpt to irpt + 1) * weighting factor rw. The result
|
|
accumulates a weighted average approximation to the tangent to the
|
|
route.
|
|
Optimized for movie PLAYing
|
|
|
|
***************************************************************************/
|
|
void ACTR::_UpdateXyzTan(XYZ *pxyz, long irpt, long rw)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
RPT rpt1;
|
|
RPT rpt2;
|
|
BRS dwr, dxr, dyr, dzr;
|
|
|
|
Assert(irpt + 1 < _pglrpt->IvMac(), "irpt out of range");
|
|
|
|
_pglrpt->Get(irpt, &rpt1);
|
|
_pglrpt->Get(irpt + 1, &rpt2);
|
|
|
|
dxr = BrsSub(rpt2.xyz.dxr, rpt1.xyz.dxr);
|
|
dyr = BrsSub(rpt2.xyz.dyr, rpt1.xyz.dyr);
|
|
dzr = BrsSub(rpt2.xyz.dzr, rpt1.xyz.dzr);
|
|
|
|
//
|
|
// Normalize the vector (norm ~ max + 1/2 min)
|
|
//
|
|
dwr = BrsAbsMax3(dxr, dyr, dzr);
|
|
if (dxr == dwr)
|
|
dwr = BrsAdd(dwr, (BRS)(LwMax(LwAbs((long)dyr), LwAbs((long)dzr)) >> 1));
|
|
else
|
|
{
|
|
if (dyr == dwr)
|
|
dwr = BrsAdd(dwr, (BRS)(LwMax(LwAbs((long)dxr), LwAbs((long)dzr)) >> 1));
|
|
else
|
|
dwr = BrsAdd(dwr, (BRS)(LwMax(LwAbs((long)dxr), LwAbs((long)dyr)) >> 1));
|
|
}
|
|
dxr = BrsDiv(dxr, dwr);
|
|
dyr = BrsDiv(dyr, dwr);
|
|
dzr = BrsDiv(dzr, dwr);
|
|
|
|
dxr = LwBound(dxr, -rOne, rOne);
|
|
dyr = LwBound(dyr, -rOne, rOne);
|
|
dzr = LwBound(dzr, -rOne, rOne);
|
|
|
|
//
|
|
// Apply the weight
|
|
//
|
|
if (rw != rOne)
|
|
{
|
|
dxr = BrsMul(rw, dxr);
|
|
dyr = BrsMul(rw, dyr);
|
|
dzr = BrsMul(rw, dzr);
|
|
}
|
|
|
|
pxyz->dxr = BrsAdd(pxyz->dxr, dxr);
|
|
pxyz->dyr = BrsAdd(pxyz->dyr, dyr);
|
|
pxyz->dzr = BrsAdd(pxyz->dzr, dzr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Compute the angles of rotation defined by the vector *pxyz
|
|
Apply the rotation to matrix *pbmat34
|
|
Return the angles of rotation in *pxa, *pya, *pza
|
|
Return a mask of the axes rotated
|
|
|
|
Note: Banking can be done by approximating the second derivative
|
|
which can be done using additional arguments.
|
|
|
|
***************************************************************************/
|
|
void ACTR::_ApplyRotFromVec(XYZ *pxyz, BMAT34 *pbmat34,
|
|
BRA *pxa, BRA *pya, BRA *pza, ulong *pgrfbra)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertNilOrVarMem(pbmat34);
|
|
AssertVarMem(pxyz);
|
|
AssertNilOrVarMem(pxa);
|
|
AssertNilOrVarMem(pya);
|
|
AssertNilOrVarMem(pza);
|
|
|
|
BRS dwr;
|
|
BRS rX, rY, rZ;
|
|
BRFR eX, eY, eZ, eW;
|
|
BRA aRot = aZero;
|
|
|
|
if (pvNil != pgrfbra)
|
|
*pgrfbra = 0;
|
|
|
|
/// Use cached tmpl _grfactn to determine the axes to rotate around
|
|
dwr = BrsAdd(BrsAbsMax3(pxyz->dxr, pxyz->dyr, pxyz->dzr), rOne);
|
|
|
|
// BrScalarToFraction required by Brender's BR_ATAN2 limitation
|
|
if (_grfactn & (factnRotateY | factnRotateZ))
|
|
{
|
|
rX = BrsDiv(pxyz->dxr, dwr);
|
|
eX = BrScalarToFraction(rX);
|
|
}
|
|
if (_grfactn & (factnRotateX | factnRotateZ))
|
|
{
|
|
rY = BrsDiv(pxyz->dyr, dwr);
|
|
eY = BrScalarToFraction(rY);
|
|
}
|
|
if (_grfactn & (factnRotateX | factnRotateY))
|
|
{
|
|
rZ = BrsDiv(pxyz->dzr, dwr);
|
|
eZ = BrScalarToFraction(rZ);
|
|
}
|
|
|
|
if (_grfactn & factnRotateY)
|
|
{
|
|
if (pxyz->dxr != rZero || pxyz->dzr != rZero)
|
|
{
|
|
aRot = BR_ATAN2(eX, eZ);
|
|
if (pvNil != pbmat34)
|
|
BrMatrix34PostRotateY(pbmat34, aRot);
|
|
|
|
if (pvNil != pgrfbra)
|
|
*pgrfbra |= fbraRotateY;
|
|
}
|
|
}
|
|
|
|
if (pvNil != pya)
|
|
*pya = aRot;
|
|
|
|
// Tilt the actor up / down
|
|
if (_grfactn & factnRotateX)
|
|
{
|
|
if (pxyz->dyr != rZero)
|
|
{
|
|
eW = BrScalarToFraction( BrsAdd(BrsAbs(rX), BrsAbs(rZ)));
|
|
aRot = BR_ATAN2(-eY, eW);
|
|
if (pvNil != pbmat34)
|
|
BrMatrix34PreRotateX(pbmat34, aRot);
|
|
|
|
if (pvNil != pgrfbra)
|
|
*pgrfbra |= fbraRotateX;
|
|
}
|
|
}
|
|
if (pvNil != pxa)
|
|
*pxa = aRot;
|
|
if (pvNil != pza)
|
|
*pxa = aZero;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Get the actor's event lifetime
|
|
Update nfrm values for all events
|
|
Return false if the actor has not yet been placed in time
|
|
pnfrmLast may be pvNil
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FGetLifetime(long *pnfrmFirst, long *pnfrmLast)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(pnfrmFirst);
|
|
AssertNilOrVarMem(pnfrmLast);
|
|
|
|
if (klwMax == _nfrmFirst)
|
|
return fFalse;
|
|
|
|
if (pvNil != pnfrmFirst)
|
|
*pnfrmFirst = _nfrmFirst;
|
|
|
|
if (pvNil == pnfrmLast)
|
|
return fTrue;
|
|
|
|
if (!_fLifeDirty)
|
|
{
|
|
*pnfrmLast = _nfrmLast;
|
|
Assert(_nfrmLast >= _nfrmFirst, "fLifeDirty incorrect");
|
|
return fTrue;
|
|
}
|
|
|
|
if (!_FComputeLifetime(pnfrmLast))
|
|
PushErc(ercSocBadFrameSlider);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Compute the actor's event lifetime
|
|
Update nfrm values for all events. Recording or rerecording pushes
|
|
out later subroutes, whereas recording off screen followed by an earlier
|
|
add does not - note therefore that when roll calling invisible actors,
|
|
the end of the existing subroute must first be truncated.
|
|
|
|
On a false return, it is possible that the template could not be
|
|
accessed, so the update is incomplete.
|
|
|
|
pnfrmLast may be pvNil
|
|
NOTE: Now that by spec, edits do not cross subroute boundaries,
|
|
performance optimization can be achieved by storing the
|
|
index if the first aev edited when _fLifeDirty is set.
|
|
|
|
Note: There is some amount of redundant checking here now
|
|
that subroutes are now step=0 terminated. Zero termination implies that
|
|
fEndSubRoute should be false, as actors should not be attempting to move
|
|
past the end of their paths. Note that FIsStalled() now flags the end
|
|
of step=0 terminated subroutes; stalls mid path should now be impossible
|
|
as by spec, motion fill now inherits the original movement along the path.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::_FComputeLifetime(long *pnfrmLast)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertNilOrVarMem(pnfrmLast);
|
|
|
|
long iaev;
|
|
long iaevAdd;
|
|
BRS dwr;
|
|
AEV aev;
|
|
AEV *paev;
|
|
RTEL rtel;
|
|
bool fFreezeThisCel;
|
|
long anid = 0;
|
|
long celn = 0;
|
|
long nfrmPrevSub = 0;
|
|
BRS rScale = rOne;
|
|
BRS dwrStep = rZero;
|
|
bool fEndSubRoute = fFalse; // moving past end of subroute (see Note above)
|
|
bool fDoneSubRoute = fFalse; // finished processing events on the current subroute
|
|
|
|
// While recording, delay the upcoming lifetime calculation
|
|
if (_fModeRecord)
|
|
{
|
|
if (pnfrmLast != pvNil)
|
|
*pnfrmLast = _nfrmLast;
|
|
_fLifeDirty = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
// Locate final subroute
|
|
rtel.irpt = 0;
|
|
rtel.dwrOffset = rZero;
|
|
rtel.dnfrm = -1;
|
|
iaevAdd = -1;
|
|
// REVIEW *****(SeanSe): Why not start at the end and go backwards?
|
|
for (iaev = 0; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
if (aetAdd == paev->aet)
|
|
iaevAdd = iaev;
|
|
}
|
|
|
|
if (iaevAdd < 0)
|
|
{
|
|
_nfrmLast = _nfrmFirst;
|
|
if (pvNil != pnfrmLast)
|
|
*pnfrmLast = _nfrmFirst;
|
|
_fLifeDirty = fFalse;
|
|
return fTrue;
|
|
}
|
|
|
|
// Loop through each frame
|
|
long fFrozen = fFalse;
|
|
long iaevNew = 0;
|
|
RTEL rtelOld = rtel;
|
|
rtelOld.dnfrm = rtel.dnfrm - 1;
|
|
for (_nfrmLast = _nfrmFirst ;
|
|
((rtel.irpt != _pglrpt->IvMac()) || (iaevNew != _pggaev->IvMac()));
|
|
_nfrmLast++)
|
|
{
|
|
fFreezeThisCel = fFrozen;
|
|
rtelOld = rtel;
|
|
// Find distance to move
|
|
// An aetActn event later in this same frame can modify this.
|
|
if (kdwrNil == dwrStep)
|
|
{
|
|
if (!_ptmpl->FGetDwrActnCel(anid, celn, &dwr))
|
|
{
|
|
_fLifeDirty = fTrue;
|
|
return fFalse;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwr = dwrStep;
|
|
}
|
|
dwr = BrsMul(dwr, rScale);
|
|
_AdvanceRtel(dwr, &rtel, iaevNew, _nfrmLast, &fEndSubRoute);
|
|
|
|
// Scan all events for this frame
|
|
for (iaev = iaevNew; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
|
|
if ((aev.rtel > rtel) &&
|
|
((aetAdd != aev.aet) ||
|
|
(_fModeRecord && !fEndSubRoute) ||
|
|
(!fEndSubRoute && !fDoneSubRoute)))
|
|
{
|
|
// To push out subroutes, do not process add events until finished
|
|
// with the previous subroute
|
|
goto LEndFrame;
|
|
}
|
|
|
|
switch(aev.aet)
|
|
{
|
|
case aetAdd:
|
|
fFreezeThisCel = fTrue;
|
|
anid = celn = 0;
|
|
if (aev.nfrm > _nfrmLast)
|
|
{
|
|
goto LEndFrame;
|
|
}
|
|
else
|
|
{
|
|
rtel = aev.rtel;
|
|
fDoneSubRoute = fEndSubRoute = fFalse;
|
|
nfrmPrevSub = _nfrmLast;
|
|
}
|
|
break;
|
|
|
|
case aetTweak:
|
|
case aetSnd:
|
|
case aetRem:
|
|
case aetCost:
|
|
case aetRotF:
|
|
case aetRotH:
|
|
case aetPull:
|
|
case aetMove:
|
|
break;
|
|
|
|
case aetFreeze:
|
|
long ffriz;
|
|
_pggaev->Get(iaev, &ffriz);
|
|
fFrozen = FPure(ffriz);
|
|
break;
|
|
|
|
case aetSize: // Uniform size transformation
|
|
// Adjust step size : affects actor lifetime
|
|
_pggaev->Get(iaev, &rScale);
|
|
break;
|
|
|
|
case aetActn:
|
|
AEVACTN aevactn;
|
|
|
|
_pggaev->Get(iaev, &aevactn);
|
|
anid = aevactn.anid;
|
|
celn = aevactn.celn;
|
|
fFrozen = fFalse;
|
|
fFreezeThisCel = fTrue;
|
|
break;
|
|
|
|
case aetStep:
|
|
_pggaev->Get(iaev, &dwrStep);
|
|
break;
|
|
|
|
default:
|
|
Assert(0, "Unknown event type");
|
|
break;
|
|
}
|
|
|
|
if (aev.rtel > rtel)
|
|
goto LEndFrame;
|
|
|
|
if (aev.nfrm != _nfrmLast)
|
|
{
|
|
aev.nfrm = _nfrmLast;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
|
|
}
|
|
|
|
LEndFrame:
|
|
|
|
// Stepsize and lifetime is a function of celn's
|
|
if (!fFreezeThisCel &&
|
|
!(fEndSubRoute && _FIsDoneAevSub(iaev, rtel)))
|
|
{
|
|
celn++;
|
|
}
|
|
|
|
// Check for end of subroute or stalled actor (eg breathe in place forever)
|
|
#ifdef BUG1960
|
|
// We are not finished updating events in the current subpath until we are either
|
|
// - beyond the subroute (fEndSubRoute)
|
|
// - out of events
|
|
// - stopped with no way to reach more events in the subpath (ie, stalled)
|
|
// and finished updating later events at this same path location
|
|
if (fEndSubRoute ||
|
|
(dwrStep == rZero &&
|
|
(iaev == _pggaev->IvMac() ||
|
|
(!(aev.rtel.irpt == rtel.irpt && aev.rtel.dwrOffset == rtel.dwrOffset) &&
|
|
_FIsStalled(iaevNew, &rtel)))))
|
|
#else //!BUG1960
|
|
if (fEndSubRoute || ((dwrStep == rZero) &&
|
|
(iaev == _pggaev->IvMac() || _FIsStalled(iaevNew, &rtel))))
|
|
#endif //!BUG1960
|
|
{
|
|
if (!fDoneSubRoute)
|
|
nfrmPrevSub = _nfrmLast;
|
|
fDoneSubRoute = fTrue;
|
|
|
|
// Are there more subroutes?
|
|
if (!_FFindNextAevAet(aetAdd, iaev, &iaev))
|
|
{
|
|
// For non-static motions, fEndSubRoute is set once the actor is
|
|
// beyond the end of the subroute. Adjust back one frame.
|
|
if (fEndSubRoute && (dwrStep != rZero) && (_nfrmLast > _nfrmFirst))
|
|
_nfrmLast--;
|
|
|
|
if (pvNil != pnfrmLast)
|
|
*pnfrmLast = _nfrmLast;
|
|
_fLifeDirty = fFalse;
|
|
return fTrue;
|
|
}
|
|
|
|
// Add events jump in space. Update rtel
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
rtel = aev.rtel;
|
|
|
|
// Initialization for _AdvanceRtel()
|
|
anid = celn = 0;
|
|
rtel.dnfrm--;
|
|
}
|
|
|
|
iaevNew = iaev;
|
|
}
|
|
|
|
Assert(0, "Logic error");
|
|
if (pvNil != pnfrmLast)
|
|
*pnfrmLast = _nfrmLast;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
An actor is said to be stalled if more route exists, the step size is
|
|
ever zero with no subsequent nonzero step size to follow.
|
|
|
|
Determine whether an actor is stalled on a subroute
|
|
Input: IaevFirst is the first event to check on the subroute.
|
|
*prtel is the route point to test for stalling
|
|
Return fFalse if not stalled.
|
|
If stalled, return the last active event.
|
|
***************************************************************************/
|
|
bool ACTR::_FIsStalled(long iaevFirst, RTEL *prtel, long *piaevLast)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertIn(iaevFirst, 0, _pggaev->IvMac());
|
|
AssertNilOrVarMem(piaevLast);
|
|
|
|
AEV aev;
|
|
long iaev;
|
|
|
|
for (iaev = iaevFirst; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if ((aev.rtel.irpt > prtel->irpt) ||
|
|
(aev.rtel.dwrOffset > prtel->dwrOffset))
|
|
{
|
|
if (piaevLast != pvNil)
|
|
{
|
|
*piaevLast = iaev;
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
if ((aev.rtel.irpt == prtel->irpt) &&
|
|
(aev.rtel.dwrOffset == prtel->dwrOffset))
|
|
{
|
|
if (aetStep == aev.aet)
|
|
{
|
|
BRS dwrAev;
|
|
_pggaev->Get(iaev, &dwrAev);
|
|
if (rZero != dwrAev)
|
|
return fFalse;
|
|
}
|
|
}
|
|
}
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Has the mouse been down long enough or moved far enough to qualify this
|
|
as a valid recording session (vs merely motion filling)?
|
|
|
|
NOTE: ** If sufficient time has not elapsed, intended motion fill will
|
|
inadvertently delete the remaining subroute is record is selected
|
|
instead of rerecord.
|
|
|
|
NOTE: ** This requires that SetTsInsert() have been called on mouse
|
|
down to initialize the timing of the recording session
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FIsRecordValid(BRS dxr, BRS dyr, BRS dzr, ulong tsCurrent)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_fModeRecord)
|
|
return fTrue;
|
|
|
|
BRS dwrMouse;
|
|
|
|
// Is this truly a motion fill or is there sufficient time or
|
|
// distance travelled to make this an authentic route record?
|
|
dwrMouse = BR_LENGTH3(dxr, dyr, dzr);
|
|
|
|
if ((dwrMouse < kdwrThreshRte) && ((tsCurrent - _tsInsert) < kdtsThreshRte))
|
|
return fFalse;
|
|
|
|
return fTrue;
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Begin a new route
|
|
fReplace = ftrue if recording, fFalse if rerecording
|
|
On failure, actor is restored from pactrRestore
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FBeginRecord(ulong tsCurrent, bool fReplace, PACTR pactrRestore)
|
|
{
|
|
AssertThis(0);
|
|
Assert(tsCurrent >= 0, "Invalid type");
|
|
|
|
RPT rpt;
|
|
AEV aev;
|
|
long iaev;
|
|
long nfrmAdd;
|
|
long nfrmPrev;
|
|
bool fClosestSubrte = fTrue;
|
|
|
|
_pglrpt->Get(_rtelCur.irpt, &rpt);
|
|
_fPathInserted = fFalse;
|
|
|
|
// Do not rejoin to a one point offstage stub.
|
|
_fRejoin = !fReplace && !(rZero == rpt.dwr);
|
|
if (_fRejoin)
|
|
{
|
|
long iaev;
|
|
AEV aev;
|
|
|
|
long irptNext = _rtelCur.irpt + 1;
|
|
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (irptNext < aev.rtel.irpt)
|
|
break;
|
|
if (aetRem == aev.aet)
|
|
{
|
|
_fRejoin = fFalse;
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
_AdjustAevForRteDel(irptNext, _iaevCur);
|
|
_pglrpt->Delete(irptNext);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recording must advance from the current point -> potentially
|
|
// inserting the current point
|
|
if (rZero != _rtelCur.dwrOffset)
|
|
{
|
|
BRS dwrCur = _rtelCur.dwrOffset;
|
|
_GetXyzFromRtel(&_rtelCur, &rpt.xyz);
|
|
_rtelCur.irpt++;
|
|
_rtelCur.dwrOffset = rZero;
|
|
_rtelCur.dnfrm = 0;
|
|
if (!_FInsertGgRpt(_rtelCur.irpt, &rpt, dwrCur))
|
|
goto LFail;
|
|
_GetXyzFromRtel(&_rtelCur, &_xyzCur);
|
|
_AdjustAevForRteIns(_rtelCur.irpt, 0);
|
|
}
|
|
|
|
// Determine the gaps between subroutes
|
|
_dnfrmGap = 0;
|
|
iaev = _iaevCur;
|
|
if (_fLifeDirty)
|
|
{
|
|
// Validate future nfrm values
|
|
if (!_FComputeLifetime())
|
|
{
|
|
PushErc(ercSocBadFrameSlider);
|
|
goto LFail;
|
|
}
|
|
}
|
|
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet != aetAdd)
|
|
continue;
|
|
nfrmAdd = aev.nfrm;
|
|
if (fClosestSubrte)
|
|
{
|
|
nfrmPrev = _nfrmCur;
|
|
fClosestSubrte = fFalse;
|
|
}
|
|
else
|
|
{
|
|
_pggaev->GetFixed(iaev-1, &aev);
|
|
nfrmPrev = aev.nfrm;
|
|
}
|
|
_dnfrmGap -= (nfrmAdd - nfrmPrev);
|
|
}
|
|
|
|
_rtelInsert = _rtelCur;
|
|
_fModeRecord = fTrue;
|
|
_dxyzRaw.dxr = rZero;
|
|
_dxyzRaw.dyr = rZero;
|
|
_dxyzRaw.dzr = rZero;
|
|
return fTrue;
|
|
|
|
LFail:
|
|
_RestoreFromUndo(pactrRestore);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Add to a new route
|
|
|
|
On Input:
|
|
grfmaf flags the Frozen/unfrozen state.
|
|
*pfStepFrm returns whether the frame was advanced
|
|
*pfStepRte returns whether the route was extended.
|
|
|
|
Note: During recording, dwr = rZero marks the end of the new subroute
|
|
NOTE: On failure, actor is restored from pactrRestore
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FRecordMove(BRS dxr, BRS dyr, BRS dzr, ulong grfmaf,
|
|
ulong tsCurrent, bool *pfStepFrm, bool *pfStepRte, PACTR pactrRestore)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pfStepFrm);
|
|
AssertVarMem(pfStepRte);
|
|
|
|
RPT rptCur;
|
|
XYZ xyzMouse;
|
|
XYZ dxyzT;
|
|
BRS dwrCur;
|
|
RPT rptNew;
|
|
BRS rFractMoved;
|
|
BRS dwrMouse;
|
|
|
|
Assert(_fOnStage, "Recording an actor that wasn't selectable??");
|
|
if (!_fModeRecord)
|
|
return fTrue; // Potential client call on motion fill
|
|
|
|
if (!_pggaev->FEnsureSpace( 2, kcbVarStep + kcbVarFreeze, fgrpNil) ||
|
|
!_pglrpt->FEnsureSpace(1, fgrpNil))
|
|
{
|
|
goto LFail;
|
|
}
|
|
|
|
// Smooth the unpruned raw mouse movement *before* we create the path
|
|
dxyzT.dxr = dxr;
|
|
dxyzT.dyr = dyr;
|
|
dxyzT.dzr = dzr;
|
|
dxr = BR_CONST_DIV(BrsAdd(_dxyzRaw.dxr, BR_CONST_MUL(2, dxr)), 3);
|
|
dyr = BR_CONST_DIV(BrsAdd(_dxyzRaw.dyr, BR_CONST_MUL(2, dyr)), 3);
|
|
dzr = BR_CONST_DIV(BrsAdd(_dxyzRaw.dzr, BR_CONST_MUL(2, dzr)), 3);
|
|
|
|
if (_fFrozen && !(grfmaf & fmafFreeze))
|
|
AssertDo(_FUnfreeze(), "Ensurespace should have ensured space");
|
|
if (!_fFrozen && (grfmaf & fmafFreeze))
|
|
AssertDo(_FFreeze(), "Ensurespace should have ensured space");
|
|
|
|
if (pvNil != pfStepFrm)
|
|
*pfStepFrm = fFalse;
|
|
if (pvNil != pfStepRte)
|
|
*pfStepRte = fFalse;
|
|
|
|
// Update distance from previously current point
|
|
Assert( _pglrpt->IvMac() > 0, "Illegal empty path");
|
|
|
|
// In record mode, know prev point on a route Node
|
|
// Determine if threshhold distance has been advanced
|
|
_pglrpt->Get(_rtelCur.irpt, &rptCur);
|
|
xyzMouse.dxr = BrsAdd(dxr, rptCur.xyz.dxr);
|
|
xyzMouse.dyr = BrsAdd(dyr, rptCur.xyz.dyr);
|
|
xyzMouse.dzr = BrsAdd(dzr, rptCur.xyz.dzr);
|
|
dwrMouse = BR_LENGTH3(dxr, dyr, dzr);
|
|
|
|
if (!_FGetDwrRecord(&dwrCur))
|
|
goto LFail;
|
|
|
|
if (dwrMouse < dwrCur)
|
|
{
|
|
if ((tsCurrent - _tsInsert) < kdtsThreshRte)
|
|
{
|
|
//Insufficient length and insufficient time to record a point
|
|
return fTrue;
|
|
}
|
|
|
|
if (rZero != _dwrStep)
|
|
{
|
|
AssertDo(FSetStep(rZero), "EnsureSpace insufficient");
|
|
}
|
|
|
|
_fPathInserted = fTrue;
|
|
if (pvNil != pfStepFrm)
|
|
{
|
|
*pfStepFrm = fTrue;
|
|
}
|
|
if (!_pscen->FGotoFrm(_pscen->Nfrm() + 1))
|
|
goto LFail;
|
|
return fTrue;
|
|
}
|
|
|
|
_tsInsert = tsCurrent;
|
|
_dxyzRaw = dxyzT;
|
|
|
|
//
|
|
// Insert new point. (Overlays last point if offstage)
|
|
// Length is sufficient. Truncate to correct step size
|
|
//
|
|
rFractMoved = BrsDiv(dwrCur, dwrMouse);
|
|
Assert (rZero != dwrCur, "Zero length step illegal in FRecordMove");
|
|
|
|
_GetXyzOnLine(&rptCur.xyz, &xyzMouse, rFractMoved, &rptNew.xyz);
|
|
rptNew.dwr = rZero;
|
|
|
|
// Adjust the point for ground zero. The rule is if the mouse
|
|
// is decreasing from above to below ground, stop at ground level
|
|
// The original dwr is used *by design*
|
|
if ((grfmaf & fmafGround) &&
|
|
(BrsAdd(rptCur.xyz.dyr, _dxyzRte.dyr) >= rZero) &&
|
|
(BrsAdd(rptNew.xyz.dyr, _dxyzRte.dyr) < rZero))
|
|
{
|
|
rptNew.xyz.dyr = -_dxyzRte.dyr;
|
|
}
|
|
|
|
if (!_pglrpt->FInsert(1 + _rtelCur.irpt, &rptNew))
|
|
goto LFail;
|
|
|
|
if (kdwrNil != _dwrStep)
|
|
{
|
|
AssertDo(FSetStep(kdwrNil), "EnsureSpace insufficient");
|
|
}
|
|
|
|
// Reset distance in previous xyz entry
|
|
rptCur.dwr = dwrCur;
|
|
Assert(dwrCur != rZero, "Bug in InsertRoute");
|
|
_pglrpt->Put(_rtelCur.irpt, &rptCur);
|
|
|
|
_AdjustAevForRteIns(1 + _rtelCur.irpt, _iaevCur);
|
|
_fPathInserted = fTrue;
|
|
|
|
if (pvNil != pfStepFrm)
|
|
*pfStepFrm = fTrue;
|
|
if (pvNil != pfStepRte)
|
|
*pfStepRte = fTrue;
|
|
|
|
|
|
_dnfrmGap++;
|
|
if (_dnfrmGap > 0)
|
|
_nfrmLast++;
|
|
|
|
if (_pscen->FGotoFrm(_pscen->Nfrm() + 1))
|
|
return fTrue;
|
|
|
|
LFail:
|
|
_RestoreFromUndo(pactrRestore);
|
|
return fFalse;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
End recording a new route or route section (mouse up)
|
|
Insert a freeze event if at the end of the subroute.
|
|
|
|
Input: fReplace is meaningful only if recording or rerecording (versus
|
|
motion fill. _fModeRecord is false on motion fill).
|
|
fReplace = fTrue if recording, ie, if replacing the remaining subroute
|
|
|
|
Path splicing rule: Never rejoin across Add events
|
|
Always reconnect to the existing subroute at the closest point which
|
|
occurs later in the route than where the initial re-recording began.
|
|
|
|
NOTE** Actn, Tweak, Freeze, Step & Rem events are deleted from the
|
|
section of the route being replaced. Other events are pushed up to
|
|
the join point (if rejoining) or to the next dnfrm of the current point,
|
|
if not rejoining.
|
|
|
|
On Failure: The actor is restored from pactrRestore
|
|
***************************************************************************/
|
|
// REVIEW *****(*****): Ver 2.0 As motion fill is self sufficient,
|
|
// FEndRecord() should be able to exit immediately on !_fModeRecord.
|
|
// Also, pactrRestore should be state var _pactrRecordRestore.
|
|
bool ACTR::FEndRecord(bool fReplace, PACTR pactrRestore)
|
|
{
|
|
AssertThis(0);
|
|
|
|
AEV aev;
|
|
BRS dwr;
|
|
long iaev;
|
|
long irpt;
|
|
RPT rpt;
|
|
RPT rptJoin;
|
|
RPT rptCur;
|
|
BRS dwrMin = kdwrMax;
|
|
RTEL rtelJoin = _rtelCur;
|
|
bool fJoin = fFalse;
|
|
long irptLim = _pglrpt->IvMac();
|
|
long iaevJoinFirst = _iaevCur;
|
|
long iaevNew;
|
|
|
|
// Determine whether to rejoin to the path
|
|
//REVIEW (*****): Can we assert _fOnStage?
|
|
if (_fModeRecord && (!_fOnStage || fReplace))
|
|
{
|
|
// Delete remnant path
|
|
_DeleteFwdCore(fFalse, pvNil, _iaevCur);
|
|
if (!_FFreeze())
|
|
goto LFail;
|
|
if (!FSetStep(rZero))
|
|
goto LFail;
|
|
goto LEndRecord;
|
|
}
|
|
|
|
//
|
|
// Motion Fill or Path-rejoining
|
|
//
|
|
// _fModeRecord == fFalse on motion fill
|
|
if (!_fModeRecord || !_fPathInserted || !_fOnStage)
|
|
{
|
|
// Set Action necessarily destroys end of path freeze events
|
|
// Reinsert an end of path freeze event if at end of subroute
|
|
if (_FIsDoneAevSub(_iaevCur, _rtelCur))
|
|
{
|
|
if (!_FFreeze())
|
|
goto LFail;
|
|
}
|
|
goto LEndRecord; //Nothing inserted
|
|
}
|
|
|
|
// On !fReplace, force continuation to remainder of route.
|
|
if (rZero == _dwrStep)
|
|
{
|
|
if (!FSetStep(kdwrNil))
|
|
goto LFail;
|
|
}
|
|
|
|
if (!_fRejoin)
|
|
{
|
|
// Gather the events at later time for the current path point
|
|
// Equivalent to setting the join time ahead
|
|
rtelJoin.dnfrm = 1;
|
|
}
|
|
else
|
|
{
|
|
Assert((_rtelCur.irpt < _pglrpt->IvMac() - 1),
|
|
"Not enough points to rejoin");
|
|
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet == aetAdd)
|
|
{
|
|
irptLim = aev.rtel.irpt;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Locate the Join node
|
|
rtelJoin.irpt = _rtelCur.irpt + 1;
|
|
rtelJoin.dwrOffset = rZero;
|
|
rtelJoin.dnfrm = 0;
|
|
for (irpt = 1 + _rtelCur.irpt; irpt < irptLim; irpt++)
|
|
{
|
|
_pglrpt->Get(irpt, &rpt);
|
|
dwr = BR_LENGTH3(BrsSub(_xyzCur.dxr, rpt.xyz.dxr),
|
|
BrsSub(_xyzCur.dyr, rpt.xyz.dyr),
|
|
BrsSub(_xyzCur.dzr, rpt.xyz.dzr));
|
|
if (dwr < dwrMin)
|
|
{
|
|
dwrMin = dwr;
|
|
rtelJoin.irpt = irpt;
|
|
}
|
|
|
|
// Do not join across subroutes
|
|
if (rZero == rpt.dwr)
|
|
break;
|
|
}
|
|
|
|
Assert(rtelJoin > _rtelCur, "Invalid Join point");
|
|
Assert(rZero == _rtelCur.dwrOffset, "_rtelCur invalid");
|
|
|
|
// Update the dwr of the most recently recorded point to be
|
|
// the length to the join point on the path
|
|
// NOTE: This cannot be zero (it would signal end of path)
|
|
_pglrpt->Get(_rtelCur.irpt, &rptCur);
|
|
_pglrpt->Get(rtelJoin.irpt, &rptJoin);
|
|
rptCur.dwr = BR_LENGTH3(BrsSub(_xyzCur.dxr, rptJoin.xyz.dxr),
|
|
BrsSub(_xyzCur.dyr, rptJoin.xyz.dyr),
|
|
BrsSub(_xyzCur.dzr, rptJoin.xyz.dzr));
|
|
|
|
if (rZero == rptCur.dwr)
|
|
{
|
|
//Prevent pathological end-of-route case
|
|
rptCur.dwr = rEps;
|
|
}
|
|
_pglrpt->Put(_rtelCur.irpt, &rptCur);
|
|
}
|
|
|
|
//
|
|
// Note: Motion fill exited earlier. This is rejoin-recording only (which
|
|
// may or may not actually have a path point to rejoin to).
|
|
// Move displaced events forward to the join point
|
|
// Spec: Replace is viewed in UI as Action replace, not just path replace. ->
|
|
// Delete intervening action, tweak, freeze, step and rem events.
|
|
//
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
|
|
#ifdef BUG1961
|
|
// If we were rejoin-recording from a static segment, there is
|
|
// no point to rejoin to (-> _fRejoin is fFalse). So gather
|
|
// events up to but not beyond the next add event.
|
|
if (aev.aet == aetAdd ||
|
|
(_fRejoin && aev.rtel >= rtelJoin))
|
|
#else
|
|
if (_fRejoin && aev.rtel >= rtelJoin)
|
|
#endif //!BUG1961
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (aev.rtel > _rtelInsert)
|
|
{
|
|
if (aev.aet == aetTweak ||
|
|
aev.aet == aetStep ||
|
|
aev.aet == aetRem ||
|
|
aev.aet == aetActn ||
|
|
aev.aet == aetFreeze)
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
aev.rtel = rtelJoin;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
_MergeAev(iaevJoinFirst, iaev, &iaevNew);
|
|
if (iaevNew < iaev)
|
|
iaev--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_fRejoin)
|
|
{
|
|
// Delete the path segment before the join point
|
|
for (irpt = rtelJoin.irpt - 1; irpt > _rtelCur.irpt; irpt--)
|
|
{
|
|
_AdjustAevForRteDel(irpt, _iaevCur);
|
|
_pglrpt->Delete(irpt);
|
|
}
|
|
|
|
// Spec: Do an action fill forward.
|
|
_PrepActnFill(iaevJoinFirst, _anidCur, _anidCur,
|
|
faetTweak | faetFreeze | faetActn);
|
|
}
|
|
|
|
#ifdef BUG1961
|
|
if (!fReplace && !_fRejoin)
|
|
{
|
|
if (!_FFreeze())
|
|
goto LFail;
|
|
if (!FSetStep(rZero))
|
|
goto LFail;
|
|
}
|
|
#endif //BUG1961
|
|
|
|
LEndRecord:
|
|
_fLifeDirty = fTrue;
|
|
#ifdef BUG1973
|
|
_fRejoin = fFalse;
|
|
#endif // BUG1973
|
|
_fModeRecord = fFalse;
|
|
_pscen->MarkDirty();
|
|
_pscen->InvalFrmRange();
|
|
AssertThis(fobjAssertFull);
|
|
return fTrue;
|
|
|
|
LFail:
|
|
_RestoreFromUndo(pactrRestore);
|
|
#ifdef BUG1973
|
|
_fRejoin = fFalse;
|
|
#endif
|
|
_fModeRecord = fFalse; // Redundant safety net
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Delete the path and events beyond this frame - and -
|
|
Insert a Freeze event to terminate the truncated event list
|
|
|
|
***************************************************************************/
|
|
void ACTR::DeleteFwdCore(bool fDeleteAll, bool *pfAlive, long iaevCur)
|
|
{
|
|
AssertThis(0);
|
|
|
|
bool fAlive;
|
|
long iaev;
|
|
AEV aev;
|
|
|
|
_DeleteFwdCore(fDeleteAll, &fAlive, iaevCur);
|
|
|
|
if (fAlive && _nfrmCur >= _nfrmFirst)
|
|
{
|
|
// If no space exists, truncation isn't changing a thing
|
|
if (_pggaev->FEnsureSpace( 2, kcbVarFreeze + kcbVarStep, fgrpNil ))
|
|
{
|
|
#ifdef BUG1932
|
|
if (_iaevAddCur < 0 || _pggaev->IvMac() == 0)
|
|
{
|
|
// Safety net code : don't add events if bug exists
|
|
Bug("Invalid fAlive value");
|
|
goto LEnd;
|
|
}
|
|
#ifdef DEBUG
|
|
_pggaev->GetFixed(_iaevAddCur, &aev);
|
|
Assert(aetAdd == aev.aet, "Should be an add event at _iaevAddCur");
|
|
#endif //DEBUG
|
|
if (_fOnStage)
|
|
{
|
|
// Insert terminating events ONLY if the actor is shown, not hidden
|
|
AssertDo(_FFreeze(), "Expected freeze event to succeed");
|
|
AssertDo(FSetStep(rZero), "Expected set step event to succeed");
|
|
}
|
|
else
|
|
goto LEnd;
|
|
#else //BUG1932
|
|
AssertDo(_FFreeze(), "Expected freeze event to succeed");
|
|
AssertDo(FSetStep(rZero), "Expected set step event to succeed");
|
|
#endif //!BUG1932
|
|
// By construct, step events SET the location of display
|
|
// Tweak events override the last step event.
|
|
for (iaev = _iaevCur - 1; iaev > 0; iaev--)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.rtel != _rtelCur)
|
|
break;
|
|
if (aev.aet == aetTweak)
|
|
{
|
|
_pggaev->Swap(iaev, _iaevCur - 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LEnd:
|
|
if (pvNil != pfAlive)
|
|
*pfAlive = fAlive;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Delete the path and events beyond this frame
|
|
**NOTE: This does not send the actor offstage. See FRemFromStageCore.
|
|
NOTE: While _fLifeDirty flags needed lifetime recomputation, scene's
|
|
InvalFrmRange() is _not_ called due to the special artifact of the
|
|
frame slider being spec'd to show extensions but not reductions.
|
|
|
|
On Input:
|
|
fDeleteAll specifies full route vs subroute deletion
|
|
If one frame was backed up before the delete, iaevCur is the
|
|
current event before the backup (else ivNil).
|
|
Returns *pfAlive = false if all events and route for this actor
|
|
have been deleted
|
|
|
|
***************************************************************************/
|
|
void ACTR::_DeleteFwdCore(bool fDeleteAll, bool *pfAlive, long iaevCur)
|
|
{
|
|
AssertThis(0);
|
|
|
|
long iaev, iaevDelLim;
|
|
long irpt, irptDelLim;
|
|
long irptDelFirst;
|
|
RPT rpt;
|
|
AEV *paev;
|
|
|
|
if (ivNil == iaevCur)
|
|
iaevCur = _iaevCur;
|
|
|
|
#ifndef BUG1870
|
|
// Delete this section of code: Placement orientation should no
|
|
// longer be overwritten.
|
|
// Preserve the current orientation
|
|
if (_iaevAddCur >= 0)
|
|
{
|
|
// If we are reducing the current subroute to a single point,
|
|
// it is necessary to store the current orientation for wysiwyg
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevAddCur);
|
|
if (paev->nfrm == _nfrmCur)
|
|
_SaveCurPathOrien();
|
|
}
|
|
#endif //!BUG1870
|
|
|
|
// If !fDeleteAll & only one subroute left, redefine fDeleteAll
|
|
iaevDelLim = _pggaev->IvMac();
|
|
irptDelLim = _pglrpt->IvMac();
|
|
if (!fDeleteAll)
|
|
{
|
|
fDeleteAll = fTrue;
|
|
for (iaev = iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
if (aetAdd == paev->aet)
|
|
{
|
|
fDeleteAll = fFalse;
|
|
irptDelLim = paev->rtel.irpt;
|
|
iaevDelLim = iaev;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove events beyond the current one
|
|
// excluding the last freeze event
|
|
if (0 < iaevDelLim)
|
|
{
|
|
for (iaev = iaevDelLim - 1; iaev >= _iaevCur; iaev--)
|
|
{
|
|
_RemoveAev(iaev);
|
|
_fLifeDirty = fTrue;
|
|
}
|
|
}
|
|
|
|
// Prune the corresponding route
|
|
irptDelFirst = _rtelCur.irpt + 1;
|
|
if (0 < irptDelLim)
|
|
{
|
|
// Note: Last remaining point on path is dependent on the event stream
|
|
if (_iaevCur <= 0 && fDeleteAll)
|
|
{
|
|
_pglrpt->FSetIvMac(0);
|
|
_fLifeDirty = fTrue;
|
|
_rtelCur.irpt = -1;
|
|
goto LDone;
|
|
}
|
|
else
|
|
{
|
|
// Delete the current path node only if no other events
|
|
// use the same node
|
|
if (_rtelCur.dwrOffset > rZero)
|
|
{
|
|
// Shorten the distance between the last two nodes
|
|
_TruncateSubRte(irptDelLim);
|
|
irptDelFirst++;
|
|
}
|
|
else
|
|
{
|
|
// Delete remaining subroute
|
|
for (irpt = irptDelLim - 1; irpt >= irptDelFirst; irpt--)
|
|
{
|
|
_AdjustAevForRteDel(irpt, 0);
|
|
_pglrpt->Delete(irpt);
|
|
_fLifeDirty = fTrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adjust the distance on the tail point
|
|
if (_rtelCur.irpt >= 0)
|
|
{
|
|
Assert(_pglrpt->IvMac() > 0, "Logic Error");
|
|
_pglrpt->Get(_rtelCur.irpt, &rpt);
|
|
rpt.dwr = rZero;
|
|
_pglrpt->Put(_rtelCur.irpt, &rpt);
|
|
}
|
|
}
|
|
|
|
LDone:
|
|
_pscen->MarkDirty();
|
|
if (_nfrmCur < _nfrmFirst)
|
|
{
|
|
if (fDeleteAll)
|
|
{
|
|
if (pvNil != pfAlive)
|
|
*pfAlive = fFalse;
|
|
_InitState();
|
|
return;
|
|
}
|
|
else if (_pggaev->IvMac() > 0)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(0);
|
|
_nfrmFirst = paev->nfrm;
|
|
Assert(aetAdd == paev->aet, "Bug in ACTR::DeleteFwdCore");
|
|
}
|
|
}
|
|
|
|
if (pvNil != pfAlive)
|
|
*pfAlive = fTrue;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Save the path specific part of the current orientation
|
|
|
|
***************************************************************************/
|
|
void ACTR::_SaveCurPathOrien(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
RPT rpt;
|
|
AEV aev;
|
|
ulong grfbra = 0;
|
|
|
|
if (_iaevAddCur >= 0 && !_ptmpl->FIsTdt())
|
|
{
|
|
_pggaev->GetFixed(_iaevAddCur, &aev);
|
|
_pglrpt->Get(aev.rtel.irpt, &rpt);
|
|
if (rZero != rpt.dwr) // ie, non-static path segment
|
|
{
|
|
// _xfrm.waPath is current and CalcRteOrient() cannot be called
|
|
// in all cases
|
|
SetAddOrient(_xfrm.xaPath, _xfrm.yaPath, _xfrm.zaPath, grfbra);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Delete the path and events prior to this frame
|
|
|
|
***************************************************************************/
|
|
void ACTR::DeleteBackCore(bool *pfAlive)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(pfAlive);
|
|
|
|
long iaev;
|
|
long iaevNew;
|
|
AEV aev;
|
|
BRS dwrOffsetT;
|
|
BRS dwrNew;
|
|
BRS dwrOld;
|
|
RPT rptOld;
|
|
long dnrpt;
|
|
|
|
// Nop if not yet at first frame
|
|
if (_nfrmCur <= _nfrmFirst)
|
|
{
|
|
if (pvNil != pfAlive)
|
|
*pfAlive = FPure(_pggaev->IvMac() > 0);
|
|
return;
|
|
}
|
|
|
|
// We need to update the nfrm field in aetAdd events in order to update
|
|
// _nfrmFirst below.
|
|
if (_fLifeDirty && !_FComputeLifetime())
|
|
{
|
|
// Below the new _nfrmFirst will be wrong, but that means the actor
|
|
// will appear in the wrong frame (iff _fLifeDirty and the next subroute
|
|
// got moved). Not much we can do about it.
|
|
PushErc(ercSocBadFrameSlider);
|
|
}
|
|
|
|
// If the current point is between nodes, a node will
|
|
// be inserted as the new first node of the subroute.
|
|
// Compute the dwr of the new node.
|
|
_pglrpt->Get(_rtelCur.irpt, &rptOld);
|
|
dwrOld = rptOld.dwr;
|
|
dwrNew = BrsSub(dwrOld, _rtelCur.dwrOffset);
|
|
|
|
// Preserve the current orientation
|
|
_SaveCurPathOrien();
|
|
|
|
// Adjust offsets for events occurring at points between the
|
|
// previous and next nodes
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.rtel.irpt > _rtelCur.irpt)
|
|
break;
|
|
|
|
if (aev.rtel.dwrOffset == _rtelCur.dwrOffset)
|
|
{
|
|
aev.rtel.dwrOffset = rZero;
|
|
if (aev.rtel.dnfrm < _rtelCur.dnfrm)
|
|
{
|
|
Bug("Event list out of order (dnfrm error)");
|
|
aev.rtel.dnfrm = 0;
|
|
}
|
|
else
|
|
{
|
|
aev.rtel.dnfrm -= _rtelCur.dnfrm;
|
|
}
|
|
}
|
|
|
|
if (dwrNew == rZero)
|
|
aev.rtel.dwrOffset = rZero;
|
|
else
|
|
{
|
|
// Set the event's dwrOffset to the correct
|
|
// part of the new dwr
|
|
dwrOffsetT = BrsSub(aev.rtel.dwrOffset, _rtelCur.dwrOffset);
|
|
aev.rtel.dwrOffset = dwrOffsetT;
|
|
}
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
|
|
//
|
|
// Alter the route's new first point to be _rtelCur.
|
|
// with the correct new dwr. Then update _rtelCur.
|
|
//
|
|
_GetXyzFromRtel(&_rtelCur, &rptOld.xyz);
|
|
rptOld.dwr = dwrNew;
|
|
_pglrpt->Put(_rtelCur.irpt, &rptOld);
|
|
dnrpt = _rtelCur.irpt;
|
|
_rtelCur.dnfrm = 0;
|
|
_rtelCur.dwrOffset = rZero;
|
|
_rtelCur.irpt = 0;
|
|
|
|
//
|
|
// Merge the earlier events
|
|
// Update the necessary state variables
|
|
//
|
|
for (iaev = 0; iaev < _iaevCur; iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (!_fOnStage || iaev < _iaevAddCur)
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
continue;
|
|
}
|
|
|
|
switch(aev.aet)
|
|
{
|
|
case aetRotH:
|
|
#ifdef BUG1870
|
|
// Supercede orient-rotate with current rotation
|
|
// so that static segment orientation will be preserved
|
|
// Note: events prior to _iaevFrmMin need to be included
|
|
// due to orient-rotations lasting the lifetime of static
|
|
// segments.
|
|
_pggaev->Put(iaev, &_xfrm.bmat34Cur);
|
|
goto LDefault;
|
|
#endif //BUG1870
|
|
case aetTweak:
|
|
case aetSnd:
|
|
if (iaev < _iaevFrmMin)
|
|
{
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
break;
|
|
}
|
|
if (iaev > 0)
|
|
goto LDefault;
|
|
break;
|
|
|
|
case aetActn:
|
|
AEVACTN aevactn;
|
|
_pggaev->Get(iaev, &aevactn);
|
|
aevactn.celn = _celnCur;
|
|
_pggaev->Put(iaev, &aevactn);
|
|
goto LDefault;
|
|
|
|
case aetRem:
|
|
_RemoveAev(iaev);
|
|
iaev--;
|
|
Bug("Offstage case should be already handled");
|
|
break;
|
|
|
|
default:
|
|
LDefault:
|
|
aev.rtel = _rtelCur;
|
|
aev.nfrm = _nfrmCur;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
if (iaev > 0)
|
|
_MergeAev(0, iaev, &iaevNew);
|
|
else
|
|
iaevNew = iaev;
|
|
|
|
if (iaevNew < iaev)
|
|
iaev--;
|
|
if (aev.aet == aetAdd)
|
|
_iaevAddCur = iaevNew;
|
|
if (aev.aet == aetActn)
|
|
_iaevActnCur = iaevNew;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust the irpt's of the later events
|
|
// Adjust the absolute beginning frame number
|
|
// Delete the first section of the route
|
|
// NOTE: There is no translation in time
|
|
//
|
|
if (dnrpt > 0)
|
|
{
|
|
// Delete the first section of the route
|
|
// If offstage, also delete the current point
|
|
#ifdef BUG1866
|
|
if (!_fOnStage)
|
|
dnrpt++;
|
|
#endif //BUG1866
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
aev.rtel.irpt -= dnrpt;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
#ifndef BUG1866
|
|
if (!_fOnStage)
|
|
dnrpt++;
|
|
#endif //!BUG1866
|
|
_pglrpt->Delete(0, dnrpt);
|
|
}
|
|
|
|
if (pvNil != pfAlive)
|
|
*pfAlive = FPure(_pggaev->IvMac() > 0);
|
|
|
|
if (_pggaev->IvMac() == 0)
|
|
{
|
|
_InitState();
|
|
}
|
|
else
|
|
{
|
|
// Adjust remaining state variables
|
|
_pggaev->GetFixed(0, &aev);
|
|
Assert(aev.aet == aetAdd, "An aetAdd event should be the first event");
|
|
_nfrmFirst = aev.nfrm;
|
|
_fLifeDirty = fTrue;
|
|
_iaevFrmMin = 0;
|
|
}
|
|
|
|
if (!_FComputeLifetime())
|
|
PushErc(ercSocBadFrameSlider);
|
|
_pscen->InvalFrmRange();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Truncate the last linear section of the subroute.
|
|
Make the current point a node.
|
|
|
|
Adjust event entries from the current rtel through events located
|
|
at irptDelLim.
|
|
Note: This truncates the route only, not the event list.
|
|
|
|
***************************************************************************/
|
|
void ACTR::_TruncateSubRte(long irptDelLim)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
AEV aev;
|
|
long iaev;
|
|
RPT rptNode1;
|
|
RPT rptNode2;
|
|
long iaevLim = _iaevCur;
|
|
long irpt = _rtelCur.irpt;
|
|
|
|
if (_rtelCur.dwrOffset == rZero)
|
|
return;
|
|
|
|
// Store the new length between nodes
|
|
_pglrpt->Get(irpt, &rptNode1);
|
|
rptNode1.dwr = _rtelCur.dwrOffset;
|
|
_pglrpt->Put(irpt, &rptNode1);
|
|
|
|
// Move node 2 rather than inserting a node
|
|
_GetXyzFromRtel(&_rtelCur, &rptNode2.xyz);
|
|
rptNode2.dwr = rZero;
|
|
Assert(irpt + 1 < _pglrpt->IvMac(), "Error in truncation");
|
|
_pglrpt->Put(irpt + 1, &rptNode2);
|
|
|
|
// Update _rtelCur
|
|
_rtelCur.dnfrm = 0;
|
|
_rtelCur.dwrOffset = rZero;
|
|
_rtelCur.irpt++;
|
|
|
|
// Update aev's rtel's
|
|
for (iaev = _iaevCur - 1; iaev >= 0; iaev--)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.rtel.irpt < irpt)
|
|
break;
|
|
if (aev.rtel.dwrOffset >= rptNode1.dwr)
|
|
{
|
|
aev.rtel.dwrOffset = rZero;
|
|
aev.rtel.irpt++;
|
|
_pggaev->PutFixed(iaev, &aev);
|
|
}
|
|
}
|
|
|
|
// Delete remaining subroute
|
|
for (irpt = irptDelLim - 1; irpt > _rtelCur.irpt; irpt--)
|
|
{
|
|
_AdjustAevForRteDel(irpt, 0);
|
|
_pglrpt->Delete(irpt);
|
|
_fLifeDirty = fTrue;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Is the mouse point within this actor.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FPtIn(long xp, long yp, long *pibset)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pibset);
|
|
|
|
return _pbody->FPtInBody(xp, yp, pibset);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
FMustRender. Optimizaton: Is Rerendering necessary between _nfrmCur and
|
|
nfrmLast, exclusive of the current frame?
|
|
Returns fFalse only when rerendering *known* to be unnecessary.
|
|
Otherwise returns fTrue.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FMustRender(long nfrmRenderLast)
|
|
{
|
|
AssertThis(0);
|
|
Assert(nfrmRenderLast >= _nfrmCur, "Invalid argument to FMustRender");
|
|
Assert(!_fLifeDirty, "FMustRender was called when nfrm values were invalid");
|
|
|
|
AEV *paev;
|
|
long iaev;
|
|
|
|
if (nfrmRenderLast == _nfrmCur)
|
|
goto LStill;
|
|
if (nfrmRenderLast < _nfrmFirst)
|
|
goto LStill;
|
|
if (_nfrmCur > _nfrmLast)
|
|
goto LStill;
|
|
if (_pglrpt->IvMac() == 0)
|
|
goto LStill;
|
|
|
|
// Intervening events? Sounds don't affect rendering.
|
|
if (_iaevCur < _pggaev->IvMac())
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(_iaevCur);
|
|
if (paev->nfrm <= nfrmRenderLast)
|
|
{
|
|
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
if (paev->nfrm > nfrmRenderLast)
|
|
break;
|
|
if (aetSnd == paev->aet)
|
|
continue;
|
|
if (paev->nfrm < _nfrmLast)
|
|
goto LMoving;
|
|
// Freeze and step events affect future frames
|
|
if (aetFreeze != paev->aet && aetStep != paev->aet)
|
|
goto LMoving;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_dwrStep != rZero)
|
|
goto LMoving; // moving along path
|
|
|
|
// Not advancing, but moving in place?
|
|
if (_fFrozen || _ccelCur == 1)
|
|
goto LStill;
|
|
LMoving:
|
|
return fTrue;
|
|
|
|
LStill:
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Get actor name
|
|
|
|
***************************************************************************/
|
|
void ACTR::GetName(PSTN pstn)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstn, 0);
|
|
|
|
if (!_pscen->Pmvie()->FGetName(_arid, pstn))
|
|
{
|
|
Ptmpl()->GetName(pstn);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Get actor world coordinates
|
|
|
|
***************************************************************************/
|
|
void ACTR::GetXyzWorld(BRS *pxr, BRS *pyr, BRS *pzr)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(pxr);
|
|
AssertNilOrVarMem(pyr);
|
|
AssertNilOrVarMem(pzr);
|
|
|
|
XYZ xyz;
|
|
|
|
_GetXyzFromRtel(&_rtelCur, &xyz);
|
|
|
|
if (pvNil != pxr)
|
|
*pxr = BrsAdd(xyz.dxr, _dxyzRte.dxr);
|
|
if (pvNil != pyr)
|
|
*pyr = BrsAdd(xyz.dyr, _dxyzRte.dyr);
|
|
if (pvNil != pzr)
|
|
*pzr = BrsAdd(xyz.dzr, _dxyzRte.dyr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Hilite the actor
|
|
|
|
***************************************************************************/
|
|
void ACTR::Hilite(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
BODY::SetHiliteColor(_fTimeFrozen ? kiclrTimeFreezeHilite : kiclrNormalHilite);
|
|
_pbody->Hilite();
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Change this actor's template. This gets called if the actor is based
|
|
on a 3-D Text template and the text gets edited. In addition to
|
|
changing the template itself, this function updates _pbody to conform
|
|
to the new template, and removes all costume events on body part sets
|
|
that no longer exist. For instance, if a three-letter text actor was
|
|
changed to two letters, all costume events on the third letter are
|
|
deleted.
|
|
|
|
***************************************************************************/
|
|
bool ACTR::FChangeTagTmpl(TAG *ptagTmplNew)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(ptagTmplNew);
|
|
|
|
PTMPL ptmpl;
|
|
long cbsetNew;
|
|
long iaev;
|
|
AEV aev;
|
|
AEVCOST aevcost;
|
|
|
|
ptmpl = (PTMPL)vptagm->PbacoFetch(ptagTmplNew, TMPL::FReadTmpl);
|
|
if (pvNil == ptmpl)
|
|
return fFalse;
|
|
if (ptmpl->FIsTdt())
|
|
{
|
|
if (!((PTDT)ptmpl)->FAdjustBody(_pbody))
|
|
{
|
|
ReleasePpo(&ptmpl);
|
|
return fFalse;
|
|
}
|
|
}
|
|
if (!ptmpl->FSetActnCel(_pbody, _anidCur, _celnCur))
|
|
return fFalse;
|
|
_PositionBody(&_xyzCur);
|
|
ReleasePpo(&_ptmpl);
|
|
_ptmpl = ptmpl;
|
|
TAGM::CloseTag(&_tagTmpl);
|
|
_tagTmpl = *ptagTmplNew;
|
|
TAGM::DupTag(ptagTmplNew);
|
|
|
|
// If the new TMPL is not a TDT, we're done...although currently this
|
|
// function should only be called with ptagTmplNew being a TDT
|
|
if (!ptmpl->FIsTdt())
|
|
return fTrue;
|
|
|
|
cbsetNew = _pbody->Cbset();
|
|
// Need to remove any costume events acting on ibset >= cbsetNew
|
|
// Loop backwards to prevent indexing problems since we are deleting
|
|
// events in this GG as we go
|
|
for (iaev = _pggaev->IvMac() - 1; iaev >= 0; iaev--)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
if (aev.aet == aetCost)
|
|
{
|
|
_pggaev->Get(iaev, &aevcost);
|
|
if (aevcost.ibset >= cbsetNew)
|
|
{
|
|
_RemoveAev(iaev, fTrue);
|
|
}
|
|
}
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
|
|
Assert the validity of the ACTR.
|
|
|
|
***************************************************************************/
|
|
void ACTR::AssertValid(ulong grfobj)
|
|
{
|
|
|
|
ACTR_PAR::AssertValid(fobjAllocated);
|
|
AssertNilOrPo(_pbody, 0);
|
|
AssertNilOrPo(_ptmpl, 0);
|
|
AssertPo(_pggaev, 0);
|
|
AssertPo(_pglrpt, 0);
|
|
AssertPo(_pglsmm, 0);
|
|
|
|
long iaevMac = _pggaev->IvMac();
|
|
long irptMac = _pglrpt->IvMac();
|
|
bool fTracing = FPure(grfobj & fobjAssertFull);
|
|
|
|
AssertIn(_iaevCur, -1, iaevMac + 1);
|
|
if (irptMac > 0)
|
|
AssertIn(_rtelCur.irpt, 0, irptMac);
|
|
if (ivNil != _iaevActnCur)
|
|
AssertIn(_iaevActnCur, 0, iaevMac);
|
|
if (_iaevAddCur != ivNil)
|
|
AssertIn(_iaevAddCur, 0, iaevMac);
|
|
|
|
if (fTracing)
|
|
{
|
|
RPT rpt;
|
|
long irpt;
|
|
long iaev;
|
|
AEV aev;
|
|
bool mpaetfSeen[aetLim];
|
|
RTEL rtel;
|
|
|
|
ClearPb(mpaetfSeen, size(mpaetfSeen));
|
|
|
|
rtel.irpt = -1;
|
|
rtel.dwrOffset = rZero;
|
|
rtel.dnfrm = 0;
|
|
|
|
// Supply a debug readable view of br_scalar path
|
|
for (irpt = 0; irpt < irptMac; irpt++)
|
|
{
|
|
_pglrpt->Get(irpt, &rpt);
|
|
}
|
|
|
|
AssertIn(_rtelCur.irpt, 0, irptMac + 1); // Supply a debug readable view of the event stream
|
|
_pggaev->GetFixed(0, &aev);
|
|
Assert(aetAdd == aev.aet, "BUG: No add event at front of list");
|
|
|
|
for (iaev = 0; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
_pggaev->GetFixed(iaev, &aev);
|
|
Assert(rtel <= aev.rtel, "Illegal ordering in event list");
|
|
AssertIn(aev.aet, 0, aetLim);
|
|
|
|
// Verify uniqueness of event types in a single frame
|
|
bool fNewFrame = (aev.aet == aetAdd || rtel != aev.rtel);
|
|
|
|
if (mpaetfSeen[aev.aet] == fTrue)
|
|
{
|
|
switch(aev.aet)
|
|
{
|
|
case aetCost:
|
|
case aetAdd:
|
|
case aetSnd:
|
|
break;
|
|
|
|
default:
|
|
Assert(fNewFrame, "Duplicate events in a single frame" );
|
|
}
|
|
}
|
|
|
|
if (fNewFrame)
|
|
{
|
|
ClearPb(mpaetfSeen, size(mpaetfSeen));
|
|
}
|
|
|
|
mpaetfSeen[aev.aet] = fTrue;
|
|
rtel = aev.rtel;
|
|
_pglrpt->Get(aev.rtel.irpt, &rpt);
|
|
Assert(aev.rtel.dwrOffset < rpt.dwr || rpt.dwr == rZero, "Invalid rtel.dwrOffset");
|
|
|
|
// Variable portion of aev retrieved for debug viewing
|
|
switch(aev.aet)
|
|
{
|
|
case aetAdd:
|
|
{
|
|
AEVADD aevadd;
|
|
Assert(_pggaev->Cb(iaev) == kcbVarAdd, "Corrupt size in event list");
|
|
_pggaev->Get(iaev, &aevadd);
|
|
break;
|
|
}
|
|
case aetActn:
|
|
{
|
|
AEVACTN aevactn;
|
|
_pggaev->Get(iaev, &aevactn);
|
|
Assert(_pggaev->Cb(iaev) == kcbVarActn, "Corrupt size in event list");
|
|
break;
|
|
}
|
|
case aetCost:
|
|
Assert(_pggaev->Cb(iaev) == kcbVarCost, "Corrupt size in event list");
|
|
break;
|
|
case aetRotF:
|
|
case aetRotH:
|
|
Assert(_pggaev->Cb(iaev) == kcbVarRot, "Corrupt size in event list");
|
|
break;
|
|
case aetSize:
|
|
Assert(_pggaev->Cb(iaev) == kcbVarSize, "Corrupt size in event list");
|
|
break;
|
|
case aetPull:
|
|
Assert(_pggaev->Cb(iaev) == kcbVarPull, "Corrupt size in event list");
|
|
break;
|
|
case aetSnd:
|
|
break;
|
|
case aetFreeze:
|
|
{
|
|
long faevfrz;
|
|
_pggaev->Get(iaev, &faevfrz);
|
|
Assert(_pggaev->Cb(iaev) == kcbVarFreeze, "Corrupt size in event list");
|
|
break;
|
|
}
|
|
case aetTweak:
|
|
Assert(_pggaev->Cb(iaev) == kcbVarTweak, "Corrupt size in event list");
|
|
break;
|
|
case aetStep:
|
|
{
|
|
BRS dwrAev;
|
|
_pggaev->Get(iaev, &dwrAev);
|
|
Assert(_pggaev->Cb(iaev) == kcbVarStep, "Corrupt size in event list");
|
|
break;
|
|
}
|
|
case aetRem:
|
|
break;
|
|
case aetMove:
|
|
Assert(_pggaev->Cb(iaev) == kcbVarMove, "Corrupt size in event list");
|
|
break;
|
|
default:
|
|
Assert(0, "Unknown event type");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Mark memory used by the ACTR
|
|
|
|
***************************************************************************/
|
|
void ACTR::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
long iaev;
|
|
AEV *paev;
|
|
AEVSND *paevsnd;
|
|
|
|
ACTR_PAR::MarkMem();
|
|
for (iaev = 0; iaev < _pggaev->IvMac(); iaev++)
|
|
{
|
|
paev = (AEV *)_pggaev->QvFixedGet(iaev);
|
|
if (aetSnd == paev->aet)
|
|
{
|
|
paevsnd = (AEVSND *)_pggaev->QvGet(iaev);
|
|
if (paevsnd->tag.sid == ksidUseCrf)
|
|
paevsnd->tag.MarkMem();
|
|
}
|
|
}
|
|
MarkMemObj(_pggaev);
|
|
MarkMemObj(_pglrpt);
|
|
MarkMemObj(_pbody);
|
|
MarkMemObj(_ptmpl);
|
|
MarkMemObj(_pglsmm);
|
|
_tagTmpl.MarkMem();
|
|
}
|
|
|
|
#endif //DEBUG
|