[Scummvm-git-logs] scummvm master -> 617ba4154d3c8faea900ce191d92faea3421f723

mgerhardy noreply at scummvm.org
Wed Aug 14 15:30:55 UTC 2024


This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
3a6301ac30 TWINE: todo comment about lba2 shortcuts
f2802c0c83 TWINE: LBA2: implemented a few more opcodes
57690caaca TWINE: LBA2: unified initSprite with lba2
a7a7a1f15f TWINE: LBA2: added anim3ds parser
8ff7ab977d TWINE: LBA2: added further life opcodes
617ba4154d TWINE: LBA2: implemented more opcodes


Commit: 3a6301ac3091e46dc167e360df1fc3e5f67a0228
    https://github.com/scummvm/scummvm/commit/3a6301ac3091e46dc167e360df1fc3e5f67a0228
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2024-08-14T17:28:39+02:00

Commit Message:
TWINE: todo comment about lba2 shortcuts

Changed paths:
    engines/twine/metaengine.cpp


diff --git a/engines/twine/metaengine.cpp b/engines/twine/metaengine.cpp
index e8b0bcaeb20..c1120a437e8 100644
--- a/engines/twine/metaengine.cpp
+++ b/engines/twine/metaengine.cpp
@@ -440,6 +440,19 @@ Common::KeymapArray TwinEMetaEngine::initKeymaps(const char *target) const {
 		act->addDefaultInputMapping("JOY_BACK");
 		gameKeyMap->addAction(act);
 
+		// TODO: lba2 has shortcuts for the inventory items
+		// J: Protopack/Jetpack
+		// P: Mecha-Penguin
+		// H: Holomap
+		// X: Dodges
+		// 1: Magic Ball
+		// 2: Darts
+		// 3: Blowpipe/Blowtron
+		// 4: Conch Shell
+		// 5: Glove
+		// 6: Laser Gun
+		// 7: Saber
+
 		array[0] = gameKeyMap;
 	}
 


Commit: f2802c0c8326148d12dd3926c12e835ec6178bf7
    https://github.com/scummvm/scummvm/commit/f2802c0c8326148d12dd3926c12e835ec6178bf7
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2024-08-14T17:29:46+02:00

Commit Message:
TWINE: LBA2: implemented a few more opcodes

Changed paths:
    engines/twine/scene/gamestate.cpp
    engines/twine/scene/gamestate.h
    engines/twine/scene/scene.h
    engines/twine/script/script_life.cpp
    engines/twine/script/script_life.h
    engines/twine/script/script_life_v1.cpp
    engines/twine/script/script_life_v1.h
    engines/twine/script/script_life_v2.cpp
    engines/twine/script/script_life_v2.h
    engines/twine/shared.h
    engines/twine/twine.h


diff --git a/engines/twine/scene/gamestate.cpp b/engines/twine/scene/gamestate.cpp
index fb36b86dfe1..a0bb4f32597 100644
--- a/engines/twine/scene/gamestate.cpp
+++ b/engines/twine/scene/gamestate.cpp
@@ -149,6 +149,11 @@ bool GameState::loadGame(Common::SeekableReadStream *file) {
 		return false;
 	}
 
+	if (!_engine->isLBA1()) {
+		warning("Loading not implemented for lba2");
+		return false;
+	}
+
 	debug(2, "Load game");
 	const byte saveFileVersion = file->readByte();
 	// 4 is dotemu enhanced version of lba1
@@ -173,8 +178,8 @@ bool GameState::loadGame(Common::SeekableReadStream *file) {
 	} while (true);
 
 	byte numGameFlags = file->readByte();
