scummvm/engines/watchmaker/saveload.cpp
Einar Johan Trøan Sømåen ca12fc10ab WATCHMAKER: Move room loading to RoomManager.
This lets us remove the dependency on malloc.h.

The replacement logic is not 100% perfect, as the original logic will
resize arrays to load additional t3dBODY-structs, and those would then
be free-d alongside their parent. This logic is currently missing, so
we will use some more memory.

However, we won't leak, as all loads are tracked by the RoomManager, and
will be freed when closing/restarting the game.
2023-04-29 13:01:08 +02:00

581 lines
20 KiB
C++
Raw Blame History

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define FORBIDDEN_SYMBOL_EXCEPTION_sprintf
#define FORBIDDEN_SYMBOL_EXCEPTION_strcpy
#include "watchmaker/saveload.h"
#include "watchmaker/3d/geometry.h"
#include "watchmaker/3d/t3d_mesh.h"
#include "watchmaker/classes/do_inv.h"
#include "watchmaker/classes/do_player.h"
#include "watchmaker/classes/do_sound.h"
#include "watchmaker/classes/do_system.h"
#include "watchmaker/define.h"
#include "watchmaker/globvar.h"
#include "watchmaker/ll/ll_anim.h"
#include "watchmaker/ll/ll_diary.h"
#include "watchmaker/ll/ll_string.h"
#include "watchmaker/ll/ll_util.h"
#include "watchmaker/schedule.h"
#include "watchmaker/walk/act.h"
#include "watchmaker/ll/ll_system.h"
#include "watchmaker/windows_hacks.h"
#define WM_SAVEFILE_VERSION 0x17 // Don't know what changed though.
namespace Watchmaker {
char *TextPtr;
/* -----------------03/08/00 17.45-------------------
* ReplaceSaveLoadTexture
* --------------------------------------------------*/
void ReplaceSaveLoadTexture(int32 ci) {
/* char str[255];
gTexture *t;
t3dMESH *m;
if( ( ci <= i00SAVEPOS01 ) || ( !InvObj[ci].meshlink[0] ) || ( ( m=LinkMeshToStr( InvObj[ci].meshlink ) ) == nullptr ) )
return;
if( ( !m->FList ) || ( !m->FList->mat ) || ( !m->FList->mat->Texture ) )
return ;
rReleaseBitmapDirect( m->FList->mat->Texture );
sprintf( str, "WmSav%#02d.tga", ci-i00SAVEPOS01+1 );
t = (gTexture *)rLoadBitmapImage( str, (t3dU8)(rTEXTURESURFACE) );
m->FList->mat->Texture = t;
*/
}
/* -----------------23/08/00 11.12-------------------
* getNextSent
* --------------------------------------------------*/
char *getNextSent() {
while (*TextPtr) {
*TextPtr = ~(*TextPtr);
TextPtr++;
}
TextPtr++;
return ((char *)(TextPtr));
}
void loadTextBucket(Common::SeekableReadStream &stream) {
extern char *TextBucket;
if (!TextBucket) TextBucket = (char *)calloc(1, TEXT_BUCKET_SIZE);
stream.read(TextBucket, TEXT_BUCKET_SIZE);
TextPtr = TextBucket;
for (int a = 0; a < MAX_OBJ_NAMES; a++) {
ObjName[a] = getNextSent();
}
for (int a = 0; a < MAX_SENTENCES; a++) {
Sentence[a] = getNextSent();
}
for (int a = 0; a < MAX_SYS_SENTS; a++) {
SysSent[a] = getNextSent();
}
for (int a = 0; a < MAX_TOOLTIP_SENTS; a++) {
TooltipSent[a] = getNextSent();
}
}
void loadAll(WorkDirs &workDirs, Init &init) {
// TODO: Cwd
auto stream = openFile(workDirs._gameDir + WM_INIT_PACK_FILENAME);
assert(stream);
init.loadFromStream(*stream);
loadTextBucket(*stream);
}
/* -----------------28/06/00 10.12-------------------
* DataSave
* --------------------------------------------------*/
// NOTA: se slot==255 forza il save (salva senza controllare le condizioni)
bool DataSave(const char *SaveName, uint8 slot) {
char str[T3D_NAMELEN];
warning("STUBBED: DataSave");
#if 0
FILE *fhs;
int32 i, j;
uint16 WmVer;
if (slot != 255) {
if (!PlayerCanSave()) return FALSE;
if (
(bPlayerInAnim && (bT2DActive != tOPTIONS))
|| bUseWith || bDialogActive || bDialogMenuActive || bMovingCamera || bGolfActive || InvStatus
)
return FALSE;
}
CharStop_SaveStatus();
bPauseAllAnims = TRUE;
for (i = 0; i < T3D_MAX_CHARACTERS; i++)
if (Character[i])
CharStop(i);
InitMessageSystem();
ClearUseWith();
ClearText();
if (bSomeOneSpeak) bSkipTalk = TRUE;
PlayerPos[CurPlayer + ocDARRELL] = 0;
PlayerGotoPos[CurPlayer + ocDARRELL] = 0;
game._messageSystem.removeEvent(EventClass::MC_PLAYER, ME_ALL);
CharStop(ocCURPLAYER);
// nascondo l'altro giocatore, cos<6F> quando riparte il gioco StartDiary() vede che e' nascosto e controlla se deve essere visualizzato
// (altrimenti quando carico e l'altro player non <20> HIDE, per via di alcune variabili puo' non essere disegnato)
Character[(CurPlayer ^ 1) + ocDARRELL]->Flags |= T3D_CHARACTER_HIDE;
//sprintf( str, "WmSav%#02d.tga", slot );
//rGrabVideo( str, 1 );
/* memcpy( BackupInv, &Inv[CurPlayer], sizeof(t3dU8)*MAX_ICONS_IN_INV );
BackupInvLen = InvLen[CurPlayer];
BackupInvBase = InvBase[CurPlayer];
memset( &Inv[CurPlayer], 0, sizeof(t3dU8)*MAX_ICONS_IN_INV );
for ( i=0; i<MAX_SAVE_SLOTS; i++ ) Inv[CurPlayer][i] = i00SAVEPOS01+i;
InvLen[CurPlayer] = MAX_SAVE_SLOTS;
InvBase[CurPlayer] = 0;
InvStatus = INV_ON|INV_MODE2|INV_MODE5;
*/
/*
#ifdef INTERNAL_TEST_VERSION
{//DEBUG: si backuppa fino a 20 files
char _str[T3D_NAMELEN];
int i;
for(i=20; i>=1; i--)
{
sprintf( str, "WmSav%#02d.%#03d", slot,i-1 );
sprintf( _str, "WmSav%#02d.%#03d", slot,i );
remove(_str);
rename(str,_str);
}
sprintf( str, "Wm%#02d.sav", slot );
sprintf( _str, "WmSav%#02d.%#03d", slot,0 );
remove(_str);
rename(str,_str);
}
#endif
*/
sprintf(str, "%sWm%#02d.sav", WmSavesDir, slot);
if ((fhs = fopen(str, "wb")) == nullptr)
return FALSE;
WmVer = (t3dU16)WM_SAVEFILE_VERSION;
fwrite(&WmVer, sizeof(t3dU16), 1, fhs);
fwrite(SaveName, sizeof(char), T3D_NAMELEN, fhs);
fwrite(t3dCurRoom->Name, sizeof(char), T3D_NAMELEN, fhs);
for (i = 0; i < MAX_ROOMS; i++) {
fwrite(&Room[i].flags, sizeof(t3dU16), 1, fhs);
}
for (i = 0; i < MAX_OBJS; i++) {
fwrite(&Obj[i].name, sizeof(t3dU16), 1, fhs);
fwrite(Obj[i].examine, sizeof(t3dU16), MAX_PLAYERS, fhs);
fwrite(Obj[i].action, sizeof(t3dU16), MAX_PLAYERS, fhs);
fwrite(Obj[i].anim, sizeof(t3dU16), MAX_PLAYERS, fhs);
fwrite(Obj[i].anim2, sizeof(t3dU16), MAX_PLAYERS, fhs);
fwrite(&Obj[i].goroom, sizeof(t3dU8), 1, fhs);
fwrite(&Obj[i].ninv, sizeof(t3dU8), 1, fhs);
fwrite(&Obj[i].flags, sizeof(t3dU16), 1, fhs);
fwrite(&Obj[i].pos, sizeof(t3dU8), 1, fhs);
}
for (i = 0; i < MAX_ICONS; i++) {
fwrite(&InvObj[i].name, sizeof(t3dU16), 1, fhs);
fwrite(InvObj[i].examine, sizeof(t3dU16), MAX_PLAYERS, fhs);
fwrite(InvObj[i].action, sizeof(t3dU16), MAX_PLAYERS, fhs);
fwrite(&InvObj[i].flags, sizeof(t3dU16), 1, fhs);
fwrite(&InvObj[i].uwobj, sizeof(t3dU16), 1, fhs);
fwrite(InvObj[i].anim, sizeof(t3dU16), MAX_PLAYERS, fhs);
fwrite(InvObj[i].anim2, sizeof(t3dU16), MAX_PLAYERS, fhs);
}
for (i = 0; i < MAX_SOUNDS; i++) {
fwrite(&Sound[i].flags, sizeof(t3dU8), 1, fhs);
}
for (i = 0; i < MAX_DIALOGS; i++) {
fwrite(&Dialog[i].flags, sizeof(t3dU16), 1, fhs);
}
for (i = 0; i < MAX_DIARIES; i++) {
fwrite(&Diary[i].startt, sizeof(t3dU16), 1, fhs);
fwrite(&Diary[i].endt, sizeof(t3dU16), 1, fhs);
fwrite(&Diary[i].end_hideobj, sizeof(t3dU16), 1, fhs);
}
for (i = 0; i < MAX_DLG_MENUS; i++) {
fwrite(&DlgMenu[i].on, sizeof(t3dU8), 1, fhs);
}
for (i = 0; i < MAX_PDALOGS; i++) {
fwrite(&PDALog[i].time, sizeof(t3dS32), 1, fhs);
fwrite(&PDALog[i].flags, sizeof(t3dS32), 1, fhs);
}
fwrite(&CurRoom, sizeof(t3dS32), 1, fhs);
fwrite(&CurInvObj, sizeof(t3dS32), 1, fhs);
fwrite(&BigInvObj, sizeof(t3dS32), 1, fhs);
fwrite(&CurPlayer, sizeof(t3dS32), 1, fhs);
fwrite(&CurSubMusic, sizeof(t3dS32), 1, fhs);
fwrite(Inv, sizeof(t3dU8), MAX_PLAYERS * MAX_ICONS_IN_INV, fhs);
fwrite(InvLen, sizeof(t3dU8), MAX_PLAYERS, fhs);
fwrite(InvBase, sizeof(t3dU8), MAX_PLAYERS, fhs);
fwrite(PlayerPos, sizeof(t3dU8), T3D_MAX_CHARACTERS, fhs);
fwrite(&CameraTargetObj, sizeof(t3dS32), 1, fhs);
fwrite(&CameraTargetBone, sizeof(t3dS32), 1, fhs);
fwrite(Comb2D, sizeof(t3dS32), 5, fhs);
fwrite(Comb2Q, sizeof(t3dS32), 5, fhs);
fwrite(Comb19, sizeof(t3dS32), 3, fhs);
fwrite(Comb1D, sizeof(t3dS32), 5, fhs);
fwrite(&Forno25, sizeof(t3dS32), 1, fhs);
fwrite(&Frigo25, sizeof(t3dS32), 1, fhs);
fwrite(Comb31, sizeof(t3dS32), 5, fhs);
fwrite(Comb33, sizeof(t3dS32), 4, fhs);
fwrite(Comb42, sizeof(t3dS32), 12, fhs);
fwrite(Comb44, sizeof(t3dS32), 3, fhs);
fwrite(Comb45, sizeof(t3dS32), 5, fhs);
fwrite(&bMoglieGym, sizeof(t3dU8), 1, fhs);
fwrite(&bMoglieSangue, sizeof(t3dU8), 1, fhs);
fwrite(&bNoPlayerSwitch, sizeof(t3dU8), 1, fhs);
fwrite(&bPorteEsternoBloccate, sizeof(t3dU8), 1, fhs);
fwrite(&bSezioneLabirinto, sizeof(t3dU8), 1, fhs);
for (i = 0; i < MAX_MODIFIED_MESH; i++) {
fwrite(&MMList[i], sizeof(struct SMeshModifier), 1, fhs);
}
fwrite(PlayerStand, sizeof(struct SPlayerStand), MAX_PLAYERS, fhs);
fwrite(&t3dCurTime, sizeof(t3dS32), 1, fhs);
for (i = 0; i < T3D_MAX_CHARACTERS; i++) {
if ((i == ocCURPLAYER) || (i == ocLASTCHAR)) continue;
if (Character[i] && Character[i]->Mesh) {
fwrite(&Character[i]->Mesh->Trasl, sizeof(t3dV3F), 1, fhs);
fwrite(&Character[i]->Dir, sizeof(t3dV3F), 1, fhs);
fwrite(&Character[i]->Flags, sizeof(t3dU8), 1, fhs);
fwrite(&Character[i]->Walk.LookX, sizeof(t3dF32), 1, fhs);
fwrite(&Character[i]->Walk.LookZ, sizeof(t3dF32), 1, fhs);
fwrite(&Character[i]->Walk.CurX, sizeof(t3dF32), 1, fhs);
fwrite(&Character[i]->Walk.CurZ, sizeof(t3dF32), 1, fhs);
fwrite(&Character[i]->Walk.CurPanel, sizeof(t3dS16), 1, fhs);
fwrite(&Character[i]->Walk.OldPanel, sizeof(t3dS16), 1, fhs);
} else {
//evito che i save non dipendano dal numero di personaggi presenti in memoria in quel momento
BYTE x, k, len;
len = sizeof(t3dV3F) * 2 + sizeof(t3dU8) + sizeof(t3dF32) * 4 + sizeof(t3dS16) * 2;
for (k = 0, x = 0; k < len; k++) fwrite(&x, 1, 1, fhs);
}
}
for (i = 0; i < MAX_PLAYERS; i++) {
for (j = 0; j < T3D_MAX_CHARACTERS; j++) {
fwrite(&UsedDlgMenu[i][j], sizeof(t3dU8), MAX_DLG_MENUS, fhs);
}
}
fclose(fhs);
bPauseAllAnims = FALSE;
CharStop_LoadStatus();
#endif
return (TRUE);
}
/* -----------------28/06/00 14.49-------------------
* DataLoad
* --------------------------------------------------*/
//se SaveName e' "" utilizzo lo slot
bool DataLoad(WGame &game, const Common::String &FileName, uint8 slot) {
char str[T3D_NAMELEN] = {};
uint16 WmVer = 0;
if (!FileName.equalsIgnoreCase("WmStart.dat"))
if (
(bPlayerInAnim && (bT2DActive != tOPTIONS))
|| bUseWith || bDialogActive || bDialogMenuActive || bMovingCamera || bGolfActive || InvStatus
)
return FALSE;
// resetto alcune variabili che potrebbero rimanere sporche dalla partita prima (uguale per StartPlayingGame(), DataLoad() )
bCacciatore = 0;
bSaveDisabled = 0;
bNotSkippableSent = 0;
bPorteEsternoBloccate = 0;
bNoPlayerSwitch = 0;
bDarkScreen = FALSE;
bSuperView = 0;
bSezioneLabirinto = 0;
bSkipTalk = FALSE;
bSomeOneSpeak = FALSE;
bPlayerSpeak = FALSE;
bWideScreen = 0;
bTitoliCodaStatic = 0;
bTitoliCodaScrolling = 0;
strcpy(RoomInfo.name, "");
StopDiary(game, 0, 0, 0);
bPauseAllAnims = TRUE;
for (int i = 0; i < T3D_MAX_CHARACTERS; i++)
if (Character[i])
CharStop(i);
game._messageSystem.init();
ClearUseWith();
ClearText();
/* FreeKey();
mleft = mright = 0;
Mouse(3);
while( mleft || mright )
Mouse(3);
// ferma omino, animazioni, spegne scritte
memcpy( OldInv, Inventario, MAXICON );
memset( Inventario, 0, MAXICON );
OldIconBase = TheIconBase;
TheIconBase = 0;
OldInvLen = InventarioLen;
InventarioLen = MAXSAVEFILE;
*/
if (!FileName.empty())
strcpy(str, FileName.c_str());
else
sprintf(str, "%sWm%#02d.sav", game.workDirs._savesDir.c_str(), slot);
auto stream = openFile(FileName);
if (!stream) {
return false;
}
Init &init = game.init;
WmVer = stream->readUint16LE();
if (WmVer != WM_SAVEFILE_VERSION) {
DebugLogFile("!!Invalid SaveGame Version: found %d, required %d", WmVer, WM_SAVEFILE_VERSION);
return false;
}
stream->read(str, T3D_NAMELEN);
stream->read(str, T3D_NAMELEN);
for (int i = 0; i < MAX_ROOMS; i++) {
init.Room[i].flags = stream->readUint16LE();
}
for (int i = 0; i < MAX_OBJS; i++) {
init.Obj[i].name = stream->readUint16LE();
init.Obj[i].examine.loadFromStream(*stream);
init.Obj[i].action.loadFromStream(*stream);
init.Obj[i].anim.loadFromStream(*stream);
init.Obj[i].anim2.loadFromStream(*stream);
init.Obj[i].goroom = stream->readByte();
init.Obj[i].ninv = stream->readByte();
init.Obj[i].flags = stream->readUint16LE();
init.Obj[i].pos = stream->readByte();
}
// HACK? (Something is off between 0.92 and the saves in the retail, leading to a slight skew.
stream->seek(92, SEEK_CUR);
for (int i = 0; i < MAX_ICONS; i++) {
init.InvObj[i].name = stream->readUint16LE();
init.InvObj[i].examine.loadFromStream(*stream);
init.InvObj[i].action.loadFromStream(*stream);
init.InvObj[i].flags = stream->readUint16LE();
init.InvObj[i].uwobj = stream->readUint16LE();
init.InvObj[i].anim.loadFromStream(*stream);
init.InvObj[i].anim2.loadFromStream(*stream);
}
for (int i = 0; i < MAX_SOUNDS; i++) {
init.Sound[i].flags = stream->readByte();
}
for (int i = 0; i < MAX_DIALOGS; i++) {
init.Dialog[i].flags = stream->readUint16LE();
}
for (int i = 0; i < MAX_DIARIES; i++) {
init.Diary[i].startt = stream->readUint16LE();
init.Diary[i].endt = stream->readUint16LE();
init.Diary[i].end_hideobj = stream->readUint16LE();
init.Diary[i].cur = 0;
for (int j = 0; j < MAX_DIARY_ITEMS; j++) {
init.Diary[i].item[j].on = FALSE;
init.Diary[i].item[j].cur = 0;
}
}
for (int i = 0; i < MAX_DLG_MENUS; i++) {
init.DlgMenu[i].on = stream->readByte();
}
for (int i = 0; i < MAX_PDALOGS; i++) {
init.PDALog[i].time = stream->readSint32LE();
init.PDALog[i].flags = stream->readSint32LE();
}
game._gameVars.setCurRoomId(stream->readSint32LE());
CurInvObj = stream->readSint32LE();
BigInvObj = stream->readSint32LE();
CurPlayer = stream->readSint32LE();
CurSubMusic = stream->readSint32LE();
for (int player = 0; player < MAX_PLAYERS; player++) {
for (int icon = 0; icon < MAX_ICONS_IN_INV; icon++) {
Inv[player][icon] = stream->readByte();
}
}
for (int i = 0; i < MAX_PLAYERS; i++) {
InvLen[i] = stream->readByte();
}
for (int i = 0; i < MAX_PLAYERS; i++) {
InvBase[i] = stream->readByte();
}
for (int i = 0; i < T3D_MAX_CHARACTERS; i++) {
PlayerPos[i] = stream->readByte();
}
CameraTargetObj = stream->readSint32LE();
CameraTargetBone = stream->readSint32LE();
for (int i = 0; i < 5; i++) {
Comb2D[i] = stream->readSint32LE();
}
for (int i = 0; i < 5; i++) {
Comb2Q[i] = stream->readSint32LE();
}
for (int i = 0; i < 3; i++) {
Comb19[i] = stream->readSint32LE();
}
for (int i = 0; i < 5; i++) {
Comb1D[i] = stream->readSint32LE();
}
Forno25 = stream->readSint32LE();
Frigo25 = stream->readSint32LE();
for (int i = 0; i < 5; i++) {
Comb31[i] = stream->readSint32LE();
}
for (int i = 0; i < 4; i++) {
Comb33[i] = stream->readSint32LE();
}
for (int i = 0; i < 12; i++) {
Comb42[i] = stream->readSint32LE();
}
for (int i = 0; i < 3; i++) {
Comb44[i] = stream->readSint32LE();
}
for (int i = 0; i < 5; i++) {
Comb45[i] = stream->readSint32LE();
}
bMoglieGym = stream->readByte();
bMoglieSangue = stream->readByte();
bNoPlayerSwitch = stream->readByte();
bPorteEsternoBloccate = stream->readByte();
bSezioneLabirinto = stream->readByte();
_vm->loadMeshModifiers(*stream);
for (int i = 0; i < MAX_PLAYERS; i++) {
PlayerStand[i] = SPlayerStand(*stream);
}
t3dCurTime = stream->readSint32LE();
for (int i = 0; i < T3D_MAX_CHARACTERS; i++) {
if ((i == ocCURPLAYER) || (i == ocLASTCHAR)) continue;
if (Character[i] && Character[i]->Mesh) {
Character[i]->Mesh->Trasl = t3dV3F(*stream);
Character[i]->Dir = t3dV3F(*stream);
Character[i]->Flags = stream->readByte();
Character[i]->Walk.LookX = stream->readFloatLE();
Character[i]->Walk.LookZ = stream->readFloatLE();
Character[i]->Walk.CurX = stream->readFloatLE();
Character[i]->Walk.CurZ = stream->readFloatLE();
Character[i]->Walk.CurPanel = stream->readSint16LE();
Character[i]->Walk.OldPanel = stream->readSint16LE();
} else {
// I avoid that the saves do not depend on the number of characters present in memory at that moment
//len = sizeof(t3dV3F) * 2 + sizeof(t3dU8) + sizeof(t3dF32) * 4 + sizeof(t3dS16) * 2;
t3dV3F unused(*stream);
t3dV3F unused2(*stream);
stream->readByte();
stream->readFloatLE();
stream->readFloatLE();
stream->readFloatLE();
stream->readFloatLE();
stream->readSint16LE();
stream->readSint16LE();
}
}
for (int i = 0; i < MAX_PLAYERS; i++) {
for (int j = 0; j < T3D_MAX_CHARACTERS; j++) {
for (int k = 0; k < MAX_DLG_MENUS; k++) {
UsedDlgMenu[i][j][k] = stream->readByte();
}
}
}
// fclose(fhs);
// t3dVectCopy( &tmp, &Character[ocDARRELL]->Mesh->Trasl );
// t3dVectCopy( &tmp2, &Character[ocVICTORIA]->Mesh->Trasl );
StopAllAnims(init);
StopSounds();
StopMusic();
t3dResetPipeline();
_vm->_roomManager->releaseLoadedFiles(T3D_STATIC_SET1);
t3dRxt = nullptr;
t3dSky = nullptr;
rReleaseAllTextures(T3D_STATIC_SET0);
LoaderFlags |= T3D_STATIC_SET0;
rSetLoaderFlags(LoaderFlags);
t3dCurRoom = nullptr;
bPauseAllAnims = FALSE;
ChangeRoom(game, str, 0, 0);
// evito che i personaggi rimangano a mezz'aria quando il gioco si stoppa con i personaggi in dubbie posizioni
Character[ocCURPLAYER]->Mesh->Flags |= T3D_MESH_DEFAULTANIM;
// resetto solo il current player, visto che l'altro deve avere l'anim di stand
if (CurPlayer == DARRELL)
Character[ocDARRELL]->Mesh->Flags |= T3D_MESH_DEFAULTANIM;
else
Character[ocVICTORIA]->Mesh->Flags |= T3D_MESH_DEFAULTANIM;
t3dResetMesh(LinkMeshToStr(init, "darrell"));
t3dResetMesh(LinkMeshToStr(init, "victoria"));
// t3dVectCopy( &Character[ocDARRELL]->Mesh->Trasl, &tmp );
// t3dVectCopy( &Character[ocDARRELL]->Pos, &tmp );
// t3dVectCopy( &Character[ocVICTORIA]->Mesh->Trasl, &tmp2 );
// t3dVectCopy( &Character[ocVICTORIA]->Pos, &tmp2 );
// CharSetPosition( ocDARRELL, 0, NULL );
// CharSetPosition( ocVICTORIA, 0, NULL );
return true;
}
} // End of namespace Watchmaker