/* Copyright (c) Microsoft Corporation. Licensed under the MIT License. */ /*************************************************************************** Actor Edit. Cut/Copy/Paste/Undo Primary authors: ACLP::(clipbd) Seanse AUND::(undo) Seanse ACTR::(undo) Seanse ACTR::(vacuum) ***** ACTR::(dup/restore) ***** Review Status: Reviewed ***************************************************************************/ #include "soc.h" ASSERTNAME RTCLASS(AUND) /*************************************************************************** Duplicate the actor from this frame through the end of subroute or (if fEntireScene) the end of the scene ***************************************************************************/ bool ACTR::FCopy(PACTR *ppactr, bool fEntireScene) { AssertThis(0); AssertVarMem(ppactr); long iaev; long iaevLast; AEV aev; AEVACTN aevactn; AEVSND aevsnd; RPT rpt; RPT rptOld; RPT *prptSrc; RPT *prptDest; (*ppactr) = PactrNew(&_tagTmpl); if (*ppactr == pvNil) { return fFalse; } // If the current point is between nodes, a node will // be inserted. Compute its dwr. _pglrpt->Get(_rtelCur.irpt, &rpt); rptOld.dwr = rpt.dwr; rpt.dwr = BrsSub(rptOld.dwr, _rtelCur.dwrOffset); // // Gather all earlier events & move to the current one // It is sufficient to begin with the current subroute // Note: Add events will require later translation of nfrm // for (iaev = _iaevAddCur; iaev < _iaevCur; iaev++) { _pggaev->GetFixed(iaev, &aev); //Is this an event we want to copy? switch (aev.aet) { case aetActn: // Copy, editing current cel only _pggaev->Get(iaev, &aevactn); aevactn.celn = _celnCur; break; // copy case aetAdd: case aetCost: case aetPull: case aetSize: case aetRotF: case aetFreeze: case aetStep: case aetMove: break; // The following events are not automatically copied case aetSnd: _pggaev->Get(iaev, &aevsnd); // Update the cno for the chid from the original movie // The scene this came from may be lost later if (!_pscen->Pmvie()->FResolveSndTag(&aevsnd.tag, aevsnd.chid)) { goto LFail; } _pggaev->Put(iaev, &aevsnd); if (aevsnd.celn == smmNil && iaev >= _iaevFrmMin) break; if (iaev > _iaevActnCur && aevsnd.celn != smmNil) { // Retain current motion match sounds break; } continue; case aetTweak: case aetRotH: #ifdef BUG1950 //REVIEW (*****): Postponed till v2.0 if (iaev >= _iaevCur) // Code change not yet verified #else //!BUG1950 if (iaev >= _iaevFrmMin) #endif //!BUG1950 { // Retain these events from this frame break; } continue; case aetRem: continue; // Do not copy default: Bug("Unknown event type... Copy or Not?"); break; } //set the event to happen right here. aev.rtel.dnfrm = 0; aev.rtel.irpt = 0; aev.rtel.dwrOffset = 0; iaevLast = (*ppactr)->_pggaev->IvMac(); // Insert aev. Tag ref count will be updated. _pggaev->Lock(); if (!(*ppactr)->_FInsertAev(iaevLast, _pggaev->Cb(iaev), (aev.aet == aetActn) ? &aevactn : _pggaev->QvGet(iaev), &aev, fFalse)) { _pggaev->Unlock(); goto LFail; } _pggaev->Unlock(); (*ppactr)->_MergeAev(0, iaevLast); } // // Copy remaining events, adjusting their locations // for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++) { _pggaev->GetFixed(iaev, &aev); if ((!fEntireScene) && (aetAdd == aev.aet) && (*ppactr)->_pggaev->IvMac() > 0) { break; } //Adjust the event indicies. aev.rtel.irpt -= _rtelCur.irpt; if (aev.rtel.irpt == 0) { if (aev.rtel.dwrOffset == _rtelCur.dwrOffset) { aev.rtel.dwrOffset = rZero; if (aev.rtel.dnfrm < _rtelCur.dnfrm) { aev.rtel.dnfrm = 0; } else { aev.rtel.dnfrm -= _rtelCur.dnfrm; } } else { if (rpt.dwr == rZero) aev.rtel.dwrOffset = rZero; else { aev.rtel.dwrOffset = BrsSub(aev.rtel.dwrOffset, _rtelCur.dwrOffset); } } } // Insert will update the tag ref count iaevLast = (*ppactr)->_pggaev->IvMac(); _pggaev->Lock(); if (!(*ppactr)->_FInsertAev(iaevLast, _pggaev->Cb(iaev), _pggaev->QvGet(iaev), &aev, fFalse)) { _pggaev->Unlock(); goto LFail; } _pggaev->Unlock(); } // // Add the point we are at. // if (!(*ppactr)->_pglrpt->FInsert(0, &rpt)) { goto LFail; } // // Copy the rest of the subroute or route // if ((_rtelCur.irpt + 1) < _pglrpt->IvMac()) { // Locate the amount of route to copy // long irptLim = _pglrpt->IvMac(); long irpt; if (!fEntireScene) { for (irpt = _rtelCur.irpt; irpt < irptLim; irpt++) { _pglrpt->Get(irpt, &rpt); if (rZero == rpt.dwr) { irptLim = irpt + 1; break; } } } if (!(*ppactr)->_pglrpt->FSetIvMac((*ppactr)->_pglrpt->IvMac() + irptLim - (_rtelCur.irpt + 1))) { goto LFail; } prptSrc = (RPT *)_pglrpt->QvGet(_rtelCur.irpt + 1); prptDest = (RPT *)(*ppactr)->_pglrpt->QvGet(1); CopyPb(prptSrc, prptDest, LwMul(irptLim - (_rtelCur.irpt + 1), size(RPT))); } else { // Mark the end of the path rpt.dwr = rZero; (*ppactr)->_pglrpt->Put(0,&rpt); } (*ppactr)->_SetStateRewound(); AssertPo(*ppactr, 0); return fTrue; LFail: ReleasePpo(ppactr); return fFalse; } /*************************************************************************** Duplicate the entire actor from *this onto *ppactr This is not from this frame on. The entire actor is duplicated. This does not duplicate the Brender body. Default is fReset = fFalse; NOTE: Upon exit, the new actor will still be in the scene. If (!fReset), all state information will have been retained. ***************************************************************************/ bool ACTR::FDup(PACTR *ppactr, bool fReset) { AssertThis(0); AssertVarMem(ppactr); long cactRef; PACTR pactrSrc = this; PACTR pactrDest; // Due to state var duplication, using NewObj, not PactrNew pactrDest = (*ppactr) = NewObj ACTR(); if (*ppactr == pvNil) { return fFalse; } // AddRef only if attached to a scene if (pvNil != _pbody) _pbody->AddRef(); _ptmpl->AddRef(); TAGM::DupTag(&_tagTmpl); // Copy over all members // Note that both copies will point to the same *_pbody & *_ptmpl cactRef = pactrDest->_cactRef; *(pactrDest) = *pactrSrc; pactrDest->_cactRef = cactRef; pactrDest->_fTimeFrozen = fFalse; if (!pactrDest->_FCreateGroups()) { goto LFail; } if (!_FDupCopy(pactrSrc, pactrDest)) { goto LFail; } if (fReset) { (*ppactr)->Reset(); } return fTrue; LFail: ReleasePpo(ppactr); return fFalse; } /*************************************************************************** Restore the actor from *ppactr onto *this ***************************************************************************/ void ACTR::Restore(PACTR pactr) { AssertThis(0); AssertVarMem(pactr); long cactRef; PACTR pactrSrc = pactr; PACTR pactrDest = this; Assert(pactr->_ptmpl == _ptmpl, "Restore ptmpl logic error"); Assert(pactr->_pbody == _pbody, "Restore pbody logic error"); // Copy over all members // Note that both copies will point to the same *_pbody & *_ptmpl cactRef = pactrDest->_cactRef; PGG pggaev = pactrDest->_pggaev; PGL pglrpt = pactrDest->_pglrpt; PGL pglsmm = pactrDest->_pglsmm; *(pactrDest) = *pactrSrc; pactrDest->_cactRef = cactRef; pactrDest->_pggaev = pggaev; pactrDest->_pglrpt = pglrpt; pactrDest->_pglsmm = pglsmm; // Swap the gl and gg structures SwapVars(&pactrSrc->_pggaev, &pactrDest->_pggaev); SwapVars(&pactrSrc->_pglrpt, &pactrDest->_pglrpt); SwapVars(&pactrSrc->_pglsmm, &pactrDest->_pglsmm); pactr->_pscen->Pmvie()->InvalViews(); return; } /*************************************************************************** Restore this actor from an undo object pactrRestore. ***************************************************************************/ void ACTR::_RestoreFromUndo(PACTR pactrRestore) { AssertBaseThis(0); AssertVarMem(pactrRestore); Assert(pactrRestore->_pbody == pvNil, "Not restoring from undo object"); long nfrmCur = _nfrmCur; PSCEN pscen = pactrRestore->_pscen; // Modify pactrRestore for Restore() pactrRestore->_pbody = _pbody; pactrRestore->_pscen = _pscen; _Hide(); // Added actor will show Restore(pactrRestore); // Restore pactrRestore to be unmodified pactrRestore->_pbody = pvNil; pactrRestore->_pscen = pscen; FGotoFrame(nfrmCur); // No further recovery meaningful _pscen->Pmvie()->InvalViews(); } /*************************************************************************** Copy the GG and GL structures for actor duplication/restoration NOTE: This is not from this frame on. The entire actor is duplicated. ***************************************************************************/ bool ACTR::_FDupCopy(PACTR pactrSrc, PACTR pactrDest) { AssertBaseThis(0); AssertPo(pactrDest->_pggaev, 0); AssertPo(pactrDest->_pglrpt, 0); RPT *prptSrc; RPT *prptDest; SMM *psmmSrc; SMM *psmmDest; // // Copy all events. // if (!pactrDest->_pggaev->FCopyEntries(pactrSrc->_pggaev, 0, 0, pactrSrc->_pggaev->IvMac())) { goto LFail; } { _pggaev->Lock(); for (long iaev = 0; iaev < _pggaev->IvMac(); iaev++) { PTAG ptag; if (_FIsIaevTag(_pggaev, iaev, &ptag)) TAGM::DupTag(ptag); } _pggaev->Unlock(); } // // Copy Route // if (pactrSrc->_pglrpt->IvMac() > 0) { if (!pactrDest->_pglrpt->FSetIvMac(pactrSrc->_pglrpt->IvMac())) { goto LFail; } prptSrc = (RPT *)pactrSrc->_pglrpt->QvGet(0); prptDest = (RPT *)pactrDest->_pglrpt->QvGet(0); CopyPb(prptSrc, prptDest, LwMul(pactrSrc->_pglrpt->IvMac(), size(RPT))); } // // Copy Smm // if (pactrSrc->_pglsmm->IvMac() > 0) { if (!pactrDest->_pglsmm->FSetIvMac(pactrSrc->_pglsmm->IvMac())) { goto LFail; } psmmSrc = (SMM *)pactrSrc->_pglsmm->QvGet(0); psmmDest = (SMM *)pactrDest->_pglsmm->QvGet(0); CopyPb(psmmSrc, psmmDest, LwMul(pactrSrc->_pglsmm->IvMac(), size(SMM))); } return fTrue; LFail: if (this != pactrDest) ReleasePpo(&pactrDest); return fFalse; } /*************************************************************************** Duplicate the indicated portion of the route from this frame on from actor "this" to actor *ppactr. Note **: The point on the route may not land on a node. If between nodes, insert a point to make the two route sections identical. ***************************************************************************/ bool ACTR::FCopyRte(PACTR *ppactr, bool fEntireScene) { AssertThis(0); AssertVarMem(ppactr); long irpt; long dnrpt; long irptLim; RPT rpt; RPT rpt1; RPT rptNode; (*ppactr) = PactrNew(&_tagTmpl); if (*ppactr == pvNil) { return fFalse; } // // Insert the current point // _GetXyzFromRtel(&_rtelCur, &rpt.xyz); _pglrpt->Get(_rtelCur.irpt, &rptNode); rpt.dwr = rZero; if (_rtelCur.dwrOffset == rZero) { rpt.dwr = rptNode.dwr; } else if (rptNode.dwr > rZero && _rtelCur.irpt < _pglrpt->IvMac() - 1) { _pglrpt->Get(_rtelCur.irpt + 1, &rpt1); rpt.dwr = BR_LENGTH3(BrsSub(rpt1.xyz.dxr, rpt.xyz.dxr), BrsSub(rpt1.xyz.dyr, rpt.xyz.dyr), BrsSub(rpt1.xyz.dzr, rpt.xyz.dzr)); if (rZero == rpt.dwr) { rpt.dwr = rEps; //Epsilon. Prevent pathological incorrect end-of-path } } if (!(*ppactr)->_pglrpt->FInsert(0, &rpt)) { goto LFail; } if ((!fEntireScene) && rpt.dwr == rZero) { goto LEnd; } // // If copying subroute only, determine amount to copy // irptLim = _pglrpt->IvMac(); if (!fEntireScene) { for (irpt = _rtelCur.irpt + 1; irpt < irptLim; irpt++) { _pglrpt->Get(irpt, &rpt); if (rZero == rpt.dwr) { irptLim = irpt + 1; break; } } } // // Copy indicated portion of the route // dnrpt = irptLim - (_rtelCur.irpt + 1); if (dnrpt > 0 && !(*ppactr)->_pglrpt->FEnsureSpace(dnrpt, fgrpNil)) goto LFail; for (irpt = _rtelCur.irpt + 1; irpt < irptLim; irpt++) { _pglrpt->Get(irpt, &rpt); if (!(*ppactr)->_pglrpt->FInsert(irpt - _rtelCur.irpt, &rpt)) { goto LFail; } if ((!fEntireScene) && (rZero == rpt.dwr)) { break; } } LEnd: (*ppactr)->_SetStateRewound(); return fTrue; LFail: ReleasePpo(ppactr); return fFalse; } /*************************************************************************** Paste the rte from the clipboard pactr to the current actor's current frame onward. On failure, unwinding is expected to be via the dup'd actor from undo. This overwrites the end of the current subroute. NOTE: To be meaningful, the pasted route section is translated to extend from the current point. ***************************************************************************/ bool ACTR::FPasteRte(PACTR pactr) { AssertThis(0); AssertVarMem(pactr); AEV aev; RPT rpt; RPT rptCur; XYZ dxyz; long iaev; long irpt; #ifdef STATIC bool fStatic; #endif //STATIC long crptDel = 0; long crptNew = pactr->_pglrpt->IvMac() - 1; if (crptNew <= 0) { PushErc(ercSocNothingToPaste); return fFalse; } if (!_pglrpt->FEnsureSpace(crptNew, fgrpNil) || !_pggaev->FEnsureSpace( 1, kcbVarStep, fgrpNil)) { return fFalse; } // // May be positioned between nodes -> potentially // insert the current point // _GetXyzFromRtel(&_rtelCur, &rptCur.xyz); if (rZero != _rtelCur.dwrOffset) { BRS dwrCur = _rtelCur.dwrOffset; _rtelCur.irpt++; _rtelCur.dwrOffset = rZero; _rtelCur.dnfrm = 0; if (!_FInsertGgRpt(_rtelCur.irpt, &rptCur, dwrCur)) return fFalse; _GetXyzFromRtel(&_rtelCur, &_xyzCur); _AdjustAevForRteIns(_rtelCur.irpt, 0); } // // Delete to the end of this *sub*route // for (irpt = _rtelCur.irpt + 1; irpt < _pglrpt->IvMac(); irpt++) { _pglrpt->Get(irpt, &rpt); crptDel++; if (rpt.dwr == rZero) { break; } } if (crptDel > 0) { _pglrpt->Delete(_rtelCur.irpt, crptDel); } // // Remove events until the end of the *sub*route // Space optimization: should precede paste // Update location pointer of events of later subroutes // for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++) { _pggaev->GetFixed(iaev, &aev); if (aev.rtel.irpt > _rtelCur.irpt + crptDel) { aev.rtel.irpt += crptNew - crptDel; _pggaev->PutFixed(iaev, &aev); continue; } else { _RemoveAev(iaev, fFalse); iaev--; } } // // Paste in the new route. // Translate the points of this section of route // Adjust the aev presently // pactr->_pglrpt->Get(0, &rpt); dxyz.dxr = BrsSub(rptCur.xyz.dxr, rpt.xyz.dxr); dxyz.dyr = BrsSub(rptCur.xyz.dyr, rpt.xyz.dyr); dxyz.dzr = BrsSub(rptCur.xyz.dzr, rpt.xyz.dzr); for (irpt = 1; irpt <= crptNew; irpt++) { pactr->_pglrpt->Get(irpt, &rpt); rpt.xyz.dxr = BrsAdd(rpt.xyz.dxr, dxyz.dxr); rpt.xyz.dyr = BrsAdd(rpt.xyz.dyr, dxyz.dyr); rpt.xyz.dzr = BrsAdd(rpt.xyz.dzr, dxyz.dzr); AssertDo(_pglrpt->FInsert(_rtelCur.irpt + irpt, &rpt), "Logic error"); } // // Set the right dwr distance from the current point to // the first point on the new section of route // _pglrpt->Get(_rtelCur.irpt + 1, &rpt); rptCur.dwr = BR_LENGTH3(BrsSub(rpt.xyz.dxr, rptCur.xyz.dxr), BrsSub(rpt.xyz.dyr, rptCur.xyz.dyr), BrsSub(rpt.xyz.dzr, rptCur.xyz.dzr)); if (rZero == rptCur.dwr) rptCur.dwr = rEps; //Epsilon. Prevent pathological incorrect end-of-path _pglrpt->Put(_rtelCur.irpt, &rptCur); #ifdef STATIC // // Force floating behavior on a static action // Assert(_iaevActnCur >= 0, "Actor has no action"); if (!_FGetStatic(_anidCur, &fStatic)) return fFalse; if (fStatic) { if (!FSetStep(kdwrNil)) return fFalse; } #else //!STATIC // Force continuation onto newly pasted path if (!FSetStep(kdwrNil)) return fFalse; #endif //!STATIC // // Set new end of path freeze & step events // long faevfrz = (long)fTrue; BRS dwrStep = rZero; aev.aet = aetFreeze; aev.rtel.irpt = _rtelCur.irpt + crptNew; aev.rtel.dnfrm = 0; aev.rtel.dwrOffset = rZero; aev.nfrm = _nfrmCur; //will be updated in ComputeLifetime() if (!_FInsertAev(_iaevCur, kcbVarFreeze, &faevfrz, &aev)) return fFalse; aev.aet = aetStep; if (!_FInsertAev(_iaevCur, kcbVarStep, &dwrStep, &aev)) return fFalse; _fLifeDirty = fTrue; _pscen->InvalFrmRange(); _pscen->MarkDirty(); _PositionBody(&_xyzCur); return fTrue; } /*************************************************************************** Put an already existing actor in this scene. ***************************************************************************/ bool ACTR::FPaste(long nfrm, SCEN *pscen) { AssertThis(0); AEV aev; RPT rpt; AEVADD aevadd; AEVSND aevsnd; BRS xrCam = rZero; BRS yrCam = rZero; BRS zrCam = kzrDefault; BRS xr, yr, zr; long iaev; long dnfrm; #ifdef BUG1888 PTMPL ptmpl; PCRF pcrf; TAG tag; // // Ensure that the tag to the TDT being pasted is in the current movie. // if (FIsTdt()) { Assert(_tagTmpl.sid == ksidUseCrf, "TDTs should be stored in a document!"); if (!pscen->Pmvie()->FEnsureAutosave(&pcrf)) { return fFalse; } if (pcrf != _tagTmpl.pcrf) { // Need to save this actor's tagTmpl in this movie because it came from another movie tag = _tagTmpl; TAGM::DupTag(&tag); // Save the tag to the movie's _pcrfAutosave. The tag now // points to the copy in this movie. if (!TAGM::FSaveTag(&tag, pcrf, fTrue)) { TAGM::CloseTag(&tag); return fFalse; } // Get a template based on the new tag ptmpl = (PTMPL)vptagm->PbacoFetch(&tag, TMPL::FReadTmpl); if (pvNil == ptmpl) { TAGM::CloseTag(&tag); return fFalse; } // Change the actor to use the new tag and template TAGM::CloseTag(&_tagTmpl); _tagTmpl = tag; ReleasePpo(&_ptmpl); _ptmpl = ptmpl; } } #endif //BUG1888 // // Update lifetime // _nfrmFirst = nfrm; _fLifeDirty = fTrue; _nfrmCur = nfrm - 1; SetPscen(pscen); // Always place actors on the "floor" _GetNewOrigin(&xr, &yr, &zr); Assert(_pggaev->IvMac() > 0, "Nothing to paste!"); _pggaev->GetFixed(0, &aev); if (aev.aet != aetAdd) return fFalse; dnfrm = aev.nfrm - nfrm; // // Begin by locating the actor at (xr,yr,zr) // There are no Full path or Sub path translations at this point. // Note: In order to place a pasted actor at the insertion point, // the translation needs to compensate for the distance // recorded in each path point -> subtract the first path point. _pglrpt->Get(0, &rpt); _dxyzSubRte.dxr = rZero; _dxyzSubRte.dyr = rZero; _dxyzSubRte.dzr = rZero; _dxyzFullRte.dxr = BrsSub(xr, rpt.xyz.dxr); _dxyzFullRte.dyr = BrsSub(yr, rpt.xyz.dyr); _dxyzFullRte.dzr = BrsSub(zr, rpt.xyz.dzr); _pggaev->Get(0, &aevadd); _dxyzFullRte.dxr = BrsSub(_dxyzFullRte.dxr, aevadd.dxr); _dxyzFullRte.dyr = BrsSub(_dxyzFullRte.dyr, aevadd.dyr); _dxyzFullRte.dzr = BrsSub(_dxyzFullRte.dzr, aevadd.dzr); // // Translate the new actor in time // Update sound events for (iaev = 0; iaev < _pggaev->IvMac(); iaev++) { _pggaev->GetFixed(iaev, &aev); if (dnfrm != 0) { aev.nfrm -= dnfrm; _pggaev->PutFixed(iaev, &aev); } if (aetSnd != aev.aet) continue; _pggaev->Get(iaev, &aevsnd); if (aevsnd.tag.sid != ksidUseCrf) continue; // Save tag (this may be a new movie) if (!aevsnd.tag.pcrf->Pcfl()->FFind(aevsnd.tag.ctg, aevsnd.tag.cno)) { PushErc(ercSocNoSndOnPaste); _RemoveAev(iaev); iaev--; continue; } if (!_pscen->Pmvie()->FSaveTagSnd(&aevsnd.tag)) { Bug("Expected to locate user sound chunk"); _RemoveAev(iaev); iaev--; } else { // Adopt this sound into the new scene if (!_pscen->Pmvie()->FChidFromUserSndCno(aevsnd.tag.cno, &aevsnd.chid)) return fFalse; // Update event _pggaev->Put(iaev, &aevsnd); } } // Rotate 3D spletters to face the camera if (_ptmpl->FIsTdt()) { _pggaev->Get(0, &aevadd); aevadd.ya = _pscen->Pbkgd()->BraRotYCamera(); _pggaev->Put(0, &aevadd); } _UpdateXyzRte(); _pscen->MarkDirty(); return fTrue; } /*************************************************************************** Make an actor look like they were just read in, and never in a scene. ***************************************************************************/ void ACTR::Reset(void) { _pscen = pvNil; ReleasePpo(&_pbody); // Sets _pbody = pvNil _nfrmCur = knfrmInvalid; _InitState(); } // // // // // BEGIN CLIPBOARD STUFF // // // // RTCLASS(ACLP) /*************************************************************************** Create an actor clipboard object This is from the current frame forward ***************************************************************************/ PACLP ACLP::PaclpNew(PACTR pactr, bool fRteOnly, bool fEntireScene) { AssertPo(pactr, 0); Assert(!fRteOnly || !fEntireScene, "Expecting subroute only"); PACLP paclp; PACTR pactrTmp; STN stn, stnCopyOf; paclp = NewObj ACLP(); if (paclp == pvNil) { return(pvNil); } if (fRteOnly) { if (!pactr->FCopyRte(&pactrTmp, fEntireScene)) { ReleasePpo(&paclp); return(pvNil); } } else { if (!pactr->FCopy(&pactrTmp, fEntireScene)) { ReleasePpo(&paclp); return(pvNil); } AssertPo(pactrTmp, 0); pactr->GetName(&stn); pactr->Pscen()->Pmvie()->Pmcc()->GetStn(idsEngineCopyOf, &stnCopyOf); if (!FEqualRgb(stn.Psz(), stnCopyOf.Psz(), CchSz(stnCopyOf.Psz()) * size(achar))) { paclp->_stnName = stnCopyOf; if (!paclp->_stnName.FAppendCh(kchSpace) || !paclp->_stnName.FAppendStn(&stn)) { PushErc(ercSocNameTooLong); ReleasePpo(&paclp); return(pvNil); } } else { paclp->_stnName = stn; } } paclp->_pactr = pactrTmp; paclp->_fRteOnly = fRteOnly; AssertPo(paclp, 0); return(paclp); } /*************************************************************************** Destroys an actor clipboard object ***************************************************************************/ ACLP::~ACLP(void) { ReleasePpo(&_pactr); } /*************************************************************************** Pastes an actor clipboard object ***************************************************************************/ bool ACLP::FPaste(PMVIE pmvie) { AssertThis(0); AssertPo(pmvie, 0); PACTR pactrNew; if (_fRteOnly) { return (pmvie->FPasteActrPath(_pactr)); } // // Duplicate the actor // if (!_pactr->FDup(&pactrNew, fTrue)) { return(fFalse); } AssertPo(pactrNew, 0); if (!pmvie->FPasteActr(pactrNew)) { ReleasePpo(&pactrNew); return(fFalse); } if (!pmvie->FNameActr(pactrNew->Arid(), &_stnName)) { pmvie->Pscen()->RemActrCore(pactrNew->Arid()); ReleasePpo(&pactrNew); return(fFalse); } ReleasePpo(&pactrNew); return(fTrue); } #ifdef DEBUG /**************************************************** * Mark memory used by the ACLP * * Parameters: * None. * * Returns: * None. * ****************************************************/ void ACLP::MarkMem(void) { ACLP_PAR::MarkMem(); MarkMemObj(_pactr); } /*************************************************************************** * Assert the validity of the ACLP * * Parameters: * grf - bit array of options * * Returns: * None. * **************************************************************************/ void ACLP::AssertValid(ulong grf) { ACLP_PAR::AssertValid(fobjAllocated); _pactr->AssertValid(grf); } #endif /*************************************************************************** Create an undo object ***************************************************************************/ bool ACTR::FCreateUndo(PACTR pactrDup, bool fSndUndo, PSTN pstn) { AssertPo(pactrDup, 0); AssertNilOrPo(pstn, 0); PAUND paund; paund = AUND::PaundNew(); if (paund == pvNil) { return fFalse; } paund->SetPactr(pactrDup); paund->SetArid(_arid); paund->SetSndUndo(fSndUndo); if (pvNil != pstn) paund->SetStn(pstn); if (!_pscen->Pmvie()->FAddUndo(paund)) { _pscen->Pmvie()->ClearUndo(); ReleasePpo(&paund); return(fFalse); } ReleasePpo(&paund); // // Detach from the scene // pactrDup->Reset(); return(fTrue); } /*************************************************************************** Add (or replace) an action, and create an undo object ***************************************************************************/ bool ACTR::FSetAction(long anid, long celn, bool fFreeze, PACTR *ppactrDup) { AssertThis(0); AssertNilOrVarMem(ppactrDup); PACTR pactrDup; if (!FDup(&pactrDup)) { return(fFalse); } if (!FSetActionCore(anid, celn, fFreeze)) { Restore(pactrDup); ReleasePpo(&pactrDup); return fFalse; } if (!FCreateUndo(pactrDup)) { Restore(pactrDup); ReleasePpo(&pactrDup); return(fFalse); } if (pvNil == ppactrDup) ReleasePpo(&pactrDup); else *ppactrDup = pactrDup; return fTrue; } /*************************************************************************** Add the event to the event list: Add actor on the stage, and create undo object. ***************************************************************************/ bool ACTR::FAddOnStage(void) { AssertThis(0); PACTR pactrDup; if (!FDup(&pactrDup)) { return(fFalse); } if (!FAddOnStageCore()) { Restore(pactrDup); ReleasePpo(&pactrDup); return fFalse; } if (!FCreateUndo(pactrDup)) { Restore(pactrDup); ReleasePpo(&pactrDup); return(fFalse); } ReleasePpo(&pactrDup); return fTrue; } /*************************************************************************** Normalize an actor. ***************************************************************************/ bool ACTR::FNormalize(ulong grfnorm) { AssertThis(0); PACTR pactrDup; if (!FDup(&pactrDup)) { return(fFalse); } if (!FNormalizeCore(grfnorm)) { Restore(pactrDup); ReleasePpo(&pactrDup); return fFalse; } if (!FCreateUndo(pactrDup)) { Restore(pactrDup); ReleasePpo(&pactrDup); return(fFalse); } ReleasePpo(&pactrDup); return fTrue; } /*************************************************************************** Set the Costume for a body part Add the event to the event list ***************************************************************************/ bool ACTR::FSetCostume(long ibset, TAG *ptag, long cmid, bool fCmtl) { AssertThis(0); Assert(fCmtl || ibset >= 0, "Invalid ibset argument"); AssertVarMem(ptag); PACTR pactrDup; FDup(&pactrDup); if (!FSetCostumeCore(ibset, ptag, cmid, fCmtl)) { Restore(pactrDup); ReleasePpo(&pactrDup); return fFalse; } if (!FCreateUndo(pactrDup)) { Restore(pactrDup); ReleasePpo(&pactrDup); return(fFalse); } ReleasePpo(&pactrDup); return fTrue; } /*************************************************************************** Delete the path and events from this frame and beyond **NOTE: This does not send the actor offstage. See FRemFromStageCore. On Input: fDeleteAll specifies full route vs subroute deletion Returns *pfAlive = false if all events and route for this actor have been deleted ***************************************************************************/ bool ACTR::FDelete(bool *pfAlive, bool fDeleteAll) { AssertThis(0); PACTR pactrDup; long iaevCurSav; AEV *paev; if (!FDup(&pactrDup)) { if (pvNil != pfAlive) TrashVar(pfAlive); return fFalse ; } // Unless we are deleting to the end of the scene, // we need to special case deletion that begins at // the same frame as the Add event - otherwise, the // code backs up one frame, putting the current frame // on the previous subroute. // Note: FDelete() does not require that the current // frame be later than _nfrmFirst. if (_iaevAddCur >= 0 && !fDeleteAll) { paev = (AEV *)_pggaev->QvFixedGet(_iaevAddCur); if (_nfrmCur == paev->nfrm) { if (!_FDeleteEntireSubrte()) goto LFail; if (pvNil != pfAlive) *pfAlive = FPure(_pggaev->IvMac() > 0); goto LDeleted; } } // Go to the previous frame to update state variables iaevCurSav = _iaevCur; if (!FGotoFrame(_nfrmCur - 1)) { goto LFail; } #ifndef BUG1870 // The next two lines are obsolete & cause placement orientation bugs // Save the current orientation _SaveCurPathOrien(); #endif //!BUG1870 DeleteFwdCore(fDeleteAll, pfAlive, iaevCurSav); // Return to original frame // _nfrmCur was decremented above if (!FGotoFrame(_nfrmCur + 1)) { goto LFail; } /* Might have deleted some sound events */ if (Pscen() != pvNil) Pscen()->UpdateSndFrame(); LDeleted: // The frame slider never required lifetime recomputation at this point. // Motion match sounds and prerendering both do, however. if (!_FComputeLifetime()) PushErc(ercSocBadFrameSlider); if (!FCreateUndo(pactrDup)) { Restore(pactrDup); ReleasePpo(&pactrDup); return fFalse; } ReleasePpo(&pactrDup); return fTrue; LFail: PushErc(ercSocGotoFrameFailure); ReleasePpo(&pactrDup); return fFalse; } /*************************************************************************** Add the event to the event list: Remove actor from the stage, and an Undo. NOTE: This should be called the call to place the actor offstage ***************************************************************************/ bool ACTR::FRemFromStage(void) { AssertThis(0); PACTR pactr; if (!FDup(&pactr)) { return(fFalse); } if (!FRemFromStageCore()) { Restore(pactr); return fFalse; } if (!FCreateUndo(pactr)) { Restore(pactr); ReleasePpo(&pactr); return(fFalse); } ReleasePpo(&pactr); return fTrue; } /**************************************************** * * Public constructor for actor undo objects. * * Parameters: * None. * * Returns: * pvNil if failure, else a pointer to the movie undo. * ****************************************************/ PAUND AUND::PaundNew() { PAUND paund; paund = NewObj AUND(); AssertNilOrPo(paund, 0); return(paund); } /**************************************************** * * Destructor for actor undo objects * ****************************************************/ AUND::~AUND(void) { AssertBaseThis(0); ReleasePpo(&_pactr); } /**************************************************** * * Does a command stored in an undo object. * * Parameters: * None. * * Returns: * fTrue if successful, else fFalse. * ****************************************************/ bool AUND::FDo(PDOCB pdocb) { AssertThis(0); AssertPo(pdocb, 0); long nfrmTmp; bool fRet; if (!_fSoonerLater) { return(FUndo(pdocb)); } nfrmTmp = _nfrm; _nfrm = _nfrmLast; fRet = FUndo(pdocb); _nfrm = nfrmTmp; return(fRet); } /**************************************************** * * Undoes a command stored in an undo object. * * Parameters: * None. * * Returns: * fTrue if successful, else fFalse. * ****************************************************/ bool AUND::FUndo(PDOCB pdocb) { AssertThis(0); AssertPo(pdocb, 0); PACTR pactr; PMVU pmvu; if (!_pmvie->FSwitchScen(_iscen)) { return(fFalse); } if (!_pmvie->Pscen()->FGotoFrm(_nfrm)) { _pmvie->ClearUndo(); return(fFalse); } _pmvie->Pmsq()->FlushMsq(); pactr = _pmvie->Pscen()->PactrFromArid(_arid); AssertNilOrPo(pactr, 0); if (pactr != pvNil) { pactr->AddRef(); } // // Have scene replace the old actor with this one // if (_pactr == pvNil) { _pmvie->Pscen()->RemActrCore(pactr->Arid()); } else { if (_stn.Cch() != 0) { // Undo actor name change STN stn; if (_pmvie->FGetName(_arid, &stn)) { // If FNameActr fails, the actor will not have // the correct name...not great, but the user's document // won't be corrupted or anything. Someone will push a // ercOom, so I ignore the return value here. _pmvie->FNameActr(_pactr->Arid(), &_stn); _stn = stn; } } if (_arid != _pactr->Arid()) { _pmvie->Pscen()->RemActrCore(_arid); _arid = _pactr->Arid(); } if (!_pmvie->Pscen()->FAddActrCore(_pactr)) { ReleasePpo(&pactr); return(fFalse); } pmvu = (PMVU)_pmvie->PddgGet(0); AssertNilOrPo(pmvu, 0); if ((pmvu != pvNil) && !pmvu->FTextMode()) { _pmvie->Pscen()->SelectActr(_pactr); } ReleasePpo(&_pactr); } _pmvie->Pscen()->InvalFrmRange(); _pactr = pactr; if (pactr != pvNil) { pactr->Reset(); } if (_fSndUndo) { _pmvie->Pmsq()->PlayMsq(); } else { _pmvie->Pmsq()->FlushMsq(); } return(fTrue); } /**************************************************** * Set the actor for this undo object. * * Parameters: * pactr - Actor to use * * Returns: * None. * ****************************************************/ void AUND::SetPactr(PACTR pactr) { AssertThis(0); _pactr = pactr; pactr->AddRef(); } #ifdef DEBUG /**************************************************** * Mark memory used by the AUND * * Parameters: * None. * * Returns: * None. * ****************************************************/ void AUND::MarkMem(void) { AssertThis(0); AUND_PAR::MarkMem(); MarkMemObj(_pactr); } /*************************************************************************** Assert the validity of the AUND. ***************************************************************************/ void AUND::AssertValid(ulong grf) { AssertNilOrPo(_pactr, 0); } #endif