-	if (numGameFlags != NUM_GAME_FLAGS) {
-		warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS, numGameFlags);
+	if (numGameFlags != NUM_GAME_FLAGS_LBA1) {
+		warning("Failed to load gameflags. Expected %u, but got %u", NUM_GAME_FLAGS_LBA1, numGameFlags);
 		return false;
 	}
 	for (uint8 i = 0; i < numGameFlags; ++i) {
@@ -248,9 +253,9 @@ bool GameState::saveGame(Common::WriteStream *file) {
 	file->writeByte(0x03);
 	file->writeString(_engine->_menuOptions->_saveGameName);
 	file->writeByte('\0');
-	file->writeByte(NUM_GAME_FLAGS);
-	for (uint8 i = 0; i < NUM_GAME_FLAGS; ++i) {
-		file->writeByte(hasGameFlag(i));
+	file->writeByte(NUM_GAME_FLAGS_LBA1);
+	for (uint8 i = 0; i < NUM_GAME_FLAGS_LBA1; ++i) {
+		file->writeByte((uint8)hasGameFlag(i));
 	}
 	file->writeByte(sceneIdx);
 	file->writeByte(getChapter());
@@ -605,6 +610,11 @@ int16 GameState::setKashes(int16 value) {
 	return _goldPieces;
 }
 
+int16 GameState::setZlitos(int16 value) {
+	_zlitosPieces = CLIP<int16>(value, 0, 999);
+	return _zlitosPieces;
+}
+
 int16 GameState::setKeys(int16 value) {
 	_inventoryNumKeys = MAX<int16>(0, value);
 	return _inventoryNumKeys;
@@ -669,7 +679,7 @@ void GameState::clearGameFlags() {
 	Common::fill(&_listFlagGame[0], &_listFlagGame[NUM_GAME_FLAGS], 0);
 }
 
-uint8 GameState::hasGameFlag(uint8 index) const {
+int16 GameState::hasGameFlag(uint8 index) const {
 	debug(6, "Query gameStateFlags[%u]=%u", index, _listFlagGame[index]);
 	return _listFlagGame[index];
 }
diff --git a/engines/twine/scene/gamestate.h b/engines/twine/scene/gamestate.h
index ed11560b803..3e156a8c284 100644
--- a/engines/twine/scene/gamestate.h
+++ b/engines/twine/scene/gamestate.h
@@ -69,7 +69,7 @@ private:
 	 * 107: Set to 1 after Twinsen kills yellow groboclone in the Citadel Island Tavern (after the Tavern has
 	 * been closed down). Makes the Tavern open again and groboclone not appear any more.
 	 */
-	int16 _listFlagGame[NUM_GAME_FLAGS];
+	int16 _listFlagGame[NUM_GAME_FLAGS]; // ListVarGame
 	// only lba1 - lba2 uses 253 gameflag
 	int16 _gameChapter = 0;
 
@@ -176,7 +176,7 @@ public:
 
 	void clearGameFlags();
 
-	uint8 hasGameFlag(uint8 index) const;
+	int16 hasGameFlag(uint8 index) const;
 
 	void setGameFlag(uint8 index, int16 value);
 
@@ -184,6 +184,7 @@ public:
 	int16 setGas(int16 value);
 	int16 setLeafs(int16 value);
 	int16 setKashes(int16 value);
+	int16 setZlitos(int16 value);
 	int16 setMagicPoints(int16 val);
 	int16 setMaxMagicPoints();
 	int16 setLeafBoxes(int16 val);
diff --git a/engines/twine/scene/scene.h b/engines/twine/scene/scene.h
index b044aba7bee..7dfc81df009 100644
--- a/engines/twine/scene/scene.h
+++ b/engines/twine/scene/scene.h
@@ -209,10 +209,10 @@ public:
 
 	bool _enableGridTileRendering = true;
 
-	uint8 _listFlagCube[NUM_SCENES_FLAGS]{0};
+	uint8 _listFlagCube[NUM_SCENES_FLAGS]{0}; // ListVarCube
 
-	int32 _sceneNumZones = 0;
-	ZoneStruct _sceneZones[NUM_MAX_ZONES];
+	int32 _sceneNumZones = 0; // NbZones
+	ZoneStruct _sceneZones[NUM_MAX_ZONES]; // ListZone
 
 	ActorStruct *getActor(int32 actorIdx); // ListObjet
 
diff --git a/engines/twine/script/script_life.cpp b/engines/twine/script/script_life.cpp
index a84dc24c9f4..d886ef213dd 100644
--- a/engines/twine/script/script_life.cpp
+++ b/engines/twine/script/script_life.cpp
@@ -1068,18 +1068,6 @@ int32 ScriptLife::lEND_COMPORTEMENT(TwinEEngine *engine, LifeScriptContext &ctx)
 	return 1; // break
 }
 
-/**
- * Set a new value for the game flag (Paramter = Game Flag Index, Parameter = Value)
- * @note Opcode @c 0x24
- */
-int32 ScriptLife::lSET_FLAG_GAME(TwinEEngine *engine, LifeScriptContext &ctx) {
-	const uint8 flagIdx = ctx.stream.readByte();
-	const uint8 flagValue = ctx.stream.readByte();
-	debugC(3, kDebugLevels::kDebugScripts, "LIFE::SET_FLAG_GAME(%i, %i)", (int)flagIdx, (int)flagValue);
-	engine->_gameState->setGameFlag(flagIdx, flagValue);
-	return 0;
-}
-
 /**
  * Kill the actor passed as paramenter (Parameter = Actor Index)
  * @note Opcode @c 0x25
diff --git a/engines/twine/script/script_life.h b/engines/twine/script/script_life.h
index 2aab2dec0cd..1bb101b12da 100644
--- a/engines/twine/script/script_life.h
+++ b/engines/twine/script/script_life.h
@@ -107,7 +107,6 @@ public:
 	static int32 lSET_COMPORTEMENT(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lSET_COMPORTEMENT_OBJ(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lEND_COMPORTEMENT(TwinEEngine *engine, LifeScriptContext &ctx);
-	static int32 lSET_FLAG_GAME(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lKILL_OBJ(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lSUICIDE(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lUSE_ONE_LITTLE_KEY(TwinEEngine *engine, LifeScriptContext &ctx);
diff --git a/engines/twine/script/script_life_v1.cpp b/engines/twine/script/script_life_v1.cpp
index fe224bcce25..645f9b96dca 100644
--- a/engines/twine/script/script_life_v1.cpp
+++ b/engines/twine/script/script_life_v1.cpp
@@ -24,6 +24,7 @@
 #include "twine/twine.h"
 #include "twine/text.h"
 #include "twine/audio/music.h"
+#include "twine/scene/gamestate.h"
 
 namespace TwinE {
 
@@ -68,6 +69,18 @@ int32 ScriptLifeV1::lMIDI_OFF(TwinEEngine *engine, LifeScriptContext &ctx) {
 	return 0;
 }
 
+/**
+ * Set a new value for the game flag (Paramter = Game Flag Index, Parameter = Value)
+ * @note Opcode @c 0x24
+ */
+int32 ScriptLifeV1::lSET_FLAG_GAME(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const uint8 flagIdx = ctx.stream.readByte();
+	const uint8 flagValue = ctx.stream.readByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::SET_FLAG_GAME(%i, %i)", (int)flagIdx, (int)flagValue);
+	engine->_gameState->setGameFlag(flagIdx, flagValue);
+	return 0;
+}
+
 static const ScriptLifeFunction function_map[] = {
 	{"END", ScriptLifeV1::lEND},
 	{"NOP", ScriptLifeV1::lNOP},
diff --git a/engines/twine/script/script_life_v1.h b/engines/twine/script/script_life_v1.h
index 36b9dce275b..e128aecb743 100644
--- a/engines/twine/script/script_life_v1.h
+++ b/engines/twine/script/script_life_v1.h
@@ -34,6 +34,7 @@ public:
 	static int32 lBUBBLE_OFF(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lPLAY_MIDI(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lMIDI_OFF(TwinEEngine *engine, LifeScriptContext &ctx);
+	static int32 lSET_FLAG_GAME(TwinEEngine *engine, LifeScriptContext &ctx);
 
 	ScriptLifeV1(TwinEEngine *engine);
 };
diff --git a/engines/twine/script/script_life_v2.cpp b/engines/twine/script/script_life_v2.cpp
index 5ab181fa434..55bbe3253c1 100644
--- a/engines/twine/script/script_life_v2.cpp
+++ b/engines/twine/script/script_life_v2.cpp
@@ -73,7 +73,7 @@ static const ScriptLifeFunction function_map[] = {
 	{"SET_COMPORTEMENT", ScriptLife::lSET_COMPORTEMENT},
 	{"SET_COMPORTEMENT_OBJ", ScriptLife::lSET_COMPORTEMENT_OBJ},
 	{"END_COMPORTEMENT", ScriptLife::lEND_COMPORTEMENT},
-	{"SET_FLAG_GAME", ScriptLife::lSET_FLAG_GAME},
+	{"SET_FLAG_GAME", ScriptLifeV2::lSET_FLAG_GAME},
 	{"KILL_OBJ", ScriptLife::lKILL_OBJ},
 	{"SUICIDE", ScriptLife::lSUICIDE},
 	{"USE_ONE_LITTLE_KEY", ScriptLife::lUSE_ONE_LITTLE_KEY},
@@ -738,12 +738,63 @@ int32 ScriptLifeV2::lBACKGROUND(TwinEEngine *engine, LifeScriptContext &ctx) {
 	return -1;
 }
 
+int32 ScriptLifeV2::lSET_FLAG_GAME(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const uint8 num = ctx.stream.readByte();
+	const int16 val = ctx.stream.readSint16LE();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::SET_FLAG_GAME(%i, %i)", (int)num, (int)val);
+	engine->_gameState->setGameFlag(num, val);
+
+	if (num == GAMEFLAG_MONEY) {
+		if (engine->_scene->_planet >= 2) {
+			engine->_gameState->setKashes(val);
+		} else {
+			engine->_gameState->setZlitos(val);
+		}
+	}
+
+	return 0;
+}
+
 int32 ScriptLifeV2::lADD_VAR_GAME(TwinEEngine *engine, LifeScriptContext &ctx) {
-	return -1;
+	const uint8 num = ctx.stream.readByte();
+	const int16 val = ctx.stream.readSint16LE();
+	int16 currentVal = engine->_gameState->hasGameFlag(num);
+	if ((int)currentVal + (int)val < 32767) {
+		currentVal += val;
+	} else {
+		currentVal = 32767;
+	}
+
+	if (num == GAMEFLAG_MONEY) {
+		if (engine->_scene->_planet >= 2) {
+			engine->_gameState->setKashes(currentVal);
+		} else {
+			engine->_gameState->setZlitos(currentVal);
+		}
+	}
+	engine->_gameState->setGameFlag(num, currentVal);
+	return 0;
 }
 
 int32 ScriptLifeV2::lSUB_VAR_GAME(TwinEEngine *engine, LifeScriptContext &ctx) {
-	return -1;
+	const uint8 num = ctx.stream.readByte();
+	const int16 val = ctx.stream.readSint16LE();
+	int16 currentVal = engine->_gameState->hasGameFlag(num);
+	if ((int)currentVal - (int)val > -32768) {
+		currentVal -= val;
+	} else {
+		currentVal = -32768;
+	}
+
+	if (num == GAMEFLAG_MONEY) {
+		if (engine->_scene->_planet >= 2) {
+			engine->_gameState->setKashes(currentVal);
+		} else {
+			engine->_gameState->setZlitos(currentVal);
+		}
+	}
+	engine->_gameState->setGameFlag(num, currentVal);
+	return 0;
 }
 
 int32 ScriptLifeV2::lADD_VAR_CUBE(TwinEEngine *engine, LifeScriptContext &ctx) {
diff --git a/engines/twine/script/script_life_v2.h b/engines/twine/script/script_life_v2.h
index f58177b3c45..0e5d05a4676 100644
--- a/engines/twine/script/script_life_v2.h
+++ b/engines/twine/script/script_life_v2.h
@@ -110,6 +110,7 @@ public:
 	static int32 lNEW_SAMPLE (TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lPOS_OBJ_AROUND(TwinEEngine *engine, LifeScriptContext &ctx);
 	static int32 lPCX_MESS_OBJ(TwinEEngine *engine, LifeScriptContext &ctx);
+	static int32 lSET_FLAG_GAME(TwinEEngine *engine, LifeScriptContext &ctx);
 
 	ScriptLifeV2(TwinEEngine *engine);
 };
diff --git a/engines/twine/shared.h b/engines/twine/shared.h
index 21ec973ff7f..03c95aeb00a 100644
--- a/engines/twine/shared.h
+++ b/engines/twine/shared.h
@@ -24,7 +24,9 @@
 
 #include "common/scummsys.h"
 
-#define NUM_GAME_FLAGS 255
+// lba1 255 - lba2 256
+#define NUM_GAME_FLAGS 256
+#define NUM_GAME_FLAGS_LBA1 255
 
 /** Number of colors used in the game */
 #define NUMOFCOLORS 256
@@ -77,6 +79,9 @@
 // Twinsun explosion
 #define GAMEFLAG_VIDEO_EXPLODE2 219
 
+// lba2 Kashes or Zlitos
+#define GAMEFLAG_MONEY 8
+
 #define OWN_ACTOR_SCENE_INDEX 0
 #define IS_HERO(x) ((x) == OWN_ACTOR_SCENE_INDEX)
 
diff --git a/engines/twine/twine.h b/engines/twine/twine.h
index 4907591e57b..e72788e04ab 100644
--- a/engines/twine/twine.h
+++ b/engines/twine/twine.h
@@ -316,9 +316,9 @@ public:
 
 	int32 _loopInventoryItem = 0;
 	int32 _stepFalling = 0;
-	uint32 _gameFlags;
+	uint32 _gameFlags = 0u;
 	Common::Platform _platform;
-	bool _flagRain;
+	bool _flagRain = false;
 
 	/** Disable screen recenter */
 	bool _disableScreenRecenter = false;


Commit: 57690caaca87006012cfd160b47e329ffd066966
    https://github.com/scummvm/scummvm/commit/57690caaca87006012cfd160b47e329ffd066966
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2024-08-14T17:29:46+02:00

Commit Message:
TWINE: LBA2: unified initSprite with lba2

Changed paths:
    engines/twine/parser/sprite.h
    engines/twine/scene/actor.cpp
    engines/twine/scene/actor.h
    engines/twine/script/script_life_v2.cpp
    engines/twine/script/script_move_v2.cpp


diff --git a/engines/twine/parser/sprite.h b/engines/twine/parser/sprite.h
index 00eb32a21b7..e8bbeec5b88 100644
--- a/engines/twine/parser/sprite.h
+++ b/engines/twine/parser/sprite.h
@@ -46,7 +46,7 @@ private:
 public:
 	bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
 
-	const BoundingBox *bbox(int index) const;
+	const BoundingBox *bbox(int index) const; // PtrZvAnim3DS, PtrZvExtra, PtrZvExtraRaw
 	const SpriteDim *dim(int index) const;
 };
 
diff --git a/engines/twine/scene/actor.cpp b/engines/twine/scene/actor.cpp
index 9bd3d712b57..35706fb4c23 100644
--- a/engines/twine/scene/actor.cpp
+++ b/engines/twine/scene/actor.cpp
@@ -133,13 +133,16 @@ void Actor::setBehaviour(HeroBehaviourType behaviour) {
 	_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, AnimationTypes::kAnimInvalid, OWN_ACTOR_SCENE_INDEX);
 }
 
-// InitSprite
-void Actor::initSpriteActor(int32 actorIdx) {
+void Actor::initSprite(int32 spriteNum, int32 actorIdx) {
 	ActorStruct *localActor = _engine->_scene->getActor(actorIdx);
 
-	if (localActor->_staticFlags.bIsSpriteActor && localActor->_sprite != -1 && localActor->_body != localActor->_sprite) {
-		const BoundingBox *spritebbox = _engine->_resources->_spriteBoundingBox.bbox(localActor->_sprite);
-		localActor->_body = localActor->_sprite;
+	localActor->_sprite = spriteNum;
+	if (!localActor->_staticFlags.bIsSpriteActor) {
+		return;
+	}
+	if (spriteNum != -1 && localActor->_body != spriteNum) {
+		const BoundingBox *spritebbox = _engine->_resources->_spriteBoundingBox.bbox(spriteNum);
+		localActor->_body = spriteNum;
 		localActor->_boundingBox = *spritebbox;
 	}
 }
@@ -249,7 +252,7 @@ void Actor::startInitObj(int16 actorIdx) {
 
 		actor->_body = -1;
 
-		initSpriteActor(actorIdx);
+		initSprite(actor->_sprite, actorIdx);
 
 		_engine->_movements->initRealAngle(LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, LBAAngles::ANGLE_0, &actor->realAngle);
 
diff --git a/engines/twine/scene/actor.h b/engines/twine/scene/actor.h
index a0423bd5b8e..428e8e20341 100644
--- a/engines/twine/scene/actor.h
+++ b/engines/twine/scene/actor.h
@@ -314,7 +314,7 @@ public:
 	/** Hero anim for behaviour menu */
 	int16 _heroAnimIdx[4];
 
-	void initSpriteActor(int32 actorIdx);
+	void initSprite(int32 spriteNum, int32 actorIdx);
 
 	/** Restart hero variables while opening new scenes */
 	void restartHeroScene();
diff --git a/engines/twine/script/script_life_v2.cpp b/engines/twine/script/script_life_v2.cpp
index 55bbe3253c1..86f54356817 100644
--- a/engines/twine/script/script_life_v2.cpp
+++ b/engines/twine/script/script_life_v2.cpp
@@ -605,8 +605,7 @@ int32 ScriptLifeV2::lSET_SPRITE(TwinEEngine *engine, LifeScriptContext &ctx) {
 	const int16 num = ctx.stream.readSint16LE();
 	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lSET_SPRITE(%i)", (int)num);
 	if (ctx.actor->_staticFlags.bIsSpriteActor) {
-		ctx.actor->_sprite = num;
-		engine->_actor->initSpriteActor(ctx.actorIdx);
+		engine->_actor->initSprite(num, ctx.actorIdx);
 	}
 	return 0;
 }
@@ -621,8 +620,7 @@ int32 ScriptLifeV2::lSET_FRAME_3DS(TwinEEngine *engine, LifeScriptContext &ctx)
 		// }
 		// sprite += ListAnim3DS[ptrobj->Coord.A3DS.Num].Deb;
 
-		ctx.actor->_sprite = sprite;
-		engine->_actor->initSpriteActor(ctx.actorIdx);
+		engine->_actor->initSprite(sprite, ctx.actorIdx);
 	}
 	return -1;
 }
diff --git a/engines/twine/script/script_move_v2.cpp b/engines/twine/script/script_move_v2.cpp
index 2ca2533575a..e9178864654 100644
--- a/engines/twine/script/script_move_v2.cpp
+++ b/engines/twine/script/script_move_v2.cpp
@@ -149,8 +149,7 @@ int32 ScriptMoveV2::mWAIT_NB_SECOND_RND(TwinEEngine *engine, MoveScriptContext &
 int32 ScriptMoveV2::mSPRITE(TwinEEngine *engine, MoveScriptContext &ctx) {
 	int16 num = ctx.stream.readSint16LE();
 	if (ctx.actor->_staticFlags.bIsSpriteActor) {
-		ctx.actor->_sprite = num;
-		engine->_actor->initSpriteActor(ctx.actorIdx);
+		engine->_actor->initSprite(num, ctx.actorIdx);
 	}
 	return 0;
 }


Commit: a7a7a1f15fd160118f2239e095b0ff78dca5a454
    https://github.com/scummvm/scummvm/commit/a7a7a1f15fd160118f2239e095b0ff78dca5a454
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2024-08-14T17:29:46+02:00

Commit Message:
TWINE: LBA2: added anim3ds parser

Changed paths:
  A engines/twine/parser/anim3ds.cpp
  A engines/twine/parser/anim3ds.h
    engines/twine/module.mk
    engines/twine/parser/anim.h
    engines/twine/resources/resources.cpp
    engines/twine/resources/resources.h
    engines/twine/scene/actor.cpp
    engines/twine/scene/actor.h
    engines/twine/script/script_life_v2.cpp
    engines/twine/script/script_move_v2.cpp


diff --git a/engines/twine/module.mk b/engines/twine/module.mk
index 71b8e838ada..5e4c4e35e40 100644
--- a/engines/twine/module.mk
+++ b/engines/twine/module.mk
@@ -14,6 +14,7 @@ MODULE_OBJS := \
 	menu/menuoptions.o \
 	\
 	parser/anim.o \
+	parser/anim3ds.o \
 	parser/blocklibrary.o \
 	parser/body.o \
 	parser/entity.o \
diff --git a/engines/twine/parser/anim.h b/engines/twine/parser/anim.h
index a10b948aa21..67789194bfc 100644
--- a/engines/twine/parser/anim.h
+++ b/engines/twine/parser/anim.h
@@ -71,8 +71,8 @@ protected:
 public:
 	bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
 
-	const KeyFrame* getKeyframe(uint index) const;
-	const Common::Array<KeyFrame>& getKeyframes() const;
+	const KeyFrame *getKeyframe(uint index) const;
+	const Common::Array<KeyFrame> &getKeyframes() const;
 	uint getNumKeyframes() const;
 	uint16 getLoopFrame() const;
 	uint16 getNumBoneframes() const;
diff --git a/engines/twine/parser/anim3ds.cpp b/engines/twine/parser/anim3ds.cpp
new file mode 100644
index 00000000000..961708b6ed4
--- /dev/null
+++ b/engines/twine/parser/anim3ds.cpp
@@ -0,0 +1,40 @@
+/* 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/>.
+ *
+ */
+
+#include "twine/parser/anim3ds.h"
+
+namespace TwinE {
+
+bool Anim3DSData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
+	assert(!lba1);
+
+	const int n = (int)stream.size() / 8;
+	for (int i = 0; i < n; ++i) {
+		T_ANIM_3DS anim;
+		stream.read(anim.Name, 4);
+		anim.Deb = stream.readSint16LE();
+		anim.Fin = stream.readSint16LE();
+		_anims.push_back(anim);
+	}
+	return !stream.err();
+}
+
+} // namespace TwinE
diff --git a/engines/twine/parser/anim3ds.h b/engines/twine/parser/anim3ds.h
new file mode 100644
index 00000000000..342d09e4a52
--- /dev/null
+++ b/engines/twine/parser/anim3ds.h
@@ -0,0 +1,48 @@
+/* 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/>.
+ *
+ */
+
+#ifndef TWINE_PARSER_ANIM3DS_H
+#define TWINE_PARSER_ANIM3DS_H
+
+#include "twine/parser/parser.h"
+#include "twine/shared.h"
+
+namespace TwinE {
+
+struct T_ANIM_3DS {
+	char Name[4]; // Name of the animation
+	int16 Deb;    // Start frame in the HQR
+	int16 Fin;    // End frame in the HQR
+};
+
+class Anim3DSData : public Parser {
+private:
+	Common::Array<T_ANIM_3DS> _anims; // ListAnim3DS
+
+public:
+	bool loadFromStream(Common::SeekableReadStream &stream, bool lba1) override;
+
+	const Common::Array<T_ANIM_3DS> &getAnims() const { return _anims; }
+};
+
+} // End of namespace TwinE
+
+#endif
diff --git a/engines/twine/resources/resources.cpp b/engines/twine/resources/resources.cpp
index cd26c1b5a25..660f1b3d5a4 100644
--- a/engines/twine/resources/resources.cpp
+++ b/engines/twine/resources/resources.cpp
@@ -24,8 +24,10 @@
 #include "common/tokenizer.h"
 #include "common/util.h"
 #include "twine/audio/sound.h"
+#include "twine/parser/anim3ds.h"
 #include "twine/renderer/renderer.h"
 #include "twine/renderer/screens.h"
+#include "twine/resources/hqr.h"
 #include "twine/scene/animations.h"
 #include "twine/scene/scene.h"
 #include "twine/text.h"
@@ -59,6 +61,18 @@ void Resources::initPalettes() {
 	free(mainPalette);
 }
 
+void Resources::preloadAnim3DS() {
+	const int index = HQR::numEntries(Resources::HQR_ANIM3DS_FILE) - 1;
+	_anim3DSData.loadFromHQR(Resources::HQR_ANIM3DS_FILE, index, _engine->isLBA1());
+}
+
+const T_ANIM_3DS *Resources::getAnim(int index) const {
+	if (index < 0 || index >= (int)_anim3DSData.getAnims().size()) {
+		return nullptr;
+	}
+	return &_anim3DSData.getAnims()[index];
+}
+
 void Resources::preloadSprites() {
 	const int32 numEntries = HQR::numEntries(Resources::HQR_SPRITES_FILE);
 	const int32 maxSprites = _engine->isLBA1() ? 200 : NUM_SPRITES;
@@ -188,6 +202,8 @@ void Resources::initResources() {
 			error("Failed to parse trajectory data");
 		}
 		debug("preload %i trajectories", (int)_trajectories.getTrajectories().size());
+	} else if (_engine->isLBA2()) {
+		preloadAnim3DS();
 	}
 
 	preloadSprites();
diff --git a/engines/twine/resources/resources.h b/engines/twine/resources/resources.h
index 2d76cc36cb0..98575400024 100644
--- a/engines/twine/resources/resources.h
+++ b/engines/twine/resources/resources.h
@@ -24,6 +24,7 @@
 
 #include "common/hashmap.h"
 #include "common/scummsys.h"
+#include "twine/parser/anim3ds.h"
 #include "twine/parser/body.h"
 #include "twine/parser/holomap.h"
 #include "twine/parser/sprite.h"
@@ -134,8 +135,10 @@ private:
 	void initPalettes();
 	/** Preload all sprites */
 	void preloadSprites();
+
 	/** Preload all animations */
 	void preloadAnimations();
+	void preloadAnim3DS();
 	void preloadSamples();
 	void loadMovieInfo();
 
@@ -145,6 +148,7 @@ private:
 	TrajectoryData _trajectories;
 
 	TextData _textData;
+	Anim3DSData _anim3DSData;
 
 public:
 	Resources(TwinEEngine *engine) : _engine(engine) {}
@@ -194,6 +198,7 @@ public:
 	const Trajectory *getTrajectory(int index) const;
 
 	const TextEntry *getText(TextBankId textBankId, TextId index) const;
+	const T_ANIM_3DS *getAnim(int index) const;
 
 	int findSmkMovieIndex(const char *name) const;
 
@@ -233,6 +238,7 @@ public:
 	static constexpr const char *HQR_BODY_FILE = "body.hqr";
 	// animations
 	static constexpr const char *HQR_ANIM_FILE = "anim.hqr";
+	static constexpr const char *HQR_ANIM3DS_FILE = "anim3ds.hqr";
 	// inventory objects
 	static constexpr const char *HQR_INVOBJ_FILE = "invobj.hqr";
 
diff --git a/engines/twine/scene/actor.cpp b/engines/twine/scene/actor.cpp
index 35706fb4c23..61d28fd8d91 100644
--- a/engines/twine/scene/actor.cpp
+++ b/engines/twine/scene/actor.cpp
@@ -133,6 +133,82 @@ void Actor::setBehaviour(HeroBehaviourType behaviour) {
 	_engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeRepeat, AnimationTypes::kAnimInvalid, OWN_ACTOR_SCENE_INDEX);
 }
 
+void Actor::setFrame(int32 actorIdx, uint32 frame) {
+#if 0
+	// TODO: converted from asm - not yet adapted
+	ActorStruct *obj = _engine->_scene->getActor(actorIdx);
+	T_PTR_NUM tempNextBody = obj->NextBody;
+	void *tempNextTexture = obj->NextTexture;
+
+	if (frame >= obj->NbFrames) {
+		return;
+	}
+
+	obj->_body = tempNextBody;
+	obj->Texture = tempNextTexture;
+
+	T_PTR_NUM tempAnim = obj->_anim;
+	void (*TransFctAnim)() = nullptr; // Couldn't find this yet
+
+	if (TransFctAnim != nullptr) {
+		uint32 ebp = frame;
+		TransFctAnim();
+		tempAnim = (T_PTR_NUM)(void *)tempAnim.Ptr;
+		frame = ebp;
+	}
+
+	obj->Interpolator = 0;
+	obj->LastAnimStepX = 0;
+	obj->LastAnimStepY = 0;
+	obj->LastAnimStepZ = 0;
+
+	uint16 nbGroups = *((uint16 *)(tempAnim.Ptr + 2));
+
+	obj->LastAnimStepAlpha = 0;
+	obj->LastAnimStepBeta = 0;
+	obj->LastAnimStepGamma = 0;
+	obj->LastOfsIsPtr = 0;
+
+	uint32 lastOfsFrame = nbGroups * 8 + 8; // infos frame + 4 WORDs per group
+
+	obj->LastNbGroups = nbGroups;
+	obj->NextNbGroups = nbGroups;
+	obj->NbGroups = nbGroups;
+
+	lastOfsFrame *= frame;
+	uint32 timerRefHR = 0; // Replace with actual TimerRefHR
+
+	lastOfsFrame += 8; // Skip header
+
+	obj->LastTimer = timerRefHR;
+	obj->Time = timerRefHR;
+	obj->Status = 1; // STATUS_FRAME
+	obj->LastOfsFrame = lastOfsFrame;
+	obj->LastFrame = frame;
+
+	uint32 ecx = nbGroups * 2 - 2; // 2 DWORDs per group, no group 0
+	T_PTR_NUM ebpPtr = tempAnim;
+	tempAnim.Ptr = tempAnim.Ptr + lastOfsFrame + 16;
+
+	memcpy(obj->CurrentFrame, tempAnim.Ptr, ecx);
+
+	if (++frame == obj->NbFrames) {
+		uint16 time = *((uint16 *)(ebpPtr.Ptr + 8));
+		frame = 0;
+		tempAnim.Ptr = (void *)(8);
+	} else {
+		uint16 time = *((uint16 *)tempAnim.Ptr);
+		tempAnim.Ptr -= ebpPtr.Ptr;
+		tempAnim.Num += obj->LastTimer;
+		obj->NextFrame = frame;
+		obj->NextOfsFrame = (uint32)(tempAnim.Ptr);
+		obj->NextTimer = time;
+		obj->Master = *((uint16 *)(tempAnim.Ptr + 8));
+		obj->Status = 1; // STATUS_FRAME
+	}
+#endif
+}
+
 void Actor::initSprite(int32 spriteNum, int32 actorIdx) {
 	ActorStruct *localActor = _engine->_scene->getActor(actorIdx);
 
diff --git a/engines/twine/scene/actor.h b/engines/twine/scene/actor.h
index 428e8e20341..f05a42d439d 100644
--- a/engines/twine/scene/actor.h
+++ b/engines/twine/scene/actor.h
@@ -76,12 +76,12 @@ struct StaticFlagsStruct {
 	uint32 bIsCarrierActor : 1;             // 0x004000 OBJ_CARRIER - can carry and move an obj
 	// take smaller value for bound, or if not set take average for bound
 	uint32 bUseMiniZv : 1;                  // 0x008000 MINI_ZV - square on smaller dimension (if 3D object)
-	uint32 bHasInvalidPosition : 1;         // 0x010000
-	uint32 bNoElectricShock : 1;            // 0x020000 NO_CHOC
-	uint32 bHasSpriteAnim3D : 1;            // 0x040000
-	uint32 bNoPreClipping : 1;              // 0x080000
-	uint32 bHasZBuffer : 1;                 // 0x100000
-	uint32 bHasZBufferInWater : 1;          // 0x200000
+	uint32 bHasInvalidPosition : 1;         // 0x010000 POS_INVALIDE - carrier considered as an invalid position
+	uint32 bNoElectricShock : 1;            // 0x020000 NO_CHOC - does not trigger electric shock animation
+	uint32 bHasSpriteAnim3D : 1;            // 0x040000 ANIM_3DS - 3DS animation (extension of 3D sprite)
+	uint32 bNoPreClipping : 1;              // 0x080000 NO_PRE_CLIP - does not pre-clip the object (for large objects)
+	uint32 bHasZBuffer : 1;                 // 0x100000 OBJ_ZBUFFER - displays object in ZBuffer (exterior only!)
+	uint32 bHasZBufferInWater : 1;          // 0x200000 OBJ_IN_WATER - displays object in ZBuffer in water (exterior only!)
 };
 
 /** Actors dynamic flags structure */
@@ -183,6 +183,14 @@ public:
 
 	int16 _actorIdx = 0; // own actor index
 	IVec3 _pos; // PosObjX, PosObjY, PosObjZ
+
+	// T_ANIM_3DS - Coord.A3DS
+	struct A3DSAnim {
+		int32 Num;
+		int32 Deb;
+		int32 Fin;
+	} A3DS;
+
 	int32 _strengthOfHit = 0;
 	int32 _hitBy = -1;
 	BonusParameter _bonusParameter;
@@ -315,6 +323,7 @@ public:
 	int16 _heroAnimIdx[4];
 
 	void initSprite(int32 spriteNum, int32 actorIdx);
+	void setFrame(int32 actorIdx, uint32 frame);
 
 	/** Restart hero variables while opening new scenes */
 	void restartHeroScene();
diff --git a/engines/twine/script/script_life_v2.cpp b/engines/twine/script/script_life_v2.cpp
index 86f54356817..bcb783a5a9c 100644
--- a/engines/twine/script/script_life_v2.cpp
+++ b/engines/twine/script/script_life_v2.cpp
@@ -22,6 +22,7 @@
 #include "twine/script/script_life_v2.h"
 #include "twine/audio/sound.h"
 #include "twine/movies.h"
+#include "twine/parser/anim3ds.h"
 #include "twine/renderer/redraw.h"
 #include "twine/renderer/renderer.h"
 #include "twine/renderer/screens.h"
@@ -611,18 +612,17 @@ int32 ScriptLifeV2::lSET_SPRITE(TwinEEngine *engine, LifeScriptContext &ctx) {
 }
 
 int32 ScriptLifeV2::lSET_FRAME_3DS(TwinEEngine *engine, LifeScriptContext &ctx) {
-	const int sprite = ctx.stream.readByte();
+	int sprite = ctx.stream.readByte();
 	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lSET_FRAME_3DS(%i)", (int)sprite);
 	if (ctx.actor->_staticFlags.bHasSpriteAnim3D) {
-		// TODO:
-		// if (sprite > (ListAnim3DS[ptrobj->Coord.A3DS.Num].Fin - ListAnim3DS[ptrobj->Coord.A3DS.Num].Deb)) {
-		// 	sprite = ListAnim3DS[ptrobj->Coord.A3DS.Num].Fin - ListAnim3DS[ptrobj->Coord.A3DS.Num].Deb;
-		// }
-		// sprite += ListAnim3DS[ptrobj->Coord.A3DS.Num].Deb;
-
+		const T_ANIM_3DS *anim = engine->_resources->getAnim(ctx.actor->A3DS.Num);
+		if (sprite >= anim->Fin - anim->Deb) {
+			sprite = anim->Fin - anim->Deb;
+		}
+		sprite += anim->Deb;
 		engine->_actor->initSprite(sprite, ctx.actorIdx);
 	}
-	return -1;
+	return 0;
 }
 
 int32 ScriptLifeV2::lIMPACT_OBJ(TwinEEngine *engine, LifeScriptContext &ctx) {
diff --git a/engines/twine/script/script_move_v2.cpp b/engines/twine/script/script_move_v2.cpp
index e9178864654..1614c7e0240 100644
--- a/engines/twine/script/script_move_v2.cpp
+++ b/engines/twine/script/script_move_v2.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "twine/script/script_move_v2.h"
+#include "twine/resources/resources.h"
 #include "twine/twine.h"
 
 namespace TwinE {
@@ -155,11 +156,26 @@ int32 ScriptMoveV2::mSPRITE(TwinEEngine *engine, MoveScriptContext &ctx) {
 }
 
 int32 ScriptMoveV2::mSET_FRAME(TwinEEngine *engine, MoveScriptContext &ctx) {
-	return -1;
+	const uint8 num = ctx.stream.readByte();
+	if (!ctx.actor->_staticFlags.bIsSpriteActor) {
+		engine->_actor->setFrame(ctx.actorIdx, num);
+	}
+	return 0;
 }
 
 int32 ScriptMoveV2::mSET_FRAME_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {
-	return -1;
+	int32 num = ctx.stream.readByte();
+	if (ctx.actor->_staticFlags.bHasSpriteAnim3D) {
+		const T_ANIM_3DS *anim = engine->_resources->getAnim(ctx.actor->A3DS.Num);
+		if (num > (anim->Fin - anim->Deb)) {
+			num = anim->Fin - anim->Deb;
+		}
+
+		num += anim->Deb;
+
+		engine->_actor->initSprite(num, ctx.actorIdx);
+	}
+	return 0;
 }
 
 int32 ScriptMoveV2::mSET_START_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {


Commit: 8ff7ab977d536373cdf200447d82597e2377c65a
    https://github.com/scummvm/scummvm/commit/8ff7ab977d536373cdf200447d82597e2377c65a
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2024-08-14T17:29:46+02:00

Commit Message:
TWINE: LBA2: added further life opcodes

Changed paths:
    engines/twine/parser/anim3ds.cpp
    engines/twine/script/script_life.cpp
    engines/twine/script/script_life_v2.cpp


diff --git a/engines/twine/parser/anim3ds.cpp b/engines/twine/parser/anim3ds.cpp
index 961708b6ed4..dd335ec2565 100644
--- a/engines/twine/parser/anim3ds.cpp
+++ b/engines/twine/parser/anim3ds.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "twine/parser/anim3ds.h"
+#include "common/debug.h"
 
 namespace TwinE {
 
@@ -27,9 +28,10 @@ bool Anim3DSData::loadFromStream(Common::SeekableReadStream &stream, bool lba1)
 	assert(!lba1);
 
 	const int n = (int)stream.size() / 8;
+	debug("preload %i anim3ds entries", n);
 	for (int i = 0; i < n; ++i) {
 		T_ANIM_3DS anim;
-		stream.read(anim.Name, 4);
+		stream.read(anim.Name, sizeof(anim.Name));
 		anim.Deb = stream.readSint16LE();
 		anim.Fin = stream.readSint16LE();
 		_anims.push_back(anim);
diff --git a/engines/twine/script/script_life.cpp b/engines/twine/script/script_life.cpp
index d886ef213dd..7237bc0ab3a 100644
--- a/engines/twine/script/script_life.cpp
+++ b/engines/twine/script/script_life.cpp
@@ -1644,6 +1644,9 @@ int32 ScriptLife::lCLR_HOLO_POS(TwinEEngine *engine, LifeScriptContext &ctx) {
 int32 ScriptLife::lADD_FUEL(TwinEEngine *engine, LifeScriptContext &ctx) {
 	const int16 value = ctx.stream.readByte();
 	debugC(3, kDebugLevels::kDebugScripts, "LIFE::ADD_FUEL(%i)", (int)value);
+	if (engine->isLBA2()) {
+		return 0;
+	}
 	engine->_gameState->addGas(value);
 	return 0;
 }
@@ -1655,6 +1658,9 @@ int32 ScriptLife::lADD_FUEL(TwinEEngine *engine, LifeScriptContext &ctx) {
 int32 ScriptLife::lSUB_FUEL(TwinEEngine *engine, LifeScriptContext &ctx) {
 	const int16 value = ctx.stream.readByte();
 	debugC(3, kDebugLevels::kDebugScripts, "LIFE::SUB_FUEL(%i)", (int)value);
+	if (engine->isLBA2()) {
+		return 0;
+	}
 	engine->_gameState->addGas(-value);
 	return 0;
 }
diff --git a/engines/twine/script/script_life_v2.cpp b/engines/twine/script/script_life_v2.cpp
index bcb783a5a9c..337f8c633c8 100644
--- a/engines/twine/script/script_life_v2.cpp
+++ b/engines/twine/script/script_life_v2.cpp
@@ -641,23 +641,47 @@ int32 ScriptLifeV2::lIMPACT_POINT(TwinEEngine *engine, LifeScriptContext &ctx) {
 	const uint8 brickTrackId = ctx.stream.readByte();
 	const uint16 n = ctx.stream.readUint16LE();
 	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lIMPACT_POINT(%i, %i)", (int)brickTrackId, (int)n);
-	// int16 x0 = ListBrickTrack[brickTrackId].x;
-	// int16 y0 = ListBrickTrack[brickTrackId].y;
-	// int16 z0 = ListBrickTrack[brickTrackId].z;
+	// const IVec3 &pos = engine->_scene->_sceneTracks[brickTrackId];
+	// int16 x0 = pos.x;
+	// int16 y0 = pos.y;
+	// int16 z0 = pos.z;
 	// TODO: DoImpact(n, x0, y0, z0, ctx.actorIdx);
 	return -1;
 }
 
+// ECHELLE
 int32 ScriptLifeV2::lLADDER(TwinEEngine *engine, LifeScriptContext &ctx) {
-	return -1;
+	const uint8 num = ctx.stream.readByte();
+	const uint8 info = ctx.stream.readByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lLADDER(%i, %i)", (int)num, (int)info);
+	int n = 0;
+	while (n < engine->_scene->_sceneNumZones) {
+		ZoneStruct &zone = engine->_scene->_sceneZones[n];
+		if (zone.type == ZoneType::kLadder && zone.num == num) {
+			zone.infoData.generic.info1 = (int32)info;
+		}
+		++n;
+	}
+	return 0;
 }
 
+// SET_ARMURE
 int32 ScriptLifeV2::lSET_ARMOR(TwinEEngine *engine, LifeScriptContext &ctx) {
-	return -1;
+	const int8 armor = ctx.stream.readSByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lSET_ARMOR(%i)", (int)armor);
+	ctx.actor->_armor = (int32)armor;
+	return 0;
 }
 
+// SET_ARMURE_OBJ
 int32 ScriptLifeV2::lSET_ARMOR_OBJ(TwinEEngine *engine, LifeScriptContext &ctx) {
-	return -1;
+	const uint8 num = ctx.stream.readByte();
+	const int8 armor = ctx.stream.readSByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lSET_ARMOR_OBJ(%i, %i)", (int)num, (int)armor);
+	if (ActorStruct *actor = engine->_scene->getActor(num)) {
+		actor->_armor = (int32)armor;
+	}
+	return 0;
 }
 
 int32 ScriptLifeV2::lADD_LIFE_POINT_OBJ(TwinEEngine *engine, LifeScriptContext &ctx) {
@@ -665,6 +689,10 @@ int32 ScriptLifeV2::lADD_LIFE_POINT_OBJ(TwinEEngine *engine, LifeScriptContext &
 }
 
 int32 ScriptLifeV2::lSTATE_INVENTORY(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const uint8 num = ctx.stream.readByte(); // num vargame
+	const uint8 idObj3D = ctx.stream.readByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lSTATE_INVENTORY(%i, %i)", (int)num, (int)idObj3D);
+	// TODO: TabInv[num].IdObj3D = idObj3D;
 	return -1;
 }
 


Commit: 617ba4154d3c8faea900ce191d92faea3421f723
    https://github.com/scummvm/scummvm/commit/617ba4154d3c8faea900ce191d92faea3421f723
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2024-08-14T17:29:46+02:00

Commit Message:
TWINE: LBA2: implemented more opcodes

Changed paths:
    engines/twine/audio/music.h
    engines/twine/menu/interface.h
    engines/twine/scene/actor.h
    engines/twine/scene/scene.cpp
    engines/twine/scene/scene.h
    engines/twine/script/script_life.cpp
    engines/twine/script/script_life_v2.cpp
    engines/twine/shared.h


diff --git a/engines/twine/audio/music.h b/engines/twine/audio/music.h
index 818e3ec9b32..d580553e2a8 100644
--- a/engines/twine/audio/music.h
+++ b/engines/twine/audio/music.h
@@ -51,6 +51,12 @@ private:
 	Audio::SoundHandle _midiHandle;
 	/** Track number of the current playing music */
 	int32 currentMusic = -1;
+public:
+	// TODO: implement the handling
+	int32 _nextMusic = -1;       // lba2: NextMusic
+	int32 _nextMusicTimer;       // lba2: NextMusicTimer
+	bool _stopLastMusic = false; // lba2: StopLastMusic
+private:
 	/**
 	 * Play CD music
 	 * @param track track number to play
diff --git a/engines/twine/menu/interface.h b/engines/twine/menu/interface.h
index 1b60a59c95f..0c2696723b0 100644
--- a/engines/twine/menu/interface.h
+++ b/engines/twine/menu/interface.h
@@ -41,6 +41,7 @@ private:
 public:
 	Interface(TwinEEngine *engine);
 	Common::Rect _clip { 0, 0, 0, 0 };
+	bool _animateTexture = false; // lba2: AnimateTexture
 
 	/**
 	 * Draw button line
diff --git a/engines/twine/scene/actor.h b/engines/twine/scene/actor.h
index f05a42d439d..0b751dfb6f9 100644
--- a/engines/twine/scene/actor.h
+++ b/engines/twine/scene/actor.h
@@ -245,6 +245,8 @@ public:
 	uint8 _brickSound = 0U; // CodeJeu
 	int32 SampleAlways = 0; // lba2
 	uint8 SampleVolume = 0; // lba2
+	// SizeSHit contains the number of the brick under the wagon - hack
+	int16 SizeSHit; // lba2 - always square
 
 	BoundingBox _boundingBox; // Xmin, YMin, Zmin, Xmax, Ymax, Zmax
 	ActorMoveStruct realAngle;
diff --git a/engines/twine/scene/scene.cpp b/engines/twine/scene/scene.cpp
index bb68aded887..9191395d8a4 100644
--- a/engines/twine/scene/scene.cpp
+++ b/engines/twine/scene/scene.cpp
@@ -673,11 +673,13 @@ void Scene::initSceneVars() {
 }
 
 void Scene::playSceneMusic() {
-	if (_currentSceneIdx == LBA1SceneId::Tippet_Island_Twinsun_Cafe && _engine->_gameState->hasArrivedHamalayi()) {
-		_engine->_music->playTrackMusic(8);
-	} else {
-		_engine->_music->playMidiMusic(_sceneMusic);
+	if (_engine->isLBA1()) {
+		if (_currentSceneIdx == LBA1SceneId::Tippet_Island_Twinsun_Cafe && _engine->_gameState->hasArrivedHamalayi()) {
+			_engine->_music->playTrackMusic(8);
+			return;
+		}
 	}
+	_engine->_music->playMidiMusic(_sceneMusic);
 }
 
 void Scene::processEnvironmentSound() {
diff --git a/engines/twine/scene/scene.h b/engines/twine/scene/scene.h
index 7dfc81df009..d5bf6d66b7b 100644
--- a/engines/twine/scene/scene.h
+++ b/engines/twine/scene/scene.h
@@ -146,8 +146,9 @@ private:
 
 	int16 _samplePlayed = 0;
 
-	int16 _sceneMusic = 0;
-
+public:
+	int16 _sceneMusic = 0;  // CubeJingle
+private:
 	IVec3 _sceneHeroPos;
 	IVec3 _zoneHeroPos;
 
diff --git a/engines/twine/script/script_life.cpp b/engines/twine/script/script_life.cpp
index 7237bc0ab3a..c2fccd6dbff 100644
--- a/engines/twine/script/script_life.cpp
+++ b/engines/twine/script/script_life.cpp
@@ -924,8 +924,10 @@ int32 ScriptLife::lMESSAGE(TwinEEngine *engine, LifeScriptContext &ctx) {
 	}
 
 	engine->_text->drawTextProgressive(textIdx);
-	if (engine->_scene->_currentSceneIdx == LBA1SceneId::Principal_Island_Library && engine->_scene->_talkingActor == 8 && textIdx == TextId::kStarWarsFanBoy) {
-		engine->unlockAchievement("LBA_ACH_008");
+	if (engine->isLBA1()) {
+		if (engine->_scene->_currentSceneIdx == LBA1SceneId::Principal_Island_Library && engine->_scene->_talkingActor == 8 && textIdx == TextId::kStarWarsFanBoy) {
+			engine->unlockAchievement("LBA_ACH_008");
+		}
 	}
 	engine->_redraw->redrawEngineActions(true);
 
diff --git a/engines/twine/script/script_life_v2.cpp b/engines/twine/script/script_life_v2.cpp
index 337f8c633c8..fee9c3e3452 100644
--- a/engines/twine/script/script_life_v2.cpp
+++ b/engines/twine/script/script_life_v2.cpp
@@ -21,7 +21,9 @@
 
 #include "twine/script/script_life_v2.h"
 #include "twine/audio/sound.h"
+#include "twine/audio/music.h"
 #include "twine/movies.h"
+#include "twine/menu/interface.h"
 #include "twine/parser/anim3ds.h"
 #include "twine/renderer/redraw.h"
 #include "twine/renderer/renderer.h"
@@ -215,8 +217,15 @@ int32 ScriptLifeV2::lFADE_TO_PAL(TwinEEngine *engine, LifeScriptContext &ctx) {
 
 int32 ScriptLifeV2::lPLAY_MUSIC(TwinEEngine *engine, LifeScriptContext &ctx) {
 	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lPLAY_MUSIC()");
-	// TODO: game var 157 is checked here
-	return lPLAY_CD_TRACK(engine, ctx);
+	const int32 val = lPLAY_CD_TRACK(engine, ctx);
+	if (engine->isLBA2()) {
+		engine->_scene->_sceneMusic = 255;
+		engine->_music->_nextMusic = -1;
+		if (engine->_gameState->hasGameFlag(157) > 0) {
+			engine->_music->_stopLastMusic = false;
+		}
+	}
+	return val;
 }
 
 int32 ScriptLifeV2::lTRACK_TO_VAR_GAME(TwinEEngine *engine, LifeScriptContext &ctx) {
@@ -429,18 +438,32 @@ int32 ScriptLifeV2::lVAR_GAME_TO_TRACK(TwinEEngine *engine, LifeScriptContext &c
 }
 
 int32 ScriptLifeV2::lANIM_TEXTURE(TwinEEngine *engine, LifeScriptContext &ctx) {
-	return -1;
+	engine->_interface->_animateTexture = ctx.stream.readByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lANIM_TEXTURE(%i)", (int)engine->_interface->_animateTexture);
+	return 0;
 }
 
 int32 ScriptLifeV2::lADD_MESSAGE_OBJ(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const TextId textIdx = (TextId)ctx.stream.readSint16LE();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lADD_MESSAGE_OBJ(%i)", (int)textIdx);
+	// TODO: implement me
 	return -1;
 }
 
 int32 ScriptLifeV2::lADD_MESSAGE(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const TextId textIdx = (TextId)ctx.stream.readSint16LE();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lADD_MESSAGE(%i)", (int)textIdx);
+	// TODO: implement me
 	return -1;
 }
 
 int32 ScriptLifeV2::lCAMERA_CENTER(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const int32 angle = ClampAngle(ToAngle(ctx.stream.readByte() * 1024));
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lCAMERA_CENTER(%i)", (int)angle);
+	// TODO: implement me - see centerOnActor in grid
+	// AddBetaCam = num ;
+	// CameraCenter( 2 ) ;
+	// FirstTime = AFF_ALL_FLIP ;
 	return -1;
 }
 
@@ -458,6 +481,9 @@ int32 ScriptLifeV2::lNO_CHOC(TwinEEngine *engine, LifeScriptContext &ctx) {
 }
 
 int32 ScriptLifeV2::lCINEMA_MODE(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const uint8 val = ctx.stream.readByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lCINEMA_MODE(%i)", (int)val);
+	// TODO: implement me
 	return -1;
 }
 
@@ -576,14 +602,37 @@ int32 ScriptLifeV2::lINIT_BUGGY(TwinEEngine *engine, LifeScriptContext &ctx) {
 }
 
 int32 ScriptLifeV2::lMEMO_ARDOISE(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const uint8 num = ctx.stream.readByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lMEMO_ARDOISE(%i)", (int)num);
+	if (engine->_gameState->hasGameFlag(GAMEFLAG_ARDOISE)) {
+		// TODO: implement me
+	}
 	return -1;
 }
 
 int32 ScriptLifeV2::lSET_CHANGE_CUBE(TwinEEngine *engine, LifeScriptContext &ctx) {
-	return -1;
+	const uint8 num = ctx.stream.readByte();
+	const uint8 info = ctx.stream.readByte();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lSET_CHANGE_CUBE(%i, %i)", (int)num, (int)info);
+	int n = 0;
+	while (n < engine->_scene->_sceneNumZones) {
+		ZoneStruct &zone = engine->_scene->_sceneZones[n];
+		if (zone.type == ZoneType::kCube && zone.num == num) {
+			if (info) {
+				zone.infoData.generic.info7 |= ZONE_ON;
+			} else {
+				zone.infoData.generic.info7 &= ~ZONE_ON;
+			}
+		}
+		++n;
+	}
+	return 0;
 }
 
 int32 ScriptLifeV2::lMESSAGE_ZOE(TwinEEngine *engine, LifeScriptContext &ctx) {
+	const int16 textIdx = ctx.stream.readSint16LE();
+	debugC(3, kDebugLevels::kDebugScripts, "LIFE::lMESSAGE_ZOE(%i)", (int)textIdx);
+	// TODO: implement me
 	return -1;
 }
 
@@ -871,13 +920,13 @@ int32 ScriptLifeV2::lINVERSE_BETA(TwinEEngine *engine, LifeScriptContext &ctx) {
 
 		// to be clean
 		APtObj = ctx.actor;
+#endif
 
-		// SizeSHit contains the number of the brick under the wagon
+		// SizeSHit contains the number of the brick under the wagon hack
 		// test front axle position
 		engine->_wagon->AdjustEssieuWagonAvant(ctx.actor->SizeSHit);
 		// test rear axle position
 		engine->_wagon->AdjustEssieuWagonArriere(ctx.actor->SizeSHit);
-#endif
 	}
 
 	// To tell an object that it is no longer being carried by me
diff --git a/engines/twine/shared.h b/engines/twine/shared.h
index 03c95aeb00a..b8cc099766b 100644
--- a/engines/twine/shared.h
+++ b/engines/twine/shared.h
@@ -81,6 +81,8 @@
 
 // lba2 Kashes or Zlitos
 #define GAMEFLAG_MONEY 8
+// FLAG_ARDOISE
+#define GAMEFLAG_ARDOISE 28
 
 #define OWN_ACTOR_SCENE_INDEX 0
 #define IS_HERO(x) ((x) == OWN_ACTOR_SCENE_INDEX)
@@ -736,6 +738,7 @@ inline int32 NormalizeAngle(int32 angle) {
  * @return The value as it is used at runtime
  */
 inline constexpr int32 ToAngle(int32 angle) {
+	// TODO: lba2 handling of factor 4
 	return angle;
 }
 




More information about the Scummvm-git-logs mailing list