[Scummvm-git-logs] scummvm master -> 1e9337f5c704ad7425df951f9c31f2099d17de50
mgerhardy
noreply at scummvm.org
Fri Jan 2 22:33:37 UTC 2026
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
1a23d89821 TWINE: reference to original name
e029e88c0d TWINE: LBA2: implemented a few more opcodes
1e9337f5c7 TWINE: LBA2: started with filling the rain stubs
Commit: 1a23d8982155a43406e3160ea755caeb351737de
https://github.com/scummvm/scummvm/commit/1a23d8982155a43406e3160ea755caeb351737de
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-01-02T23:26:37+01:00
Commit Message:
TWINE: reference to original name
Changed paths:
engines/twine/scene/grid.h
diff --git a/engines/twine/scene/grid.h b/engines/twine/scene/grid.h
index 74d7b0ec0ab..5c9446f06dc 100644
--- a/engines/twine/scene/grid.h
+++ b/engines/twine/scene/grid.h
@@ -22,7 +22,7 @@
#ifndef TWINE_SCENE_GRID_H
#define TWINE_SCENE_GRID_H
-#define WATER_BRICK (0xF1)
+#define WATER_BRICK (0xF1) // CJ_WATER
#include "common/scummsys.h"
#include "twine/parser/blocklibrary.h"
Commit: e029e88c0d3e2750f43ff0dffb86553404e855d8
https://github.com/scummvm/scummvm/commit/e029e88c0d3e2750f43ff0dffb86553404e855d8
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-01-02T23:26:46+01:00
Commit Message:
TWINE: LBA2: implemented a few more opcodes
Changed paths:
engines/twine/audio/sound.h
engines/twine/renderer/redraw.cpp
engines/twine/renderer/redraw.h
engines/twine/scene/collision.cpp
engines/twine/scene/collision.h
engines/twine/scene/grid.h
engines/twine/scene/scene.h
engines/twine/script/script_life_v2.cpp
engines/twine/script/script_move_v2.cpp
engines/twine/script/script_move_v2.h
engines/twine/shared.h
diff --git a/engines/twine/audio/sound.h b/engines/twine/audio/sound.h
index 21172090dc0..5f202131c59 100644
--- a/engines/twine/audio/sound.h
+++ b/engines/twine/audio/sound.h
@@ -70,6 +70,10 @@ private:
/** Remove a sample from the channel usage list */
void removeChannelWatch(int32 channelIdx);
public:
+ int32 _parmSampleVolume = 127;
+ int32 _parmSampleDecalage = 0;
+ int32 _parmSampleFrequence = 0;
+
Sound(TwinEEngine *engine);
~Sound();
diff --git a/engines/twine/renderer/redraw.cpp b/engines/twine/renderer/redraw.cpp
index 24f4ef0fef0..3a55dfc5a87 100644
--- a/engines/twine/renderer/redraw.cpp
+++ b/engines/twine/renderer/redraw.cpp
@@ -855,10 +855,13 @@ void Redraw::setRenderText(const Common::String &text) {
}
void Redraw::renderText() {
- if (_textDisappearTime <= _engine->timerRef) {
+ if (_text.empty()) {
return;
}
- if (_text.empty()) {
+
+ if (_textDisappearTime != -1 && _engine->timerRef > _textDisappearTime) {
+ _text.clear();
+ _textDisappearTime = -1;
return;
}
_engine->_text->setFontColor(COLOR_WHITE);
@@ -874,6 +877,10 @@ void Redraw::renderText() {
addPhysBox(redraw);
}
+void Redraw::fillBackground(uint8 color) {
+ _engine->_frontVideoBuffer.fillRect(Common::Rect(0, 0, _engine->width(), _engine->height()), color);
+}
+
void Redraw::drawScene(bool flagflip) { // AffScene
int32 tmp_projPosX = _projPosScreen.x;
int32 tmp_projPosY = _projPosScreen.y;
diff --git a/engines/twine/renderer/redraw.h b/engines/twine/renderer/redraw.h
index 678746f3bbb..b2089df25ec 100644
--- a/engines/twine/renderer/redraw.h
+++ b/engines/twine/renderer/redraw.h
@@ -141,6 +141,8 @@ private:
void renderOverlays();
void renderText();
+ void fillBackground(uint8 color);
+
public:
Redraw(TwinEEngine *engine);
diff --git a/engines/twine/scene/collision.cpp b/engines/twine/scene/collision.cpp
index e12e7de8db9..bb8b8cd3672 100644
--- a/engines/twine/scene/collision.cpp
+++ b/engines/twine/scene/collision.cpp
@@ -516,4 +516,9 @@ int32 Collision::extraCheckExtraCol(ExtraListStruct *extra, int32 extraIdx) cons
return -1;
}
+void Collision::doImpact(int32 num, int32 x, int32 y, int32 z, int32 owner) {
+ debugC(3, kDebugLevels::kDebugCollision, "Collision::doImpact(%i, %i, %i, %i, %i)", num, x, y, z, owner);
+ // TODO: Implement me
+}
+
} // namespace TwinE
diff --git a/engines/twine/scene/collision.h b/engines/twine/scene/collision.h
index c0beca7f5bb..d60896189f8 100644
--- a/engines/twine/scene/collision.h
+++ b/engines/twine/scene/collision.h
@@ -51,6 +51,8 @@ public:
*/
bool checkZvOnZv(int32 actorIdx1, int32 actorIdx2) const;
+ void doImpact(int32 num, int32 x, int32 y, int32 z, int32 owner);
+
/**
* Reajust actor position in scene according with brick shape bellow actor
* @param brickShape Shape of brick bellow the actor
diff --git a/engines/twine/scene/grid.h b/engines/twine/scene/grid.h
index 5c9446f06dc..712519b155f 100644
--- a/engines/twine/scene/grid.h
+++ b/engines/twine/scene/grid.h
@@ -199,6 +199,8 @@ public:
/** Current grid camera x, y and z coordinates */
IVec3 _worldCube; // WorldXCube WorldYCube
+ int32 _addBetaCam = 0;
+
/** Flag to know if the engine is using celling grids */
int16 _zoneGrm = 0;
/** Current celling grid index */
diff --git a/engines/twine/scene/scene.h b/engines/twine/scene/scene.h
index 401b450557a..dcd4abde649 100644
--- a/engines/twine/scene/scene.h
+++ b/engines/twine/scene/scene.h
@@ -177,6 +177,7 @@ public:
uint8 _island = 0;
uint8 _shadowLevel = 0; // lba2
uint8 _modeLabyrinthe = 0; // lba2
+ uint8 _cinemaMode = 0; // lba2
uint8 _currentCubeX = 0; // lba2
uint8 _currentCubeY = 0; // lba2
diff --git a/engines/twine/script/script_life_v2.cpp b/engines/twine/script/script_life_v2.cpp
index 6394f8d7ce3..a2586d3a26d 100644
--- a/engines/twine/script/script_life_v2.cpp
+++ b/engines/twine/script/script_life_v2.cpp
@@ -19,6 +19,8 @@
*
*/
+#include "twine/scene/grid.h"
+#include "twine/scene/collision.h"
#include "twine/script/script_life_v2.h"
#include "twine/audio/sound.h"
#include "twine/audio/music.h"
@@ -212,8 +214,15 @@ int32 ScriptLifeV2::lPALETTE(TwinEEngine *engine, LifeScriptContext &ctx) {
int32 ScriptLifeV2::lFADE_TO_PAL(TwinEEngine *engine, LifeScriptContext &ctx) {
const int32 palIndex = engine->_screens->mapLba2Palette(ctx.stream.readByte());
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::FADE_TO_PAL(%i)", palIndex);
- // TODO: implement
- return -1;
+ engine->saveTimer(false);
+ HQR::getPaletteEntry(engine->_screens->_ptrPal, Resources::HQR_RESS_FILE, palIndex);
+ engine->_screens->fadeToPal(engine->_screens->_ptrPal);
+ engine->_screens->_flagPalettePcx = true;
+ if (palIndex == 3) { // Black palette?
+ engine->_screens->_flagFade = true;
+ }
+ engine->restoreTimer();
+ return 0;
}
int32 ScriptLifeV2::lPLAY_MUSIC(TwinEEngine *engine, LifeScriptContext &ctx) {
@@ -445,27 +454,51 @@ int32 ScriptLifeV2::lANIM_TEXTURE(TwinEEngine *engine, LifeScriptContext &ctx) {
}
int32 ScriptLifeV2::lADD_MESSAGE_OBJ(TwinEEngine *engine, LifeScriptContext &ctx) {
+ const int32 actorIdx = ctx.stream.readByte();
const TextId textIdx = (TextId)ctx.stream.readSint16LE();
- debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lADD_MESSAGE_OBJ(%i)", (int)textIdx);
- // TODO: implement me
- return -1;
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lADD_MESSAGE_OBJ(%i, %i)", (int)actorIdx, (int)textIdx);
+
+ if (engine->_scene->getActor(actorIdx)->_lifePoint > 0) {
+ engine->saveTimer(false);
+ engine->testRestoreModeSVGA(true);
+ if (engine->_text->_showDialogueBubble) {
+ engine->_redraw->drawBubble(actorIdx);
+ }
+ engine->_text->setFontCrossColor(engine->_scene->getActor(actorIdx)->_talkColor);
+ engine->_scene->_talkingActor = (int16)actorIdx;
+ engine->setPalette(engine->_screens->_ptrPal);
+ engine->_text->drawTextProgressive(textIdx);
+ engine->_redraw->drawScene(true);
+ engine->restoreTimer();
+ }
+ return 0;
}
int32 ScriptLifeV2::lADD_MESSAGE(TwinEEngine *engine, LifeScriptContext &ctx) {
const TextId textIdx = (TextId)ctx.stream.readSint16LE();
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lADD_MESSAGE(%i)", (int)textIdx);
- // TODO: implement me
- return -1;
+
+ engine->saveTimer(false);
+ engine->testRestoreModeSVGA(true);
+ if (engine->_text->_showDialogueBubble) {
+ engine->_redraw->drawBubble(ctx.actorIdx);
+ }
+ engine->_text->setFontCrossColor(ctx.actor->_talkColor);
+ engine->_scene->_talkingActor = (int16)ctx.actorIdx;
+ engine->setPalette(engine->_screens->_ptrPal);
+ engine->_text->drawTextProgressive(textIdx);
+ engine->_redraw->drawScene(true);
+ engine->restoreTimer();
+ return 0;
}
int32 ScriptLifeV2::lCAMERA_CENTER(TwinEEngine *engine, LifeScriptContext &ctx) {
const int32 angle = ClampAngle(ToAngle(ctx.stream.readByte() * 1024));
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lCAMERA_CENTER(%i)", (int)angle);
- // TODO: implement me - see centerOnActor in grid
- // AddBetaCam = num ;
- // CameraCenter( 2 ) ;
- // FirstTime = AFF_ALL_FLIP ;
- return -1;
+ engine->_grid->_addBetaCam = angle;
+ engine->_grid->centerOnActor(engine->_scene->getActor(2));
+ engine->_redraw->_firstTime = true;
+ return 0;
}
int32 ScriptLifeV2::lBUBBLE(TwinEEngine *engine, LifeScriptContext &ctx) {
@@ -482,10 +515,22 @@ 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::kDebugScriptsLife, "LIFE::lCINEMA_MODE(%i)", (int)val);
- // TODO: implement me
- return -1;
+ const uint8 num = ctx.stream.readByte();
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lCINEMA_MODE(%i)", (int)num);
+ if (num != engine->_scene->_cinemaMode) {
+ engine->_scene->_cinemaMode = num;
+ if (!num) {
+ engine->_redraw->_firstTime = true;
+ // TODO: DureeCycleCinema = BoundRegleTrois( 1, 500, 39, ClipWindowYMin ) ;
+ } else {
+ // TODO: DureeCycleCinema = BoundRegleTrois( 1, 500, 39, 39-ClipWindowYMin ) ;
+ engine->_gameState->setGameFlag(GAMEFLAG_ESC, 0);
+ }
+ // TODO: DebCycleCinema = ClipWindowYMin ;
+ // TODO: LastYCinema = ClipWindowYMin ;
+ // TODO: TimerCinema = TimerRefHR ;
+ }
+ return 0;
}
int32 ScriptLifeV2::lSAVE_HERO(TwinEEngine *engine, LifeScriptContext &ctx) {
@@ -565,9 +610,18 @@ int32 ScriptLifeV2::lPLAY_ACF(TwinEEngine *engine, LifeScriptContext &ctx) {
} while (true);
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lPLAY_ACF(%s)", movie);
+ if (!engine->_screens->_flagFade) {
+ // TODO: FadeToBlackAndSamples( PtrPal ) ;
+ }
+ engine->_screens->_flagFade = true;
+
engine->_movie->playMovie(movie);
// TODO: lba2 is doing more stuff here - reset the cinema mode, init the scene and palette stuff
+ // if (CubeMode==CUBE_INTERIEUR) InitGrille( NumCube ) ;
+ // RazListPartFlow() ;
+ // ChoicePalette() ;
engine->setPalette(engine->_screens->_ptrPal);
+ engine->_screens->_flagFade = true;
engine->restoreTimer();
engine->_redraw->_firstTime = true;
@@ -632,10 +686,24 @@ int32 ScriptLifeV2::lSET_CHANGE_CUBE(TwinEEngine *engine, LifeScriptContext &ctx
}
int32 ScriptLifeV2::lMESSAGE_ZOE(TwinEEngine *engine, LifeScriptContext &ctx) {
- const int16 textIdx = ctx.stream.readSint16LE();
+ const TextId textIdx = (TextId)ctx.stream.readSint16LE();
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lMESSAGE_ZOE(%i)", (int)textIdx);
- // TODO: implement me
- return -1;
+
+ engine->_scene->_talkingActor = OWN_ACTOR_SCENE_INDEX;
+ ActorStruct *hero = engine->_scene->getActor(OWN_ACTOR_SCENE_INDEX);
+ int32 oldColor = hero->_talkColor;
+ hero->_talkColor = 1; // COUL_ZOE
+
+ engine->saveTimer(false);
+ engine->testRestoreModeSVGA(true);
+ engine->_text->setFontCrossColor(hero->_talkColor);
+ engine->setPalette(engine->_screens->_ptrPal);
+ engine->_text->drawTextProgressive(textIdx);
+ engine->_redraw->drawScene(true);
+ engine->restoreTimer();
+
+ hero->_talkColor = oldColor;
+ return 0;
}
int32 ScriptLifeV2::lACTION(TwinEEngine *engine, LifeScriptContext &ctx) {
@@ -648,9 +716,9 @@ int32 ScriptLifeV2::lSET_FRAME(TwinEEngine *engine, LifeScriptContext &ctx) {
const int frame = ctx.stream.readByte();
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSET_FRAME(%i)", (int)frame);
if (!ctx.actor->_flags.bSprite3D) {
- // TODO: ObjectSetFrame(ctx.actorIdx, frame);
+ engine->_actor->setFrame(ctx.actorIdx, frame);
}
- return -1;
+ return 0;
}
int32 ScriptLifeV2::lSET_SPRITE(TwinEEngine *engine, LifeScriptContext &ctx) {
@@ -683,21 +751,18 @@ int32 ScriptLifeV2::lIMPACT_OBJ(TwinEEngine *engine, LifeScriptContext &ctx) {
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lIMPACT_OBJ(%i, %i, %i)", (int)num, (int)n, (int)y);
ActorStruct *otherActor = engine->_scene->getActor(num);
if (otherActor->_lifePoint > 0) {
- // TODO: DoImpact(n, otherActor->_pos.x, otherActor->_pos.y + y, otherActor->_pos.z, num);
+ engine->_collision->doImpact(n, otherActor->posObj().x, otherActor->posObj().y + y, otherActor->posObj().z, num);
}
- return -1;
+ return 0;
}
int32 ScriptLifeV2::lIMPACT_POINT(TwinEEngine *engine, LifeScriptContext &ctx) {
const uint8 brickTrackId = ctx.stream.readByte();
const uint16 n = ctx.stream.readUint16LE();
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lIMPACT_POINT(%i, %i)", (int)brickTrackId, (int)n);
- // 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;
+ const IVec3 &pos = engine->_scene->_sceneTracks[brickTrackId];
+ engine->_collision->doImpact(n, pos.x, pos.y, pos.z, ctx.actorIdx);
+ return 0;
}
// ECHELLE
@@ -810,43 +875,63 @@ int32 ScriptLifeV2::lRESTORE_COMPORTEMENT(TwinEEngine *engine, LifeScriptContext
}
int32 ScriptLifeV2::lSAMPLE(TwinEEngine *engine, LifeScriptContext &ctx) {
- const int16 sample = ctx.stream.readSint16LE();
- debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE(%i)", (int)sample);
- // TODO: HQ_3D_MixSample(sample, 0x1000, 0, 1, ctx.actor->posObj());
- return -1;
+ const uint16 sampleIdx = ctx.stream.readUint16LE();
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE(%i)", (int)sampleIdx);
+ engine->_sound->mixSample3D(sampleIdx, 0x1000, 1, ctx.actor->posObj(), ctx.actorIdx);
+ return 0;
}
int32 ScriptLifeV2::lSAMPLE_RND(TwinEEngine *engine, LifeScriptContext &ctx) {
- const int16 sample = ctx.stream.readSint16LE();
- debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE_RND(%i)", (int)sample);
- // TODO: HQ_3D_MixSample(sample, 0x800, 0x1000, 1, ctx.actor->posObj());
- return -1;
+ const uint16 sampleIdx = ctx.stream.readUint16LE();
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE_RND(%i)", (int)sampleIdx);
+ int32 frequency = 0x800 + engine->getRandomNumber(0x800);
+ engine->_sound->mixSample3D(sampleIdx, frequency, 1, ctx.actor->posObj(), ctx.actorIdx);
+ return 0;
}
int32 ScriptLifeV2::lSAMPLE_ALWAYS(TwinEEngine *engine, LifeScriptContext &ctx) {
- const int16 sample = ctx.stream.readSint16LE();
- debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE_ALWAYS(%i)", (int)sample);
- // TODO:
- return -1;
+ const uint16 sampleIdx = ctx.stream.readUint16LE();
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE_ALWAYS(%i)", (int)sampleIdx);
+ if (!engine->_sound->isSamplePlaying(sampleIdx)) {
+ engine->_sound->mixSample3D(sampleIdx, 0x1000, 1, ctx.actor->posObj(), ctx.actorIdx);
+ }
+ return 0;
}
int32 ScriptLifeV2::lSAMPLE_STOP(TwinEEngine *engine, LifeScriptContext &ctx) {
- const int16 sample = ctx.stream.readSint16LE();
- debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE_STOP(%i)", (int)sample);
- // TODO:
- return -1;
+ const uint16 sampleIdx = ctx.stream.readUint16LE();
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lSAMPLE_STOP(%i)", (int)sampleIdx);
+ engine->_sound->stopSample(sampleIdx);
+ return 0;
}
int32 ScriptLifeV2::lREPEAT_SAMPLE(TwinEEngine *engine, LifeScriptContext &ctx) {
- const int16 sample = ctx.stream.readSint16LE();
- uint8 repeat = ctx.stream.readByte();
- debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lREPEAT_SAMPLE(%i, %i)", (int)sample, (int)repeat);
- // TODO: HQ_3D_MixSample(sample, 0x1000, 0, repeat, ctx.actor->posObj());
- return -1;
+ const uint16 sampleIdx = ctx.stream.readUint16LE();
+ const uint8 num = ctx.stream.readByte();
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lREPEAT_SAMPLE(%i, %i)", (int)sampleIdx, (int)num);
+ engine->_sound->mixSample3D(sampleIdx, 0x1000, num, ctx.actor->posObj(), ctx.actorIdx);
+ return 0;
}
int32 ScriptLifeV2::lBACKGROUND(TwinEEngine *engine, LifeScriptContext &ctx) {
- return -1;
+ const uint8 val = ctx.stream.readByte();
+ debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::lBACKGROUND(%i)", (int)val);
+ if (val != 0) {
+ if (!ctx.actor->_flags.bIsBackgrounded) {
+ ctx.actor->_flags.bIsBackgrounded = 1;
+ if (ctx.actor->_workFlags.bWasDrawn) {
+ engine->_redraw->_firstTime = true;
+ }
+ }
+ } else {
+ if (ctx.actor->_flags.bIsBackgrounded) {
+ ctx.actor->_flags.bIsBackgrounded = 0;
+ if (ctx.actor->_workFlags.bWasDrawn) {
+ engine->_redraw->_firstTime = true;
+ }
+ }
+ }
+ return 0;
}
int32 ScriptLifeV2::lSET_FLAG_GAME(TwinEEngine *engine, LifeScriptContext &ctx) {
diff --git a/engines/twine/script/script_move_v2.cpp b/engines/twine/script/script_move_v2.cpp
index ea6f5221eab..c68620a07a2 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/audio/sound.h"
#include "twine/resources/resources.h"
#include "twine/twine.h"
@@ -54,7 +55,7 @@ static const ScriptMoveFunction function_map[] = {
{"CLOSE", ScriptMove::mCLOSE},
{"WAIT_DOOR", ScriptMove::mWAIT_DOOR},
{"SAMPLE_RND", ScriptMove::mSAMPLE_RND},
- {"SAMPLE_ALWAYS", ScriptMove::mSAMPLE_ALWAYS},
+ {"SAMPLE_ALWAYS", ScriptMoveV2::mSAMPLE_ALWAYS},
{"SAMPLE_STOP", ScriptMove::mSAMPLE_STOP},
{"PLAY_FLA", ScriptMove::mPLAY_FLA},
{"REPEAT_SAMPLE", ScriptMove::mREPEAT_SAMPLE},
@@ -155,6 +156,15 @@ int32 ScriptMoveV2::mSPRITE(TwinEEngine *engine, MoveScriptContext &ctx) {
return 0;
}
+int32 ScriptMoveV2::mSAMPLE_ALWAYS(TwinEEngine *engine, MoveScriptContext &ctx) {
+ int32 sampleIdx = ctx.stream.readSint16LE();
+ debugC(3, kDebugLevels::kDebugScriptsMove, "MOVE::SAMPLE_ALWAYS(%i)", (int)sampleIdx);
+ if (!engine->_sound->isSamplePlaying(sampleIdx)) {
+ engine->_sound->mixSample3D(sampleIdx, engine->_sound->_parmSampleFrequence, engine->_sound->_parmSampleVolume, ctx.actor->posObj(), ctx.actorIdx);
+ }
+ return 0;
+}
+
int32 ScriptMoveV2::mSET_FRAME(TwinEEngine *engine, MoveScriptContext &ctx) {
const uint8 num = ctx.stream.readByte();
if (!ctx.actor->_flags.bSprite3D) {
@@ -179,41 +189,94 @@ int32 ScriptMoveV2::mSET_FRAME_3DS(TwinEEngine *engine, MoveScriptContext &ctx)
}
int32 ScriptMoveV2::mSET_START_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ int32 num = ctx.stream.readByte();
+ if (ctx.actor->_flags.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;
+
+ ctx.actor->A3DS.Deb = num;
+ }
+ return 0;
}
int32 ScriptMoveV2::mSET_END_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ int32 num = ctx.stream.readByte();
+ if (ctx.actor->_flags.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;
+
+ ctx.actor->A3DS.Fin = num;
+ }
+ return 0;
}
int32 ScriptMoveV2::mSTART_ANIM_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ const int32 num = ctx.stream.readByte(); // NbFps
+ if (ctx.actor->_flags.bHasSpriteAnim3D) {
+ engine->_actor->initSprite(ctx.actor->A3DS.Deb, ctx.actorIdx);
+ ctx.actor->SizeSHit = (int16)num;
+ }
+ return 0;
}
int32 ScriptMoveV2::mSTOP_ANIM_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ if (ctx.actor->_flags.bHasSpriteAnim3D) {
+ ctx.actor->SizeSHit = 0;
+ }
+ return 0;
}
int32 ScriptMoveV2::mWAIT_ANIM_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ if (ctx.actor->_flags.bHasSpriteAnim3D) {
+ if (ctx.actor->_sprite != ctx.actor->A3DS.Fin && ctx.actor->SizeSHit != 0) {
+ ctx.undo(1); // OffsetTrack--
+ return 1; // wait
+ }
+ }
+ return 0;
}
int32 ScriptMoveV2::mWAIT_FRAME_3DS(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ int32 num = ctx.stream.readByte();
+ if (ctx.actor->_flags.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;
+
+ if (ctx.actor->_sprite != num) {
+ ctx.undo(2); // OffsetTrack -= 2
+ return 1; // wait
+ }
+ }
+ return 0;
}
// DECALAGE
int32 ScriptMoveV2::mOFFSET(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ engine->_sound->_parmSampleDecalage = ctx.stream.readSint16LE();
+ return 0;
}
// FREQUENCE
int32 ScriptMoveV2::mFREQUENCY(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ engine->_sound->_parmSampleFrequence = ctx.stream.readSint16LE();
+ return 0;
}
int32 ScriptMoveV2::mVOLUME(TwinEEngine *engine, MoveScriptContext &ctx) {
- return -1;
+ engine->_sound->_parmSampleVolume = ctx.stream.readByte();
+ return 0;
}
ScriptMoveV2::ScriptMoveV2(TwinEEngine *engine) : ScriptMove(engine, function_map, ARRAYSIZE(function_map)) {
diff --git a/engines/twine/script/script_move_v2.h b/engines/twine/script/script_move_v2.h
index 3a0e632c561..94d9607b3bf 100644
--- a/engines/twine/script/script_move_v2.h
+++ b/engines/twine/script/script_move_v2.h
@@ -33,6 +33,7 @@ public:
static int32 mWAIT_NB_DIZIEME(TwinEEngine *engine, MoveScriptContext &ctx);
static int32 mSPRITE(TwinEEngine *engine, MoveScriptContext &ctx);
static int32 mWAIT_NB_SECOND_RND(TwinEEngine *engine, MoveScriptContext &ctx);
+ static int32 mSAMPLE_ALWAYS(TwinEEngine *engine, MoveScriptContext &ctx);
static int32 mSET_FRAME(TwinEEngine *engine, MoveScriptContext &ctx);
static int32 mSET_FRAME_3DS(TwinEEngine *engine, MoveScriptContext &ctx);
static int32 mSET_START_3DS(TwinEEngine *engine, MoveScriptContext &ctx);
diff --git a/engines/twine/shared.h b/engines/twine/shared.h
index f1cc992dace..67b2c57d1b8 100644
--- a/engines/twine/shared.h
+++ b/engines/twine/shared.h
@@ -83,6 +83,8 @@
// Twinsun explosion
#define GAMEFLAG_VIDEO_EXPLODE2 219
+#define GAMEFLAG_ESC 249
+
// lba2 Kashes or Zlitos
#define GAMEFLAG_MONEY 8
// FLAG_ARDOISE
Commit: 1e9337f5c704ad7425df951f9c31f2099d17de50
https://github.com/scummvm/scummvm/commit/1e9337f5c704ad7425df951f9c31f2099d17de50
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-01-02T23:33:10+01:00
Commit Message:
TWINE: LBA2: started with filling the rain stubs
Changed paths:
engines/twine/renderer/renderer.h
engines/twine/scene/rain.cpp
engines/twine/scene/rain.h
engines/twine/scene/scene.cpp
engines/twine/twine.cpp
diff --git a/engines/twine/renderer/renderer.h b/engines/twine/renderer/renderer.h
index d22a109c1c6..07c740186d4 100644
--- a/engines/twine/renderer/renderer.h
+++ b/engines/twine/renderer/renderer.h
@@ -297,6 +297,22 @@ public:
void draw3dObject(int32 x, int32 y, const BodyData &bodyData, int32 angle, int32 cameraZoom);
void asmTexturedTriangleNoClip(const ComputedVertex vertexCoordinates[3], const ComputedVertex textureCoordinates[3], const uint8 *holomapImage, uint32 holomapImageSize);
+
+ inline IVec3 getCameraPosition() const {
+ return _cameraPos;
+ }
+
+ inline IVec3 getCameraRotation() const {
+ return _cameraRot;
+ }
+
+ inline int32 getLFactorX() const {
+ return _lFactorX;
+ }
+
+ inline int32 getLFactorY() const {
+ return _lFactorY;
+ }
};
inline void Renderer::setCameraRotation(int32 x, int32 y, int32 z) {
diff --git a/engines/twine/scene/rain.cpp b/engines/twine/scene/rain.cpp
index a3e885133bb..561351c3a97 100644
--- a/engines/twine/scene/rain.cpp
+++ b/engines/twine/scene/rain.cpp
@@ -20,22 +20,151 @@
*/
#include "twine/scene/rain.h"
+#include "twine/renderer/renderer.h"
+#include "twine/scene/grid.h"
+#include "twine/twine.h"
namespace TwinE {
+#define RAIN_VX 200
+#define RAIN_VY 2500
+#define RAIN_WEIGHT 30
+#define RAIN_STOP 0
+#define RAIN_DELTA_X 128
+#define RAIN_DELTA_Y 256
+#define RAIN_DELTA_Z 128
+
void Rain::InitOneRain(T_RAIN *pt) {
+ IVec3 cameraPos = _engine->_renderer->getCameraPosition();
+
+ int32 rndy = _engine->getRandomNumber(cameraPos.y + 10000);
+
+ pt->YRain = cameraPos.y + rndy;
+
+ rndy = rndy / 2 + 15000;
+
+ pt->XRain = cameraPos.x - rndy + _engine->getRandomNumber(30000);
+ pt->ZRain = cameraPos.z - rndy + _engine->getRandomNumber(30000);
+ pt->Timer = 0;
}
void Rain::InitRain() {
+ for (int32 i = 0; i < MAX_RAIN; i++) {
+ InitOneRain(&TabRain[i]);
+ }
+
+ LastTimer = 0;
}
void Rain::GereRain() {
+ int32 temp = _engine->timerRef;
+ DeltaRain = LastTimer ? (temp - LastTimer) * 10 : 0;
+ LastTimer = temp;
+
+ for (int32 i = 0; i < MAX_RAIN; i++) {
+ if (!TabRain[i].Timer) {
+ TabRain[i].XRain += DeltaRain / 2;
+ TabRain[i].ZRain += DeltaRain / 2;
+ TabRain[i].YRain -= DeltaRain;
+ }
+ }
}
void Rain::ClearImpactRain() {
+ for (int32 i = 0; i < MAX_RAIN; i++) {
+ if (TabRain[i].Timer) {
+ InitOneRain(&TabRain[i]);
+ }
+ }
}
void Rain::AffRain() {
+ int32 lFactorX = _engine->_renderer->getLFactorX();
+ int32 lFactorY = _engine->_renderer->getLFactorY();
+ IVec3 cameraRot = _engine->_renderer->getCameraRotation();
+ int32 cameraZr = cameraRot.z;
+
+ // ClipZFar approximation
+ int32 clipZFar = 14000; // Default value from CREDITS.CPP
+ int32 startZFog = 5000; // Default value from CREDITS.CPP
+
+ for (int32 i = 0; i < MAX_RAIN; i++) {
+ if (TabRain[i].Timer) {
+ int32 dt = LastTimer - TabRain[i].Timer;
+
+ int32 c = TabRain[i].XRain >> 16;
+ int32 x = (int16)(TabRain[i].XRain & 0xFFFF);
+ int32 y = TabRain[i].YRain;
+ int32 z = TabRain[i].ZRain;
+
+ int32 xp, yp;
+ yp = (RAIN_VY - RAIN_WEIGHT * dt) * dt / 256;
+ if (yp < 0) {
+ yp = 0;
+ xp = RAIN_VX * RAIN_VY / RAIN_WEIGHT / 256;
+ } else {
+ xp = RAIN_VX * dt / 256;
+ yp = (yp * lFactorY) / z;
+ }
+
+ xp = (xp * lFactorX) / z;
+
+ int32 x0 = x - xp;
+ int32 x1 = x + xp;
+
+ // int32 y0 = y - yp;
+ // int32 y1 = y;
+
+ // z = ruleThree32(0, 65535, clipZFar, z);
+
+ // Draw splash
+ _engine->_workVideoBuffer.drawLine(x, y, x0, y - yp, c);
+ _engine->_workVideoBuffer.drawLine(x, y, x1, y - yp, c);
+
+ if (dt && !yp) {
+ InitOneRain(&TabRain[i]);
+ }
+ } else {
+ if (TabRain[i].YRain <= RAIN_STOP) {
+ InitOneRain(&TabRain[i]);
+ continue;
+ }
+
+ IVec3 p1 = _engine->_renderer->longWorldRot(TabRain[i].XRain - RAIN_DELTA_X, TabRain[i].YRain + RAIN_DELTA_Y, TabRain[i].ZRain - RAIN_DELTA_Z);
+ IVec3 proj1 = _engine->_renderer->projectPoint(p1);
+
+ int32 xp = proj1.x;
+ int32 yp = proj1.y;
+ int32 z0 = ruleThree32(0, 65535, clipZFar, cameraZr - p1.z);
+
+ IVec3 p2 = _engine->_renderer->longWorldRot(TabRain[i].XRain, TabRain[i].YRain, TabRain[i].ZRain);
+ IVec3 proj2 = _engine->_renderer->projectPoint(p2);
+
+ int32 Z0 = cameraZr - p2.z;
+ // int32 z1 = ruleThree32(0, 65535, clipZFar, Z0);
+
+ int32 c = boundRuleThree(16 * 3 + 10, 16 * 3 + 3, clipZFar - startZFog, Z0);
+
+ // Draw rain drop
+ _engine->_workVideoBuffer.drawLine(xp, yp, proj2.x, proj2.y, c);
+
+ // Check collision with ground
+ int32 groundHeight = 0;
+ IVec3 pos(TabRain[i].XRain, TabRain[i].YRain, TabRain[i].ZRain);
+ _engine->_grid->getBlockBufferGround(pos, groundHeight);
+
+ if (TabRain[i].YRain <= groundHeight) {
+ // Splash
+ TabRain[i].XRain = ((xp & 0xFFFF) | (c << 16));
+ TabRain[i].YRain = yp;
+ TabRain[i].ZRain = z0;
+ TabRain[i].Timer = LastTimer;
+ }
+ }
+ }
+}
+
+Rain::Rain(TwinEEngine *engine) : _engine(engine) {
}
} // namespace TwinE
diff --git a/engines/twine/scene/rain.h b/engines/twine/scene/rain.h
index f5b22878b8b..396e6457028 100644
--- a/engines/twine/scene/rain.h
+++ b/engines/twine/scene/rain.h
@@ -27,19 +27,25 @@
namespace TwinE {
+#define MAX_RAIN 200
+
class Rain {
private:
- //TwinEEngine *_engine;
+ TwinEEngine *_engine;
+ int32 LastTimer = 0;
+ int32 DeltaRain = 0;
public:
struct T_RAIN {
- int32 XRain;
- int32 YRain;
- int32 ZRain;
- int32 Timer;
+ int32 XRain = 0;
+ int32 YRain = 0;
+ int32 ZRain = 0;
+ int32 Timer = 0;
};
- Rain(TwinEEngine *engine) /*: _engine(engine) */ {}
+ T_RAIN TabRain[MAX_RAIN];
+
+ Rain(TwinEEngine *engine);
void InitOneRain(T_RAIN *pt);
void InitRain();
diff --git a/engines/twine/scene/scene.cpp b/engines/twine/scene/scene.cpp
index 59a6046eabf..d79f2562068 100644
--- a/engines/twine/scene/scene.cpp
+++ b/engines/twine/scene/scene.cpp
@@ -20,6 +20,7 @@
*/
#include "twine/scene/scene.h"
+#include "twine/scene/rain.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/memstream.h"
@@ -484,6 +485,7 @@ bool Scene::loadScene(int32 index) {
if (_engine->isLBA1()) {
return loadSceneLBA1();
} else if (_engine->isLBA2()) {
+ _engine->_rain->InitRain();
return loadSceneLBA2();
}
diff --git a/engines/twine/twine.cpp b/engines/twine/twine.cpp
index c903de2629a..b0d498940e3 100644
--- a/engines/twine/twine.cpp
+++ b/engines/twine/twine.cpp
@@ -918,6 +918,10 @@ bool TwinEEngine::runGameEngine() { // mainLoopInteration
_movements->update();
+ if (isLBA2()) {
+ _rain->GereRain();
+ }
+
_debugState->update();
if (_menuOptions->flagCredits) {
@@ -1237,6 +1241,10 @@ bool TwinEEngine::runGameEngine() { // mainLoopInteration
_redraw->drawScene(_redraw->_firstTime);
+ if (isLBA2()) {
+ _rain->AffRain();
+ }
+
// workaround to fix hero redraw after drowning
if (_actor->_cropBottomScreen && _redraw->_firstTime) {
_scene->_sceneHero->_flags.bIsInvisible = 1;
More information about the Scummvm-git-logs
mailing list