Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS

Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
This commit is contained in:
Cyd
2026-06-24 21:28:16 -05:00
commit 2b8ca921cb
66341 changed files with 7923174 additions and 0 deletions
@@ -0,0 +1,718 @@
/**********************************************************************
*<
FILE: linkctrl.cpp
DESCRIPTION: An animated link controller
CREATED BY: Rolf Berteig
HISTORY: 1/25/97
*> Copyright (c) 1996, All Rights Reserved.
**********************************************************************/
#include "ctrl.h"
#include "units.h"
#include "istdplug.h"
#define LINKCTRL_NAME GetString(IDS_RB_LINKCTRL)
class LinkCtrl;
class PickLinkMode :
public PickModeCallback,
public PickNodeCallback {
public:
LinkCtrl *cont;
PickLinkMode(LinkCtrl *c) {cont=c;}
BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags);
BOOL Pick(IObjParam *ip,ViewExp *vpt);
void EnterMode(IObjParam *ip);
void ExitMode(IObjParam *ip);
BOOL RightClick(IObjParam *ip,ViewExp *vpt) {return TRUE;}
BOOL Filter(INode *node);
PickNodeCallback *GetFilter() {return this;}
};
class LinkCtrl : public ILinkCtrl {
public:
Control *tmControl; // ref 0
Tab<INode*> nodes; // ref 1-n
Tab<TimeValue> times;
static HWND hWnd;
static IObjParam *ip;
static LinkCtrl *editCont;
static ISpinnerControl *iTime;
static ICustButton *iPickOb, *iDelOb;
static PickLinkMode *pickLinkMode;
static BOOL valid;
LinkCtrl(BOOL loading=FALSE);
// Animatable methods
void DeleteThis() {delete this;}
int GetNumTargets() {return 0;}
Class_ID ClassID() {return LINKCTRL_CLASSID;}
SClass_ID SuperClassID() {return CTRL_MATRIX3_CLASS_ID;}
void GetClassName(TSTR& s) {s = LINKCTRL_NAME;}
void BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev);
void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next);
int NumSubs() {return 1;}
Animatable* SubAnim(int i) {return tmControl;}
TSTR SubAnimName(int i) {return GetString(IDS_RB_TRANSFORM);}
// Reference methods
RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage);
IOResult Save(ISave *isave);
IOResult Load(ILoad *iload);
RefTargetHandle Clone(RemapDir &remap = NoRemap());
int NumRefs() {return 1+nodes.Count();}
RefTargetHandle GetReference(int i);
void SetReference(int i, RefTargetHandle rtarg);
BOOL AssignController(Animatable *control,int subAnim);
// Control methods
void Copy(Control *from);
BOOL IsLeaf() {return FALSE;}
void CommitValue(TimeValue t) {}
void RestoreValue(TimeValue t) {}
void GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE);
void SetValue(TimeValue t, void *val, int commit=1, GetSetMethod method=CTRL_ABSOLUTE);
BOOL InheritsParentTransform() {return FALSE;}
BOOL CanInstanceController() {return FALSE;}
// Animatable methods
void CopyKeysFromTime(TimeValue src,TimeValue dst,DWORD flags) {tmControl->CopyKeysFromTime(src,dst,flags);}
BOOL IsKeyAtTime(TimeValue t,DWORD flags) {return tmControl->IsKeyAtTime(t,flags);}
BOOL GetNextKeyTime(TimeValue t,DWORD flags,TimeValue &nt) {return tmControl->GetNextKeyTime(t,flags,nt);}
int GetKeyTimes(Tab<TimeValue> &times,Interval range,DWORD flags) {return tmControl->GetKeyTimes(times,range,flags);}
int GetKeySelState(BitArray &sel,Interval range,DWORD flags) {return tmControl->GetKeySelState(sel,range,flags);}
Control *GetPositionController() {return tmControl->GetPositionController();}
Control *GetRotationController() {return tmControl->GetRotationController();}
Control *GetScaleController() {return tmControl->GetScaleController();}
BOOL SetPositionController(Control *c) {return tmControl->SetPositionController(c);}
BOOL SetRotationController(Control *c) {return tmControl->SetRotationController(c);}
BOOL SetScaleController(Control *c) {return tmControl->SetScaleController(c);}
void MapKeys(TimeMap *map,DWORD flags);
// From ILinkCtrl
int GetParentCount() {return nodes.Count();}
TimeValue GetLinkTime(int i) {return times[i];}
void SetLinkTime(int i,TimeValue t) {SetTime(t,i);}
void LinkTimeChanged() {SortNodes(-1);}
BOOL SetFrameNumber(int targetNumber, int frameNumber) {return TRUE;}
BOOL SetTarget(INode *target, int targetNumber) {return TRUE;}
BOOL AddTarget(INode *target, int frameNo) {return TRUE;}
INode* GetNode(int targetNumber) {return NULL;}
BOOL DeleteTarget(int selection) {return TRUE;}
int GetFrameNumber(int targetNumber) {return 0;}
// Local methods
Matrix3 GetParentTM(TimeValue t,Interval *valid=NULL);
Matrix3 CompTM(TimeValue t, int i);
void AddNewLink(INode *node,TimeValue t);
void DeleteLink(int i);
void SetTime(TimeValue t,int i);
void SortNodes(int sel);
void SetupDialog(HWND hWnd);
void DestroyDialog();
void SetupList(int sel=-1);
void ListSelChanged();
void Invalidate();
void Update();
};
class LinksRestore : public RestoreObj {
public:
LinkCtrl *cont;
Tab<INode*> unodes;
Tab<INode*> rnodes;
Tab<TimeValue> utimes;
Tab<TimeValue> rtimes;
LinksRestore(LinkCtrl *c) {
cont = c;
unodes = cont->nodes;
utimes = cont->times;
}
void Restore(int isUndo) {
if (isUndo) {
rnodes = cont->nodes;
rtimes = cont->times;
}
cont->nodes = unodes;
cont->times = utimes;
cont->NotifyDependents(FOREVER,0,REFMSG_CHANGE);
cont->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
cont->Invalidate();
}
void Redo() {
cont->nodes = rnodes;
cont->times = rtimes;
cont->NotifyDependents(FOREVER,0,REFMSG_CHANGE);
cont->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
cont->Invalidate();
}
void EndHold() {cont->ClearAFlag(A_HELD);}
};
//--- Class Descriptor ----------------------------------------------
HWND LinkCtrl::hWnd = NULL;
IObjParam *LinkCtrl::ip = NULL;
LinkCtrl *LinkCtrl::editCont = NULL;
ISpinnerControl *LinkCtrl::iTime = NULL;
ICustButton *LinkCtrl::iPickOb = NULL;
ICustButton *LinkCtrl::iDelOb = NULL;
PickLinkMode *LinkCtrl::pickLinkMode = NULL;
BOOL LinkCtrl::valid = FALSE;
class LinkCtrlClassDesc:public ClassDesc {
public:
int IsPublic() {return 1;}
void * Create(BOOL loading) {return new LinkCtrl(loading);}
const TCHAR * ClassName() {return LINKCTRL_NAME;}
SClass_ID SuperClassID() {return CTRL_MATRIX3_CLASS_ID; }
Class_ID ClassID() {return LINKCTRL_CLASSID;}
const TCHAR* Category() {return _T("");}
};
static LinkCtrlClassDesc linkCtrlCD;
ClassDesc* GetLinkCtrlDesc() {return &linkCtrlCD;}
BOOL CALLBACK LinkParamDialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
//--- LinkCtrl methods -----------------------------------------------
LinkCtrl::LinkCtrl(BOOL loading)
{
tmControl = NULL;
if (!loading) {
Control *cont;
ClassDesc *desc = GetDefaultController(CTRL_MATRIX3_CLASS_ID);
if (desc && desc->ClassID()==ClassID()) {
cont = (Control*)CreateInstance(CTRL_MATRIX3_CLASS_ID, Class_ID(PRS_CONTROL_CLASS_ID,0));
} else {
cont = NewDefaultMatrix3Controller();
}
ReplaceReference(0,cont);
}
}
void LinkCtrl::BeginEditParams(
IObjParam *ip, ULONG flags,Animatable *prev)
{
if (flags&BEGIN_EDIT_MOTION) {
this->ip = ip;
editCont = this;
hWnd = ip->AddRollupPage(
hInstance,
MAKEINTRESOURCE(IDD_LINK_PARAMS),
LinkParamDialogProc,
GetString(IDS_RB_LINKPARAMS),
(LPARAM)this);
pickLinkMode = new PickLinkMode(this);
tmControl->BeginEditParams(ip,flags,NULL);
}
}
void LinkCtrl::EndEditParams(
IObjParam *ip, ULONG flags,Animatable *next)
{
if (editCont) {
ip->ClearPickMode();
tmControl->EndEditParams(ip,flags,NULL);
ip->DeleteRollupPage(hWnd);
hWnd = NULL;
ip = NULL;
editCont = NULL;
delete pickLinkMode;
pickLinkMode = NULL;
}
}
RefResult LinkCtrl::NotifyRefChanged(
Interval iv, RefTargetHandle hTarg,
PartID& partID, RefMessage msg)
{
switch (msg) {
case REFMSG_TARGET_DELETED: {
for (int i=nodes.Count()-1; i>=0; i--) {
if (nodes[i]==hTarg) DeleteLink(i);
}
break;
}
}
return REF_SUCCEED;
}
#define LINKCOUNT_CHUNK 0x0100
#define TIMES_CHUNK 0x0110
IOResult LinkCtrl::Save(ISave *isave)
{
ULONG nb;
int ct = nodes.Count();
isave->BeginChunk(LINKCOUNT_CHUNK);
isave->Write(&ct,sizeof(ct),&nb);
isave->EndChunk();
if (ct) {
isave->BeginChunk(TIMES_CHUNK);
isave->Write(times.Addr(0),sizeof(TimeValue)*ct,&nb);
isave->EndChunk();
}
return IO_OK;
}
IOResult LinkCtrl::Load(ILoad *iload)
{
ULONG nb;
int ct;
IOResult res;
while (IO_OK==(res=iload->OpenChunk())) {
switch (iload->CurChunkID()) {
case LINKCOUNT_CHUNK: {
iload->Read(&ct,sizeof(ct),&nb);
nodes.SetCount(ct);
times.SetCount(ct);
for (int i=0; i<ct; i++) nodes[i] = NULL;
break;
}
case TIMES_CHUNK:
iload->Read(times.Addr(0),sizeof(TimeValue)*ct,&nb);
break;
}
iload->CloseChunk();
if (res!=IO_OK) return res;
}
return IO_OK;
}
RefTargetHandle LinkCtrl::Clone(RemapDir &remap)
{
LinkCtrl *ctrl = new LinkCtrl();
ctrl->ReplaceReference(0,remap.CloneRef(tmControl));
ctrl->nodes.SetCount(nodes.Count());
ctrl->times.SetCount(times.Count());
for (int i=0; i<nodes.Count(); i++) {
ctrl->nodes[i] = NULL;
ctrl->ReplaceReference(i+1,nodes[i]);
ctrl->times[i] = times[i];
}
return ctrl;
}
RefTargetHandle LinkCtrl::GetReference(int i)
{
if (i==0) return tmControl;
return nodes[i-1];
}
void LinkCtrl::SetReference(int i, RefTargetHandle rtarg)
{
if (i==0) tmControl = (Control*)rtarg;
else nodes[i-1] = (INode*)rtarg;
}
BOOL LinkCtrl::AssignController(Animatable *control,int subAnim)
{
if (control->ClassID()==ClassID()) return FALSE;
ReplaceReference(0,(ReferenceTarget*)control);
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
return TRUE;
}
void LinkCtrl::Copy(Control *from)
{
if (from->ClassID()==ClassID()) {
LinkCtrl *lc = (LinkCtrl*)from;
ReplaceReference(0,(ReferenceTarget*)lc->tmControl->Clone());
} else {
ReplaceReference(0,(ReferenceTarget*)from->Clone());
}
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
}
void LinkCtrl::GetValue(
TimeValue t, void *val, Interval &valid,
GetSetMethod method)
{
Matrix3 *ptm = (Matrix3*)val;
*ptm = GetParentTM(t,&valid);
tmControl->GetValue(t,val,valid,method);
}
void LinkCtrl::SetValue(
TimeValue t, void *val, int commit,
GetSetMethod method)
{
SetXFormPacket *pckt = (SetXFormPacket*)val;
pckt->tmParent = GetParentTM(t);
tmControl->SetValue(t,val,commit,method);
}
Matrix3 LinkCtrl::CompTM(TimeValue t, int i)
{
Matrix3 rtm(1);
if (i) {
// Evaluate the tm of the previous node the instant of the switch
Matrix3 pptm = CompTM(times[i],i-1);
// Evaluate the tm of this node the instant of the switch
Matrix3 ptm = nodes[i]->GetNodeTM(times[i]);
rtm = pptm*Inverse(ptm);
}
return rtm*nodes[i]->GetNodeTM(t);
}
Matrix3 LinkCtrl::GetParentTM(TimeValue t,Interval *valid)
{
if (!nodes.Count()) return Matrix3(1);
for (int i=0; i<nodes.Count(); i++) {
if (times[i]>t) {
if (i) i--;
break;
}
}
if (i>nodes.Count()-1) i = nodes.Count()-1;
// Compute a matrix that adjusts the transitions between links so
// as to maintain continuity
Matrix3 rtm(1);
if (i) {
// Evaluate (recursively) the tm of the previous node the instant of the switch
//Matrix3 pptm = nodes[i-1]->GetNodeTM(times[i]);
Matrix3 pptm = CompTM(times[i],i-1);
// Evaluate the tm of this node the instant of the switch
Matrix3 ptm = nodes[i]->GetNodeTM(times[i]);
rtm = pptm*Inverse(ptm);
}
// Validity is limited to the duration of this link
if (valid) {
Interval iv = FOREVER;
if (i) iv.SetStart(times[i]);
if (i<times.Count()-1) iv.SetEnd(times[i+1]);
*valid &= iv;
}
return rtm*nodes[i]->GetNodeTM(t,valid);
}
void LinkCtrl::AddNewLink(INode *node,TimeValue t)
{
if (theHold.Holding() && !TestAFlag(A_HELD))
theHold.Put(new LinksRestore(this));
if (!nodes.Count()) {
Matrix3 tm(1), ntm = node->GetNodeTM(t), itm(1);
tmControl->GetValue(t,&tm,FOREVER,CTRL_RELATIVE);
tmControl->ChangeParents(t,itm,ntm,tm);
}
for (int i=0; i<nodes.Count(); i++) {
if (times[i]>t) break;
}
nodes.Insert(i,1,&node);
times.Insert(i,1,&t);
MakeRefByID(FOREVER,i+1,node);
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
SetupList(i);
}
void LinkCtrl::DeleteLink(int i)
{
if (nodes.Count()==1) {
TimeValue t = GetCOREInterface()->GetTime();
Matrix3 ptm = GetParentTM(t);
Matrix3 itm(1), tm = ptm;
tmControl->GetValue(t,&tm,FOREVER,CTRL_RELATIVE);
tmControl->ChangeParents(t,ptm,itm,tm);
}
DeleteReference(i+1);
if (theHold.Holding() && !TestAFlag(A_HELD))
theHold.Put(new LinksRestore(this));
nodes.Delete(i,1);
times.Delete(i,1);
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
if (hWnd && editCont==this) SetupList();
}
void LinkCtrl::SetTime(TimeValue t,int i)
{
if (theHold.Holding() && !TestAFlag(A_HELD)) {
theHold.Put(new LinksRestore(this));
SetAFlag(A_HELD);
}
times[i] = t;
SortNodes(i);
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
}
void LinkCtrl::MapKeys(TimeMap *map,DWORD flags)
{
if (theHold.Holding() && !TestAFlag(A_HELD)) {
theHold.Put(new LinksRestore(this));
SetAFlag(A_HELD);
}
for (int i=0; i<times.Count(); i++) {
times[i] = map->map(times[i]);
}
if (hWnd && editCont==this) SetupList();
NotifyDependents(FOREVER,0,REFMSG_CHANGE);
}
static LinkCtrl *theSortCtrl = NULL;
static int __cdecl CompareNodes(const void *a, const void *b)
{
int *aa = (int*)a;
int *bb = (int*)b;
assert(theSortCtrl);
return theSortCtrl->times[*aa] - theSortCtrl->times[*bb];
}
void LinkCtrl::SortNodes(int sel)
{
// Setup a map for a shell sort
Tab<int> map;
map.SetCount(nodes.Count());
for (int i=0; i<map.Count(); i++) map[i] = i;
// Sort
theSortCtrl = this;
map.Sort(CompareNodes);
// Put everything in the right place
Tab<INode*> onodes = nodes;
Tab<TimeValue> otimes = times;
for (i=0; i<nodes.Count(); i++) {
nodes[i] = onodes[map[i]];
times[i] = otimes[map[i]];
}
// Redo the list selecting the node in its new place
if (sel>=0) {
for (i=0; i<map.Count(); i++) {
if (map[i]==sel) {
SetupList(i);
break;
}
}
}
}
//--- UI ------------------------------------------------------------
static BOOL CALLBACK LinkParamDialogProc(
HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LinkCtrl *cont = (LinkCtrl*)GetWindowLong(hWnd,GWL_USERDATA);
switch (msg) {
case WM_INITDIALOG:
cont = (LinkCtrl*)lParam;
SetWindowLong(hWnd,GWL_USERDATA,lParam);
cont->SetupDialog(hWnd);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_LINK_PICK:
cont->ip->SetPickMode(cont->pickLinkMode);
break;
case IDC_LINK_DELETE: {
int sel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
if (sel!=LB_ERR) {
theHold.Begin();
cont->DeleteLink(sel);
theHold.Accept(GetString(IDS_RB_DELETELINK));
cont->ip->RedrawViews(cont->ip->GetTime());
}
break;
}
case IDC_LINK_LIST:
if (HIWORD(wParam)==LBN_SELCHANGE) {
cont->ListSelChanged();
}
break;
}
break;
case CC_SPINNER_BUTTONDOWN:
theHold.Begin();
break;
case CC_SPINNER_CHANGE: {
ISpinnerControl *spin = (ISpinnerControl*)lParam;
int sel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
if (sel!=LB_ERR) cont->SetTime(spin->GetIVal(),sel);
cont->ip->RedrawViews(cont->ip->GetTime());
break;
}
case CC_SPINNER_BUTTONUP:
if (HIWORD(wParam)) {
theHold.Accept(GetString(IDS_RB_SETLINKTIME));
} else {
theHold.Cancel();
}
cont->ip->RedrawViews(cont->ip->GetTime());
break;
case WM_PAINT:
cont->Update();
return FALSE;
case WM_DESTROY:
cont->DestroyDialog();
break;
default:
return FALSE;
}
return TRUE;
}
void LinkCtrl::SetupDialog(HWND hWnd)
{
this->hWnd = hWnd;
iTime = GetISpinner(GetDlgItem(hWnd,IDC_LINK_TIMESPIN));
iTime->SetLimits(TIME_NegInfinity,TIME_PosInfinity,FALSE);
iTime->SetScale(10.0f);
iTime->LinkToEdit(GetDlgItem(hWnd,IDC_LINK_TIME),EDITTYPE_TIME);
iPickOb = GetICustButton(GetDlgItem(hWnd,IDC_LINK_PICK));
iDelOb = GetICustButton(GetDlgItem(hWnd,IDC_LINK_DELETE));
iPickOb->SetType(CBT_CHECK);
iPickOb->SetHighlightColor(GREEN_WASH);
SetupList();
}
void LinkCtrl::DestroyDialog()
{
ReleaseISpinner(iTime);
ReleaseICustButton(iPickOb);
ReleaseICustButton(iDelOb);
iTime = NULL;
iPickOb = NULL;
iDelOb = NULL;
}
void LinkCtrl::SetupList(int sel)
{
if (!hWnd || editCont!=this) return;
int csel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
if (sel>=0) csel = sel;
SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_RESETCONTENT,0,0);
for (int i=0; i<nodes.Count(); i++) {
SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_ADDSTRING,0,
(LPARAM)nodes[i]->GetName());
}
if (csel!=LB_ERR && csel<nodes.Count()) {
SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_SETCURSEL,csel,0);
}
ListSelChanged();
}
void LinkCtrl::ListSelChanged()
{
int sel = SendDlgItemMessage(hWnd,IDC_LINK_LIST,LB_GETCURSEL,0,0);
if (sel!=LB_ERR) {
iDelOb->Enable();
iTime->Enable();
iTime->SetValue(times[sel],FALSE);
EnableWindow(GetDlgItem(hWnd,IDC_LINK_TIMELABEL),TRUE);
} else {
iDelOb->Disable();
iTime->Disable();
EnableWindow(GetDlgItem(hWnd,IDC_LINK_TIMELABEL),FALSE);
}
}
void LinkCtrl::Invalidate()
{
if (hWnd && editCont==this) {
valid = FALSE;
InvalidateRect(hWnd,NULL,FALSE);
}
}
void LinkCtrl::Update()
{
if (!valid) {
valid = TRUE;
SetupList();
}
}
//--- PickLinkMode ------------------------------------------------
BOOL PickLinkMode::Filter(INode *node)
{
if (node) {
return (node->TestForLoop(FOREVER,cont)==REF_SUCCEED);
}
return FALSE;
}
BOOL PickLinkMode::HitTest(
IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags)
{
INode *node = cont->ip->PickNode(hWnd,m);
return node?TRUE:FALSE;
}
BOOL PickLinkMode::Pick(IObjParam *ip,ViewExp *vpt)
{
INode *node = vpt->GetClosestHit();
if (node) {
if (node->TestForLoop(FOREVER,cont)==REF_SUCCEED) {
theHold.Begin();
cont->AddNewLink(node,ip->GetTime());
theHold.Accept(GetString(IDS_RB_ADDLINK));
cont->ip->RedrawViews(cont->ip->GetTime());
}
}
return FALSE;
}
void PickLinkMode::EnterMode(IObjParam *ip)
{cont->iPickOb->SetCheck(TRUE);}
void PickLinkMode::ExitMode(IObjParam *ip)
{cont->iPickOb->SetCheck(FALSE);}