1116 lines
28 KiB
C++
1116 lines
28 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
|
|
tmpl.cpp: Actor template class
|
|
|
|
Primary Author: ******
|
|
Review Status: REVIEWED - any changes to this file must be reviewed!
|
|
|
|
Here's the TMPL chunk tree (* means more than one chunk can go here):
|
|
|
|
TMPL - template flags
|
|
|
|
|
+--GLPI (chid 0) - parent IDs (BODY part hierarchy)
|
|
|
|
|
+--GLBS (chid 0) - body part sets for BODY
|
|
|
|
|
+--GGCM (chid 0) - custom costumes per body part set (GG of cmids)
|
|
|
|
|
+--CMTL* (chid <cmid>) - custom material...see mtrl.h
|
|
| |
|
|
| +--MTRL*
|
|
| |
|
|
| +--TMAP
|
|
|
|
|
+--MODL* - models used in this template
|
|
|
|
|
+--ACTN* (chid <anid>) - action for this template
|
|
|
|
|
+--GGCL (chid 0) - GG of cels for this action
|
|
|
|
|
+--GLXF (chid 0) - GL of transformation matrices for this action
|
|
|
|
|
+--GLMS (chid 0) - GL of motionmatch sounds for cels of this action
|
|
|
|
About Actions: An action is an activity that a body can perform, such
|
|
as walking, jumping, breathing, or resting. Actions are broken down
|
|
into cels, where each cel describes the position of each body part of
|
|
the actor at one step or phase of the action. After reaching the last
|
|
cel, the action loops around to the beginning cel and repeats. Cels
|
|
consist of a list of cel part specs (CPS). Each CPS refers to a single
|
|
body part of an actor, such as a leg or head. The CPS tells what
|
|
BRender model to use for the body part for this cel and what matrix to
|
|
use to orient it to its parent body part. Each time the cel number is
|
|
changed, TMPL reads each CPS of the new cel and updates each body part
|
|
to use the new model and transformation matrix.
|
|
|
|
About the GGCM: it is indexed by body part set, and tells how many
|
|
custom materials are available for each body part set, and what the
|
|
CMIDs are for those materials. The first CMID in each body part set
|
|
is the default one (for the default costume). Example: If a body had
|
|
3 body part sets (shirt, pants, and head), and there are 2 shirts,
|
|
3 pants, and 1 head, the GGCM would be:
|
|
|
|
0: fixed = 2, variable = (0, 1)
|
|
1: fixed = 3, variable = (2, 3, 4)
|
|
2: fixed = 1, variable = (5)
|
|
|
|
where the fixed part is the count of available CMIDs for each body
|
|
part set, and the numbers in the variable part are the CMIDs for the
|
|
respective body part sets. The default costume for this actor would
|
|
be CMID 0 for the shirt, 2 for the pants, and 5 for the head.
|
|
|
|
***************************************************************************/
|
|
#include "soc.h"
|
|
ASSERTNAME
|
|
|
|
RTCLASS(ACTN)
|
|
RTCLASS(TMPL)
|
|
|
|
/***************************************************************************
|
|
Create a new action
|
|
***************************************************************************/
|
|
PACTN ACTN::PactnNew(PGG pggcel, PGL pglbmat34, ulong grfactn)
|
|
{
|
|
AssertPo(pggcel, 0);
|
|
AssertPo(pglbmat34, 0);
|
|
|
|
PACTN pactn;
|
|
|
|
pactn = NewObj ACTN;
|
|
if (pvNil == pactn)
|
|
goto LFail;
|
|
pactn->_grfactn = grfactn;
|
|
|
|
// Duplicate pggcel into pactn->_pggcel
|
|
pactn->_pggcel = pggcel->PggDup();
|
|
if (pvNil == pactn->_pggcel)
|
|
goto LFail;
|
|
|
|
// Duplicate pglbmat34 into pactn->_pglbmat34
|
|
pactn->_pglbmat34 = pglbmat34->PglDup();
|
|
if (pvNil == pactn->_pglbmat34)
|
|
goto LFail;
|
|
|
|
AssertPo(pactn, 0);
|
|
|
|
return pactn;
|
|
LFail:
|
|
ReleasePpo(&pactn);
|
|
return pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
A PFNRPO (chunky resource reader function) to read an ACTN from a file
|
|
***************************************************************************/
|
|
bool ACTN::FReadActn(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
|
|
PBACO *ppbaco, long *pcb)
|
|
{
|
|
AssertPo(pcrf, 0);
|
|
AssertPo(pblck, 0);
|
|
AssertNilOrVarMem(ppbaco);
|
|
AssertVarMem(pcb);
|
|
|
|
ACTN *pactn;
|
|
|
|
*pcb = pblck->Cb(fTrue); // estimate ACTN size (not a good estimate)
|
|
if (pvNil == ppbaco)
|
|
return fTrue;
|
|
|
|
if (!pblck->FUnpackData())
|
|
goto LFail;
|
|
*pcb = pblck->Cb();
|
|
|
|
pactn = NewObj ACTN;
|
|
if (pvNil == pactn || !pactn->_FInit(pcrf->Pcfl(), ctg, cno))
|
|
{
|
|
ReleasePpo(&pactn);
|
|
LFail:
|
|
TrashVar(ppbaco);
|
|
TrashVar(pcb);
|
|
return fFalse;
|
|
}
|
|
AssertPo(pactn, 0);
|
|
*ppbaco = pactn;
|
|
*pcb = size(ACTN) + pactn->_pggcel->CbOnFile() +
|
|
pactn->_pglbmat34->CbOnFile();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Read an ACTN from a chunk
|
|
***************************************************************************/
|
|
ACTN::_FInit(PCFL pcfl, CTG ctg, CNO cno)
|
|
{
|
|
AssertPo(pcfl, 0);
|
|
|
|
KID kid;
|
|
BLCK blck;
|
|
ACTNF actnf;
|
|
short bo;
|
|
long icel;
|
|
|
|
if (!pcfl->FFind(ctg, cno, &blck) || !blck.FUnpackData())
|
|
return fFalse;
|
|
|
|
if (blck.Cb() < size(ACTNF))
|
|
return fFalse;
|
|
if (!blck.FReadRgb(&actnf, size(ACTNF), 0))
|
|
return fFalse;
|
|
if (kboOther == actnf.bo)
|
|
SwapBytesBom(&actnf, kbomActnf);
|
|
Assert(kboCur == actnf.bo, "bad ACTNF");
|
|
_grfactn = actnf.grfactn;
|
|
|
|
// read GG of cels (chid 0, ctg kctgGgcl):
|
|
if (!pcfl->FGetKidChidCtg(ctg, cno, 0, kctgGgcl, &kid))
|
|
return fFalse;
|
|
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
|
|
return fFalse;
|
|
_pggcel = GG::PggRead(&blck, &bo);
|
|
if (pvNil == _pggcel)
|
|
return fFalse;
|
|
AssertBomRglw(kbomCel, size(CEL));
|
|
AssertBomRgsw(kbomCps, size(CPS));
|
|
if (kboOther == bo)
|
|
{
|
|
for (icel = 0; icel < _pggcel->IvMac(); icel++)
|
|
{
|
|
SwapBytesRglw(_pggcel->QvFixedGet(icel), size(CEL) / size(long));
|
|
SwapBytesRgsw(_pggcel->QvGet(icel), _pggcel->Cb(icel) /
|
|
size(short));
|
|
}
|
|
}
|
|
|
|
// read GL of transforms (chid 0, ctg kctgGlxf):
|
|
if (!pcfl->FGetKidChidCtg(ctg, cno, 0, kctgGlxf, &kid))
|
|
return fFalse;
|
|
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
|
|
return fFalse;
|
|
_pglbmat34 = GL::PglRead(&blck, &bo);
|
|
if (pvNil == _pglbmat34)
|
|
return fFalse;
|
|
AssertBomRglw(kbomBmat34, size(BMAT34));
|
|
if (kboOther == bo)
|
|
{
|
|
SwapBytesRglw(_pglbmat34->QvGet(0), LwMul(_pglbmat34->IvMac(),
|
|
size(BMAT34) / size(long)));
|
|
}
|
|
|
|
// read (optional) GL of motion-match sounds (chid 0, ctg kctgGlms):
|
|
if (pcfl->FGetKidChidCtg(ctg, cno, 0, kctgGlms, &kid))
|
|
{
|
|
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
|
|
return fFalse;
|
|
_pgltagSnd = GL::PglRead(&blck, &bo);
|
|
if (pvNil == _pgltagSnd)
|
|
return fFalse;
|
|
AssertBomRglw(kbomTag, size(TAG));
|
|
if (kboOther == bo)
|
|
{
|
|
SwapBytesRglw(_pgltagSnd->QvGet(0), LwMul(_pgltagSnd->IvMac(),
|
|
size(TAG) / size(long)));
|
|
}
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor
|
|
***************************************************************************/
|
|
ACTN::~ACTN(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
ReleasePpo(&_pggcel);
|
|
ReleasePpo(&_pglbmat34);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a CEL
|
|
***************************************************************************/
|
|
void ACTN::GetCel(long icel, CEL *pcel)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(icel, 0, Ccel());
|
|
AssertVarMem(pcel);
|
|
|
|
_pggcel->GetFixed(icel, pcel);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a CPS
|
|
***************************************************************************/
|
|
void ACTN::GetCps(long icel, long icps, CPS *pcps)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(icel, 0, Ccel());
|
|
AssertIn(icps, 0, _pggcel->Cb(icel) / size(CPS));
|
|
AssertVarMem(pcps);
|
|
|
|
CPS *prgcps = (CPS *)_pggcel->QvGet(icel);
|
|
*pcps = prgcps[icps];
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a sound for icel. If there is no sound, ptag's CTG is set to
|
|
ctgNil.
|
|
***************************************************************************/
|
|
void ACTN::GetSnd(long icel, PTAG ptag)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(icel, 0, Ccel());
|
|
AssertVarMem(ptag);
|
|
|
|
CEL cel;
|
|
|
|
ptag->ctg = ctgNil;
|
|
if (pvNil != _pgltagSnd)
|
|
{
|
|
GetCel(icel, &cel);
|
|
if (cel.chidSnd >= 0) // negative means no sound
|
|
_pgltagSnd->Get(cel.chidSnd, ptag);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a transformation matrix
|
|
***************************************************************************/
|
|
void ACTN::GetMatrix(long imat34, BMAT34 *pbmat34)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(imat34, 0, _pglbmat34->IvMac());
|
|
AssertVarMem(pbmat34);
|
|
|
|
_pglbmat34->Get(imat34, pbmat34);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the ACTN.
|
|
***************************************************************************/
|
|
void ACTN::AssertValid(ulong grf)
|
|
{
|
|
ACTN_PAR::AssertValid(fobjAllocated);
|
|
AssertPo(_pggcel, 0);
|
|
AssertPo(_pglbmat34, 0);
|
|
AssertNilOrPo(_pgltagSnd, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the ACTN
|
|
***************************************************************************/
|
|
void ACTN::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
ACTN_PAR::MarkMem();
|
|
MarkMemObj(_pggcel);
|
|
MarkMemObj(_pglbmat34);
|
|
MarkMemObj(_pgltagSnd);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
A PFNRPO (chunky resource reader function) to read TMPL objects.
|
|
***************************************************************************/
|
|
bool TMPL::FReadTmpl(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
|
|
PBACO *ppbaco, long *pcb)
|
|
{
|
|
AssertPo(pcrf, 0);
|
|
AssertPo(pblck, 0);
|
|
AssertNilOrVarMem(ppbaco);
|
|
AssertVarMem(pcb);
|
|
|
|
TMPL *ptmpl;
|
|
KID kid;
|
|
|
|
*pcb = pblck->Cb(fTrue); // estimate TMPL size (not a good estimate)
|
|
if (pvNil == ppbaco)
|
|
return fTrue;
|
|
|
|
if (pcrf->Pcfl()->FGetKidChidCtg(ctg, cno, 0, kctgTdt, &kid))
|
|
ptmpl = NewObj TDT;
|
|
else
|
|
ptmpl = NewObj TMPL;
|
|
if (pvNil == ptmpl || !ptmpl->_FInit(pcrf->Pcfl(), ctg, cno))
|
|
{
|
|
ReleasePpo(&ptmpl);
|
|
TrashVar(ppbaco);
|
|
TrashVar(pcb);
|
|
return fFalse;
|
|
}
|
|
AssertPo(ptmpl, 0);
|
|
*ppbaco = ptmpl;
|
|
if (ptmpl->_grftmpl & ftmplTdt)
|
|
{
|
|
*pcb = size(TDT);
|
|
}
|
|
else
|
|
{
|
|
*pcb = size(TMPL) + ptmpl->_pglibactPar->CbOnFile() +
|
|
ptmpl->_pglibset->CbOnFile() + ptmpl->_pggcmid->CbOnFile();
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Read a TMPL from a chunk
|
|
***************************************************************************/
|
|
bool TMPL::_FReadTmplf(PCFL pcfl, CTG ctg, CNO cno)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
BLCK blck;
|
|
TMPLF tmplf;
|
|
|
|
if (!pcfl->FFind(ctg, cno, &blck) || !blck.FUnpackData())
|
|
return fFalse;
|
|
|
|
if (blck.Cb() < size(TMPLF))
|
|
return fFalse;
|
|
if (!blck.FReadRgb(&tmplf, size(TMPLF), 0))
|
|
return fFalse;
|
|
|
|
if (kboOther == tmplf.bo)
|
|
SwapBytesBom(&tmplf, kbomTmplf);
|
|
Assert(kboCur == tmplf.bo, "freaky tmplf!");
|
|
_xaRest = tmplf.xaRest;
|
|
_yaRest = tmplf.yaRest;
|
|
_zaRest = tmplf.zaRest;
|
|
_grftmpl = tmplf.grftmpl;
|
|
if (!pcfl->FGetName(ctg, cno, &_stn))
|
|
return fFalse;
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Write the TMPLF chunk. Creates a new chunk and returns the CNO in pcno.
|
|
|
|
Note: In Socrates, normal actor templates are read-only, but this
|
|
function will get called for TDTs.
|
|
***************************************************************************/
|
|
bool TMPL::_FWriteTmplf(PCFL pcfl, CTG ctg, CNO *pcno)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pcfl, 0);
|
|
AssertVarMem(pcno);
|
|
|
|
TMPLF tmplf;
|
|
|
|
// Add TMPL chunk
|
|
tmplf.bo = kboCur;
|
|
tmplf.osk = koskCur;
|
|
tmplf.xaRest = _xaRest;
|
|
tmplf.yaRest = _yaRest;
|
|
tmplf.zaRest = _zaRest;
|
|
tmplf.grftmpl = _grftmpl;
|
|
|
|
if (!pcfl->FAddPv(&tmplf, size(TMPLF), ctg, pcno))
|
|
return fFalse;
|
|
if (!pcfl->FSetName(kctgTmpl, *pcno, &_stn))
|
|
{
|
|
pcfl->Delete(ctg, *pcno);
|
|
return fFalse;
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Read a TMPL from a chunk
|
|
***************************************************************************/
|
|
TMPL::_FInit(PCFL pcfl, CTG ctg, CNO cno)
|
|
{
|
|
AssertPo(pcfl, 0);
|
|
|
|
KID kid;
|
|
short bo;
|
|
BLCK blck;
|
|
long ibact;
|
|
short ibset;
|
|
|
|
if (!_FReadTmplf(pcfl, ctg, cno))
|
|
return fFalse;
|
|
|
|
// read GLPI (parent tree)
|
|
if (!pcfl->FGetKidChidCtg(ctg, cno, 0, kctgGlpi, &kid))
|
|
return fFalse;
|
|
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
|
|
return fFalse;
|
|
_pglibactPar = GL::PglRead(&blck, &bo);
|
|
if (pvNil == _pglibactPar)
|
|
return fFalse;
|
|
Assert(_pglibactPar->CbEntry() == size(short), "Bad _pglibactPar!");
|
|
if (kboOther == bo)
|
|
SwapBytesRgsw(_pglibactPar->QvGet(0), _pglibactPar->IvMac());
|
|
|
|
// read GLBS (body-part-set ID list)
|
|
if (!pcfl->FGetKidChidCtg(ctg, cno, 0, kctgGlbs, &kid))
|
|
return fFalse;
|
|
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
|
|
return fFalse;
|
|
_pglibset = GL::PglRead(&blck, &bo);
|
|
if (pvNil == _pglibset)
|
|
return fFalse;
|
|
Assert(_pglibset->CbEntry() == size(short), "Bad TMPL _pglibset!");
|
|
if (kboOther == bo)
|
|
SwapBytesRgsw(_pglibset->QvGet(0), _pglibset->IvMac());
|
|
|
|
#ifdef DEBUG
|
|
// GLDC is obsolete
|
|
if (pcfl->FGetKidChidCtg(ctg, cno, 0, kctgGldc, &kid))
|
|
Warn("Obsolete GLDC structure...get rid of it");
|
|
#endif //DEBUG
|
|
|
|
// _cbset is (highest entry in _pglibset) + 1.
|
|
_cbset = -1;
|
|
for (ibact = 0; ibact < _pglibset->IvMac(); ibact++)
|
|
{
|
|
_pglibset->Get(ibact, &ibset);
|
|
if (ibset > _cbset)
|
|
_cbset = ibset;
|
|
}
|
|
_cbset++;
|
|
|
|
// Count custom costumes
|
|
_ccmid = 0;
|
|
while (pcfl->FGetKidChidCtg(ctg, cno, _ccmid, kctgCmtl, &kid))
|
|
_ccmid++;
|
|
|
|
// Count actions
|
|
_cactn = 0;
|
|
while (pcfl->FGetKidChidCtg(ctg, cno, _cactn, kctgActn, &kid))
|
|
_cactn++;
|
|
|
|
// read GGCM (costume info)
|
|
if (!pcfl->FGetKidChidCtg(ctg, cno, 0, kctgGgcm, &kid))
|
|
{
|
|
// REVIEW *****: temp until Pete updates sitobren
|
|
goto LBuildGgcm;
|
|
// return fFalse;
|
|
}
|
|
if (!pcfl->FFind(kid.cki.ctg, kid.cki.cno, &blck))
|
|
return fFalse;
|
|
_pggcmid = GG::PggRead(&blck, &bo);
|
|
if (pvNil == _pggcmid)
|
|
return fFalse;
|
|
Assert(_pggcmid->CbFixed() == size(long), "Bad TMPL _pggcmid");
|
|
Assert(_pggcmid->IvMac() == _cbset, "Bad TMPL _pggcmid");
|
|
if (kboOther == bo)
|
|
{
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
SwapBytesRglw(_pggcmid->QvFixedGet(ibset), 1);
|
|
SwapBytesRglw(_pggcmid->QvGet(ibset),
|
|
*(long *)_pggcmid->QvFixedGet(ibset));
|
|
}
|
|
}
|
|
return fTrue;
|
|
// REVIEW *****: temp code until Pete converts our TMPL content
|
|
LBuildGgcm:
|
|
long ikid;
|
|
PCMTL pcmtl;
|
|
PCRF pcrf;
|
|
long rgcmid[50];
|
|
long ccmid;
|
|
|
|
Warn("missing GGCM...building one on the fly");
|
|
|
|
pcrf = CRF::PcrfNew(pcfl, 0);
|
|
if (pvNil == pcrf)
|
|
return fFalse;
|
|
|
|
_pggcmid = GG::PggNew(size(long));
|
|
if (pvNil == _pggcmid)
|
|
{
|
|
ReleasePpo(&pcrf);
|
|
return fFalse;
|
|
}
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
ikid = 0;
|
|
ccmid = 0;
|
|
|
|
while(pcfl->FGetKid(ctg, cno, ikid++, &kid))
|
|
{
|
|
if (kid.cki.ctg != kctgCmtl)
|
|
continue;
|
|
pcmtl = (PCMTL)pcrf->PbacoFetch(kid.cki.ctg, kid.cki.cno, CMTL::FReadCmtl);
|
|
if (pvNil == pcmtl)
|
|
{
|
|
ReleasePpo(&pcrf);
|
|
return fFalse;
|
|
}
|
|
if (pcmtl->Ibset() == ibset)
|
|
{
|
|
rgcmid[ccmid++] = kid.chid;
|
|
}
|
|
ReleasePpo(&pcmtl);
|
|
}
|
|
if (!_pggcmid->FAdd(ccmid * size(long), pvNil, rgcmid, &ccmid))
|
|
{
|
|
ReleasePpo(&pcrf);
|
|
return fFalse;
|
|
}
|
|
}
|
|
ReleasePpo(&pcrf);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clean up and delete template
|
|
***************************************************************************/
|
|
TMPL::~TMPL(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
ReleasePpo(&_pglibactPar);
|
|
ReleasePpo(&_pglibset);
|
|
ReleasePpo(&_pggcmid);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return a list of all tags embedded in this TMPL. Note that a
|
|
return value of pvNil does not mean an error occurred, but simply that
|
|
this TMPL has no embedded tags.
|
|
***************************************************************************/
|
|
PGL TMPL::PgltagFetch(PCFL pcfl, CTG ctg, CNO cno, bool *pfError)
|
|
{
|
|
AssertPo(pcfl, 0);
|
|
AssertVarMem(pfError);
|
|
|
|
KID kid;
|
|
|
|
*pfError = fFalse;
|
|
if (pcfl->FGetKidChidCtg(ctg, cno, 0, kctgTdt, &kid))
|
|
return TDT::PgltagFetch(pcfl, ctg, cno, pfError);
|
|
else
|
|
return pvNil; // standard TMPLs have no embedded tags
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Creates a new tree of body parts (br_actors) based on this template.
|
|
Note: ACTR also calls FSetDefaultCost after creating the body, but
|
|
by calling it here, it is guaranteed that the body will have a material
|
|
on each body part (no null pointers for bact->material). So the user
|
|
will never see a body part that isn't texture mapped.
|
|
***************************************************************************/
|
|
PBODY TMPL::PbodyCreate(void)
|
|
{
|
|
AssertThis(0);
|
|
PBODY pbody = BODY::PbodyNew(_pglibactPar, _pglibset);
|
|
|
|
if (pvNil == pbody || !FSetDefaultCost(pbody))
|
|
{
|
|
ReleasePpo(&pbody);
|
|
return pvNil;
|
|
}
|
|
return pbody;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fills in the name of the given action
|
|
***************************************************************************/
|
|
bool TMPL::FGetActnName(long anid, PSTN pstn)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, _cactn);
|
|
AssertPo(pstn, 0);
|
|
|
|
KID kid;
|
|
|
|
if (!Pcrf()->Pcfl()->FGetKidChidCtg(Ctg(), Cno(), anid, kctgActn, &kid))
|
|
return fFalse;
|
|
return Pcrf()->Pcfl()->FGetName(kid.cki.ctg, kid.cki.cno, pstn);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reads an ACTN chunk from disk
|
|
***************************************************************************/
|
|
PACTN TMPL::_PactnFetch(long anid)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, _cactn);
|
|
|
|
KID kid;
|
|
ACTN *pactn;
|
|
CHID chidActn = anid;
|
|
|
|
if (!Pcrf()->Pcfl()->FGetKidChidCtg(Ctg(), Cno(), chidActn, kctgActn,
|
|
&kid))
|
|
{
|
|
return pvNil;
|
|
}
|
|
pactn = (ACTN *)Pcrf()->PbacoFetch(kid.cki.ctg, kid.cki.cno,
|
|
ACTN::FReadActn);
|
|
AssertNilOrPo(pactn, 0);
|
|
return pactn;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reads a MODL chunk from disk
|
|
***************************************************************************/
|
|
PMODL TMPL::_PmodlFetch(CHID chidModl)
|
|
{
|
|
AssertThis(0);
|
|
|
|
KID kid;
|
|
MODL *pmodl;
|
|
|
|
if (!Pcrf()->Pcfl()->FGetKidChidCtg(Ctg(), Cno(), chidModl, kctgBmdl,
|
|
&kid))
|
|
{
|
|
return pvNil;
|
|
}
|
|
pmodl = (MODL *)Pcrf()->PbacoFetch(kid.cki.ctg, kid.cki.cno,
|
|
MODL::FReadModl);
|
|
AssertNilOrPo(pmodl, 0);
|
|
return pmodl;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Sets up the body part tree to use the correct models and transformation
|
|
matrices for the given cel of the given action. Also returns the
|
|
distance to the next cel in *pdwr.
|
|
***************************************************************************/
|
|
bool TMPL::FSetActnCel(BODY *pbody, long anid, long celn, BRS *pdwr)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbody, 0);
|
|
AssertIn(anid, 0, _cactn);
|
|
AssertNilOrVarMem(pdwr);
|
|
|
|
long icel;
|
|
ACTN *pactn = pvNil;
|
|
CEL cel;
|
|
short ibprt;
|
|
long cbprt = _pglibactPar->IvMac();
|
|
CPS cps;
|
|
PMODL *prgpmodl = pvNil;
|
|
BMAT34 bmat34;
|
|
bool fRet = fFalse;
|
|
|
|
pactn = _PactnFetch(anid);
|
|
if (pvNil == pactn)
|
|
goto LEnd;
|
|
icel = celn % pactn->Ccel();
|
|
if (icel < 0)
|
|
icel += pactn->Ccel();
|
|
pactn->GetCel(icel, &cel);
|
|
|
|
if (!FAllocPv((void **)&prgpmodl, LwMul(cbprt, size (PMODL)), fmemClear,
|
|
mprNormal))
|
|
{
|
|
goto LEnd;
|
|
}
|
|
for (ibprt = 0; ibprt < cbprt; ibprt++)
|
|
{
|
|
pactn->GetCps(icel, ibprt, &cps);
|
|
if (chidNil == cps.chidModl)
|
|
{
|
|
// Appendages for accessories...don't smash
|
|
// accessory models with action models
|
|
Assert(pvNil == prgpmodl[ibprt], "fmemClear didn't work?");
|
|
}
|
|
else
|
|
{
|
|
prgpmodl[ibprt] = _PmodlFetch(cps.chidModl);
|
|
if (pvNil == prgpmodl[ibprt])
|
|
goto LEnd;
|
|
}
|
|
}
|
|
|
|
if (pvNil != pdwr)
|
|
*pdwr = cel.dwr;
|
|
for (ibprt = 0; ibprt < cbprt; ibprt++)
|
|
{
|
|
if (pvNil != prgpmodl[ibprt])
|
|
pbody->SetPartModel(ibprt, prgpmodl[ibprt]);
|
|
pactn->GetCps(icel, ibprt, &cps);
|
|
pactn->GetMatrix(cps.imat34, &bmat34);
|
|
pbody->SetPartMatrix(ibprt, &bmat34);
|
|
}
|
|
fRet = fTrue;
|
|
LEnd:
|
|
if (pvNil != prgpmodl)
|
|
{
|
|
for (ibprt = 0; ibprt < cbprt; ibprt++)
|
|
ReleasePpo(&prgpmodl[ibprt]);
|
|
FreePpv((void **)&prgpmodl);
|
|
}
|
|
ReleasePpo(&pactn);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Retrieves the distance travelled by cel celn of action anid.
|
|
***************************************************************************/
|
|
bool TMPL::FGetDwrActnCel(long anid, long celn, BRS *pdwr)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, _cactn);
|
|
AssertVarMem(pdwr);
|
|
|
|
ACTN *pactn;
|
|
long icel;
|
|
CEL cel;
|
|
|
|
pactn = _PactnFetch(anid);
|
|
if (pvNil == pactn)
|
|
return fFalse;
|
|
icel = celn % pactn->Ccel();
|
|
if (icel < 0)
|
|
icel += pactn->Ccel();
|
|
pactn->GetCel(icel, &cel);
|
|
ReleasePpo(&pactn);
|
|
*pdwr = cel.dwr;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Retrieves the number of cels in this action
|
|
***************************************************************************/
|
|
bool TMPL::FGetCcelActn(long anid, long *pccel)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, _cactn);
|
|
AssertVarMem(pccel);
|
|
|
|
ACTN *pactn;
|
|
|
|
pactn = _PactnFetch(anid);
|
|
if (pvNil == pactn)
|
|
return fFalse;
|
|
*pccel = pactn->Ccel();
|
|
ReleasePpo(&pactn);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Retrieves the number of cels in this action
|
|
***************************************************************************/
|
|
bool TMPL::FGetSndActnCel(long anid, long celn, bool *pfSoundExists,
|
|
PTAG ptag)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, _cactn);
|
|
AssertVarMem(pfSoundExists);
|
|
AssertVarMem(ptag);
|
|
|
|
ACTN *pactn;
|
|
long icel;
|
|
|
|
*pfSoundExists = fFalse;
|
|
pactn = _PactnFetch(anid);
|
|
if (pvNil == pactn)
|
|
return fFalse;
|
|
icel = celn % pactn->Ccel();
|
|
if (icel < 0)
|
|
icel += pactn->Ccel();
|
|
pactn->GetSnd(icel, ptag);
|
|
if (ptag->ctg != ctgNil)
|
|
*pfSoundExists = fTrue;
|
|
ReleasePpo(&pactn);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
Retrieves the distance travelled by cel celn of action anid.
|
|
***************************************************************************/
|
|
bool TMPL::FGetGrfactn(long anid, ulong *pgrfactn)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(anid, 0, _cactn);
|
|
AssertVarMem(pgrfactn);
|
|
|
|
ACTN *pactn;
|
|
|
|
pactn = _PactnFetch(anid);
|
|
if (pvNil == pactn)
|
|
return fFalse;
|
|
*pgrfactn = pactn->Grfactn();
|
|
ReleasePpo(&pactn);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get orientation for template when actor has no path
|
|
***************************************************************************/
|
|
void TMPL::GetRestOrien(BRA *pxa, BRA *pya, BRA *pza)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pxa);
|
|
AssertVarMem(pya);
|
|
AssertVarMem(pza);
|
|
|
|
*pxa = _xaRest;
|
|
*pya = _yaRest;
|
|
*pza = _zaRest;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Puts default costume on pbody
|
|
***************************************************************************/
|
|
bool TMPL::FSetDefaultCost(BODY *pbody)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pbody, 0);
|
|
|
|
long ibset;
|
|
long cmid;
|
|
PCMTL *prgpcmtl;
|
|
bool fRet = fFalse;
|
|
|
|
if (!FAllocPv((void **)&prgpcmtl, LwMul(_cbset, size(PCMTL)), fmemClear,
|
|
mprNormal))
|
|
{
|
|
goto LEnd;
|
|
}
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
cmid = CmidOfBset(ibset, 0);
|
|
prgpcmtl[ibset] = PcmtlFetch(cmid);
|
|
if (pvNil == prgpcmtl[ibset])
|
|
goto LEnd;
|
|
Assert(prgpcmtl[ibset]->Ibset() == ibset, "ibset's don't match");
|
|
}
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
pbody->SetPartSetCmtl(prgpcmtl[ibset]);
|
|
fRet = fTrue;
|
|
LEnd:
|
|
if (pvNil != prgpcmtl)
|
|
{
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
ReleasePpo(&prgpcmtl[ibset]);
|
|
FreePpv((void **)&prgpcmtl);
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns the number of custom materials available for ibset
|
|
***************************************************************************/
|
|
long TMPL::CcmidOfBset(long ibset)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibset, 0, _cbset);
|
|
|
|
return *(long *)_pggcmid->QvFixedGet(ibset);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns the icmid'th CMID available for ibset
|
|
***************************************************************************/
|
|
long TMPL::CmidOfBset(long ibset, long icmid)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibset, 0, _cbset);
|
|
AssertIn(icmid, 0, CcmidOfBset(ibset));
|
|
|
|
long *prgcmid;
|
|
|
|
prgcmid = (long *)_pggcmid->QvGet(ibset);
|
|
return prgcmid[icmid];
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Tells whether ibset holds accessories by checking to see if one of
|
|
its costumes has model children.
|
|
***************************************************************************/
|
|
bool TMPL::FBsetIsAccessory(long ibset)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibset, 0, _cbset);
|
|
|
|
long cmid;
|
|
KID kid;
|
|
|
|
if (pvNil == Pcrf())
|
|
return fFalse; // probably a TDT
|
|
|
|
cmid = CmidOfBset(ibset, 0);
|
|
if (!Pcrf()->Pcfl()->FGetKidChidCtg(Ctg(), Cno(), cmid, kctgCmtl,
|
|
&kid))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
return CMTL::FHasModels(Pcrf()->Pcfl(), kid.cki.ctg, kid.cki.cno);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Returns the ibset of the accessory associated with ibset, if any. If
|
|
ibset is itself an accessory, it is returned in *pibsetAcc. Otherwise,
|
|
if ibset is the parent of an accessory, that accessory is returned in
|
|
*pibsetAcc.
|
|
***************************************************************************/
|
|
bool TMPL::FIbsetAccOfIbset(long ibset, long *pibsetAcc)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(ibset, 0, _cbset);
|
|
AssertVarMem(pibsetAcc);
|
|
|
|
long ibsetT;
|
|
long ibact;
|
|
long ibactPar;
|
|
short ibsetOfIbact;
|
|
short ibsetOfIbactPar;
|
|
|
|
if (FBsetIsAccessory(ibset))
|
|
{
|
|
*pibsetAcc = ibset;
|
|
return fTrue;
|
|
}
|
|
|
|
for (ibsetT = 0; ibsetT < _cbset; ibsetT++)
|
|
{
|
|
if (FBsetIsAccessory(ibsetT))
|
|
{
|
|
// for each ibact in ibsetT, see if its parent is in ibset
|
|
for (ibact = 0; ibact < _pglibactPar->IvMac(); ibact++)
|
|
{
|
|
ibsetOfIbact = *(short *)_pglibset->QvGet(ibact);
|
|
if (ibsetT == ibsetOfIbact)
|
|
{
|
|
// see if ibact's parent in ibset
|
|
ibactPar = *(short *)_pglibactPar->QvGet(ibact);
|
|
ibsetOfIbactPar = *(short *)_pglibset->QvGet(ibactPar);
|
|
if (ibsetOfIbactPar == ibset)
|
|
{
|
|
// so ibset is a parent bset of ibsetT
|
|
*pibsetAcc = ibsetT;
|
|
return fTrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return fFalse; // ibset is not a parent bset of any accessory bset.
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
See if cmid1 and cmid2 are for the same accessory by comparing child
|
|
model chunks
|
|
***************************************************************************/
|
|
bool TMPL::FSameAccCmids(long cmid1, long cmid2)
|
|
{
|
|
AssertThis(0);
|
|
|
|
KID kid1;
|
|
KID kid2;
|
|
|
|
if (!Pcrf()->Pcfl()->FGetKidChidCtg(Ctg(), Cno(), cmid1, kctgCmtl,
|
|
&kid1) ||
|
|
!Pcrf()->Pcfl()->FGetKidChidCtg(Ctg(), Cno(), cmid2, kctgCmtl,
|
|
&kid2))
|
|
{
|
|
return fFalse; // safer to assume they're different
|
|
}
|
|
return CMTL::FEqualModels(Pcrf()->Pcfl(), kid1.cki.cno, kid2.cki.cno);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get a custom material. The cmid is really the chid under the TMPL.
|
|
***************************************************************************/
|
|
PCMTL TMPL::PcmtlFetch(long cmid)
|
|
{
|
|
AssertThis(0);
|
|
AssertIn(cmid, 0, _ccmid);
|
|
|
|
PCMTL pcmtl;
|
|
KID kid;
|
|
|
|
if (!Pcrf()->Pcfl()->FGetKidChidCtg(Ctg(), Cno(), cmid, kctgCmtl,
|
|
&kid))
|
|
{
|
|
return pvNil;
|
|
}
|
|
pcmtl = (PCMTL)Pcrf()->PbacoFetch(kid.cki.ctg, kid.cki.cno,
|
|
CMTL::FReadCmtl);
|
|
AssertNilOrPo(pcmtl, 0);
|
|
return pcmtl;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Puts the template's name into pstn
|
|
***************************************************************************/
|
|
void TMPL::GetName(PSTN pstn)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstn, 0);
|
|
|
|
*pstn = _stn;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the TMPL.
|
|
***************************************************************************/
|
|
void TMPL::AssertValid(ulong grftmpl)
|
|
{
|
|
long ibset;
|
|
long ccmid;
|
|
|
|
TMPL_PAR::AssertValid(fobjAllocated);
|
|
AssertPo(_pglibactPar, 0);
|
|
AssertPo(_pglibset, 0);
|
|
AssertPo(_pggcmid, 0);
|
|
AssertPo(&_stn, 0);
|
|
|
|
// Verify correctness of _pggcmid
|
|
Assert(_pggcmid->IvMac() == _cbset, 0);
|
|
for (ibset = 0; ibset < _cbset; ibset++)
|
|
{
|
|
ccmid = *(long *)_pggcmid->QvFixedGet(ibset);
|
|
Assert(_pggcmid->Cb(ibset) / size(long) == ccmid, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the TMPL
|
|
***************************************************************************/
|
|
void TMPL::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
TMPL_PAR::MarkMem();
|
|
MarkMemObj(_pglibactPar);
|
|
MarkMemObj(_pglibset);
|
|
MarkMemObj(_pggcmid);
|
|
}
|
|
#endif //DEBUG
|